Skip to content

Commit 18ab4a7

Browse files
cahillsfclaude
andcommitted
add --function-endpoint-suffix flag for custom function DNS
Adds a configurable DNS suffix (via CLI flag or FUNCTION_ENDPOINT_SUFFIX env var) that gets appended to function service endpoints and included in TLS SANs. This allows function gRPC endpoints to use custom DNS names instead of the default in-cluster service DNS. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d5ca3fa commit 18ab4a7

5 files changed

Lines changed: 89 additions & 13 deletions

File tree

cmd/crossplane/core/core.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ type startCommand struct {
107107

108108
XpkgCacheDir string `aliases:"cache-dir" default:"/cache/xpkg" env:"XPKG_CACHE_DIR,CACHE_DIR" help:"Directory used for caching package images." short:"c"`
109109

110-
PackageRuntime string `default:"Deployment" env:"PACKAGE_RUNTIME" help:"The package runtime to use for packages with a runtime (e.g. Providers and Functions)" placeholder:"runtime | runtime1=package1;runtime2=package2"`
110+
PackageRuntime string `default:"Deployment" env:"PACKAGE_RUNTIME" help:"The package runtime to use for packages with a runtime (e.g. Providers and Functions)" placeholder:"runtime | runtime1=package1;runtime2=package2"`
111+
FunctionEndpointSuffix string `env:"FUNCTION_ENDPOINT_SUFFIX" help:"DNS suffix appended to function service endpoints."`
111112

112113
SyncInterval time.Duration `default:"1h" help:"How often all resources will be double-checked for drift from the desired state." short:"s"`
113114
PollInterval time.Duration `default:"1m" help:"How often individual resources will be checked for drift from the desired state."`
@@ -654,6 +655,7 @@ func (c *startCommand) Run(s *runtime.Scheme, log logging.Logger) error { //noli
654655
ServiceAccount: c.ServiceAccount,
655656
PackageRuntime: pr,
656657
MaxConcurrentPackageEstablishers: c.MaxConcurrentPackageEstablishers,
658+
FunctionEndpointSuffix: c.FunctionEndpointSuffix,
657659
}
658660

659661
if err := pkg.Setup(mgr, po); err != nil {

internal/controller/pkg/controller/options.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,6 @@ type Options struct {
4242
// MaxConcurrentPackageEstablishers is the maximum number of goroutines to use
4343
// for establishing Providers, Configurations and Functions.
4444
MaxConcurrentPackageEstablishers int
45+
46+
FunctionEndpointSuffix string
4547
}

internal/controller/pkg/runtime/reconciler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ func SetupFunctionRevision(mgr ctrl.Manager, o controller.Options) error {
220220
WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name), o.EventFilterFunctions...)), //nolint:staticcheck // TODO(adamwg) Update crossplane-runtime to the new events API.
221221
WithNamespace(o.Namespace),
222222
WithServiceAccount(o.ServiceAccount),
223-
WithRuntimeHooks(NewFunctionHooks(mgr.GetClient())),
223+
WithRuntimeHooks(NewFunctionHooks(mgr.GetClient(), o.FunctionEndpointSuffix)),
224224
WithFeatureFlags(o.Features),
225225
WithConfigStore(xpkg.NewImageConfigStore(mgr.GetClient(), o.Namespace)),
226226
)

internal/controller/pkg/runtime/runtime_function.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,18 @@ const (
4848

4949
// FunctionHooks performs runtime operations for function packages.
5050
type FunctionHooks struct {
51-
client resource.ClientApplicator
51+
client resource.ClientApplicator
52+
endpointSuffix string
5253
}
5354

5455
// NewFunctionHooks returns a new FunctionHooks.
55-
func NewFunctionHooks(client client.Client) *FunctionHooks {
56+
func NewFunctionHooks(client client.Client, endpointSuffix string) *FunctionHooks {
5657
return &FunctionHooks{
5758
client: resource.ClientApplicator{
5859
Client: client,
5960
Applicator: resource.NewAPIPatchingApplicator(client),
6061
},
62+
endpointSuffix: endpointSuffix,
6163
}
6264
}
6365

@@ -101,15 +103,24 @@ func (h *FunctionHooks) Pre(ctx context.Context, pr v1.PackageRevisionWithRuntim
101103
return errors.Errorf("cannot apply function package hooks to %T", pr)
102104
}
103105

104-
fRev.Status.Endpoint = fmt.Sprintf(ServiceEndpointFmt, svc.Name, svc.Namespace, GRPCPort)
106+
if h.endpointSuffix != "" {
107+
fRev.Status.Endpoint = fmt.Sprintf(ServiceEndpointFmt, svc.Name, svc.Namespace+"."+h.endpointSuffix, GRPCPort)
108+
} else {
109+
fRev.Status.Endpoint = fmt.Sprintf(ServiceEndpointFmt, svc.Name, svc.Namespace, GRPCPort)
110+
}
105111

106112
secServer := build.TLSServerSecret()
107113
if err := h.client.Applicator.Apply(ctx, secServer); err != nil {
108114
return errors.Wrap(err, errApplyFunctionSecret)
109115
}
110116

117+
dnsNames := initializer.DNSNamesForService(svc.Name, svc.Namespace)
118+
if h.endpointSuffix != "" {
119+
dnsNames = append(dnsNames, svc.Name+"."+svc.Namespace+"."+h.endpointSuffix)
120+
}
121+
111122
if err := initializer.NewTLSCertificateGenerator(secServer.Namespace, initializer.RootCACertSecretName,
112-
initializer.TLSCertificateGeneratorWithServerSecretName(secServer.GetName(), initializer.DNSNamesForService(svc.Name, svc.Namespace)),
123+
initializer.TLSCertificateGeneratorWithServerSecretName(secServer.GetName(), dnsNames),
113124
initializer.TLSCertificateGeneratorWithOwner([]metav1.OwnerReference{meta.AsController(meta.TypedReferenceTo(pr, pr.GetObjectKind().GroupVersionKind()))})).Run(ctx, h.client.Client); err != nil {
114125
return errors.Wrapf(err, "cannot generate TLS certificates for %q", pr.GetLabels()[v1.LabelParentPackage])
115126
}

internal/controller/pkg/runtime/runtime_function_test.go

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,11 @@ import (
4040

4141
func TestFunctionPreHook(t *testing.T) {
4242
type args struct {
43-
client client.Client
44-
pkg runtime.Object
45-
rev v1.PackageRevisionWithRuntime
46-
manifests ManifestBuilder
43+
client client.Client
44+
pkg runtime.Object
45+
rev v1.PackageRevisionWithRuntime
46+
manifests ManifestBuilder
47+
endpointSuffix string
4748
}
4849

4950
type want struct {
@@ -115,11 +116,71 @@ func TestFunctionPreHook(t *testing.T) {
115116
},
116117
},
117118
},
119+
"SuccessWithEndpointSuffix": {
120+
reason: "Successful run of pre hook with endpoint suffix.",
121+
args: args{
122+
endpointSuffix: "suffix.example.com",
123+
pkg: &pkgmetav1.Function{
124+
Spec: pkgmetav1.FunctionSpec{},
125+
},
126+
rev: &v1.FunctionRevision{
127+
Spec: v1.FunctionRevisionSpec{
128+
PackageRevisionSpec: v1.PackageRevisionSpec{
129+
DesiredState: v1.PackageRevisionActive,
130+
},
131+
PackageRevisionRuntimeSpec: v1.PackageRevisionRuntimeSpec{
132+
TLSServerSecretName: ptr.To("some-server-secret"),
133+
},
134+
},
135+
},
136+
manifests: &MockManifestBuilder{
137+
ServiceFn: func(_ ...ServiceOverride) *corev1.Service {
138+
return &corev1.Service{}
139+
},
140+
TLSServerSecretFn: func() *corev1.Secret {
141+
return &corev1.Secret{}
142+
},
143+
},
144+
client: &test.MockClient{
145+
MockGet: func(_ context.Context, _ client.ObjectKey, obj client.Object) error {
146+
if svc, ok := obj.(*corev1.Service); ok {
147+
svc.Name = "some-service"
148+
svc.Namespace = "some-namespace"
149+
}
150+
return nil
151+
},
152+
MockPatch: func(_ context.Context, _ client.Object, _ client.Patch, _ ...client.PatchOption) error {
153+
return nil
154+
},
155+
MockUpdate: func(_ context.Context, _ client.Object, _ ...client.UpdateOption) error {
156+
return nil
157+
},
158+
},
159+
},
160+
want: want{
161+
rev: &v1.FunctionRevision{
162+
Spec: v1.FunctionRevisionSpec{
163+
PackageRevisionSpec: v1.PackageRevisionSpec{
164+
DesiredState: v1.PackageRevisionActive,
165+
},
166+
PackageRevisionRuntimeSpec: v1.PackageRevisionRuntimeSpec{
167+
TLSServerSecretName: ptr.To("some-server-secret"),
168+
},
169+
},
170+
Status: v1.FunctionRevisionStatus{
171+
Endpoint: fmt.Sprintf(ServiceEndpointFmt, "some-service", "some-namespace.suffix.example.com", revision.ServicePort),
172+
PackageRevisionRuntimeStatus: v1.PackageRevisionRuntimeStatus{
173+
TLSServerSecretName: ptr.To("some-server-secret"),
174+
},
175+
},
176+
},
177+
},
178+
},
118179
}
119180

120181
for name, tc := range cases {
121182
t.Run(name, func(t *testing.T) {
122-
h := NewFunctionHooks(tc.args.client)
183+
h := NewFunctionHooks(tc.args.client, tc.args.endpointSuffix)
123184

124185
err := h.Pre(context.TODO(), tc.args.rev, tc.args.manifests)
125186
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
@@ -584,7 +645,7 @@ func TestFunctionPostHook(t *testing.T) {
584645

585646
for name, tc := range cases {
586647
t.Run(name, func(t *testing.T) {
587-
h := NewFunctionHooks(tc.args.client)
648+
h := NewFunctionHooks(tc.args.client, "")
588649

589650
err := h.Post(context.TODO(), tc.args.rev, tc.args.manifests)
590651
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
@@ -689,7 +750,7 @@ func TestFunctionDeactivateHook(t *testing.T) {
689750

690751
for name, tc := range cases {
691752
t.Run(name, func(t *testing.T) {
692-
h := NewFunctionHooks(tc.args.client)
753+
h := NewFunctionHooks(tc.args.client, "")
693754

694755
err := h.Deactivate(context.TODO(), tc.args.rev, tc.args.manifests)
695756
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {

0 commit comments

Comments
 (0)