From 604709e6a832efd5b8d8d26e4194101adf9f3f5c Mon Sep 17 00:00:00 2001 From: Stephen Cahill Date: Mon, 12 Jan 2026 15:52:40 -0500 Subject: [PATCH 1/6] fix empty secret name behavior --- internal/initializer/tls.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/internal/initializer/tls.go b/internal/initializer/tls.go index 8ce1bce324c..4033e086049 100644 --- a/internal/initializer/tls.go +++ b/internal/initializer/tls.go @@ -89,16 +89,24 @@ func TLSCertificateGeneratorWithOwner(owner []metav1.OwnerReference) TLSCertific } // TLSCertificateGeneratorWithServerSecretName returns an TLSCertificateGeneratorOption that sets server secret name. +// If the secret name is empty, the option is a no-op. func TLSCertificateGeneratorWithServerSecretName(s string, dnsNames []string) TLSCertificateGeneratorOption { return func(g *TLSCertificateGenerator) { + if s == "" { + return + } g.tlsServerSecretName = &s g.tlsServerDNSNames = dnsNames } } // TLSCertificateGeneratorWithClientSecretName returns an TLSCertificateGeneratorOption that sets client secret name. +// If the secret name is empty, the option is a no-op. func TLSCertificateGeneratorWithClientSecretName(s string, subjects []string) TLSCertificateGeneratorOption { return func(g *TLSCertificateGenerator) { + if s == "" { + return + } g.tlsClientSecretName = &s g.tlsClientDNSNames = subjects } From efa99686e051326ab5b8c30efcb0f02de2d53480 Mon Sep 17 00:00:00 2001 From: Stephen Cahill Date: Mon, 12 Jan 2026 17:07:45 -0500 Subject: [PATCH 2/6] allow overrides of filenames --- cmd/crossplane/core/core.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/cmd/crossplane/core/core.go b/cmd/crossplane/core/core.go index b05b9a9f5ab..9daa06f0d0b 100644 --- a/cmd/crossplane/core/core.go +++ b/cmd/crossplane/core/core.go @@ -61,7 +61,6 @@ import ( "github.com/crossplane/crossplane/v2/internal/controller/protection" "github.com/crossplane/crossplane/v2/internal/engine" "github.com/crossplane/crossplane/v2/internal/features" - "github.com/crossplane/crossplane/v2/internal/initializer" "github.com/crossplane/crossplane/v2/internal/metrics" "github.com/crossplane/crossplane/v2/internal/protection/usage" "github.com/crossplane/crossplane/v2/internal/transport" @@ -123,6 +122,10 @@ type startCommand struct { TLSClientSecretName string `env:"TLS_CLIENT_SECRET_NAME" help:"The name of the TLS Secret that will be store Crossplane's client certificate."` TLSClientCertsDir string `env:"TLS_CLIENT_CERTS_DIR" help:"The path of the folder which will store TLS client certificate of Crossplane."` + TLSClientCACertFileName string `default:"ca.crt" env:"TLS_CLIENT_CA_CERT_FILENAME" help:"The filename of the CA certificate in the TLS client certs directory."` + TLSClientCertFileName string `default:"tls.crt" env:"TLS_CLIENT_CERT_FILENAME" help:"The filename of the client certificate in the TLS client certs directory."` + TLSClientKeyFileName string `default:"tls.key" env:"TLS_CLIENT_KEY_FILENAME" help:"The filename of the client private key in the TLS client certs directory."` + EnableDependencyVersionUpgrades bool `group:"Alpha Features:" help:"Enable support for upgrading dependency versions when the parent package is updated."` EnableDependencyVersionDowngrades bool `group:"Alpha Features:" help:"Enable support for upgrading and downgrading dependency versions when a dependent package is updated."` EnableSignatureVerification bool `group:"Alpha Features:" help:"Enable support for package signature verification via ImageConfig API."` @@ -254,9 +257,9 @@ func (c *startCommand) Run(s *runtime.Scheme, log logging.Logger) error { //noli } clienttls, err := certificates.LoadMTLSConfig( - filepath.Join(c.TLSClientCertsDir, initializer.SecretKeyCACert), - filepath.Join(c.TLSClientCertsDir, corev1.TLSCertKey), - filepath.Join(c.TLSClientCertsDir, corev1.TLSPrivateKeyKey), + filepath.Join(c.TLSClientCertsDir, c.TLSClientCACertFileName), + filepath.Join(c.TLSClientCertsDir, c.TLSClientCertFileName), + filepath.Join(c.TLSClientCertsDir, c.TLSClientKeyFileName), false) if err != nil { return errors.Wrap(err, "cannot load client TLS certificates") From 192a72382a490bde4531d25aec998dacf0bf2446 Mon Sep 17 00:00:00 2001 From: Stephen Cahill Date: Mon, 12 Jan 2026 21:26:11 -0500 Subject: [PATCH 3/6] allow file path CA input in start command --- cmd/crossplane/core/core.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cmd/crossplane/core/core.go b/cmd/crossplane/core/core.go index 9daa06f0d0b..9c234f5a51a 100644 --- a/cmd/crossplane/core/core.go +++ b/cmd/crossplane/core/core.go @@ -126,6 +126,9 @@ type startCommand struct { TLSClientCertFileName string `default:"tls.crt" env:"TLS_CLIENT_CERT_FILENAME" help:"The filename of the client certificate in the TLS client certs directory."` TLSClientKeyFileName string `default:"tls.key" env:"TLS_CLIENT_KEY_FILENAME" help:"The filename of the client private key in the TLS client certs directory."` + TLSServerCertFileName string `default:"tls.crt" env:"TLS_SERVER_CERT_FILENAME" help:"The filename of the client certificate in the TLS client certs directory."` + TLSServerKeyFileName string `default:"tls.key" env:"TLS_SERVER_KEY_FILENAME" help:"The filename of the client private key in the TLS client certs directory."` + EnableDependencyVersionUpgrades bool `group:"Alpha Features:" help:"Enable support for upgrading dependency versions when the parent package is updated."` EnableDependencyVersionDowngrades bool `group:"Alpha Features:" help:"Enable support for upgrading and downgrading dependency versions when a dependent package is updated."` EnableSignatureVerification bool `group:"Alpha Features:" help:"Enable support for package signature verification via ImageConfig API."` @@ -191,7 +194,9 @@ func (c *startCommand) Run(s *runtime.Scheme, log logging.Logger) error { //noli SyncPeriod: &c.SyncInterval, }, WebhookServer: webhook.NewServer(webhook.Options{ - CertDir: c.TLSServerCertsDir, + CertDir: c.TLSServerCertsDir, + CertName: c.TLSServerCertFileName, + KeyName: c.TLSServerKeyFileName, TLSOpts: []func(*tls.Config){ func(t *tls.Config) { t.MinVersion = tls.VersionTLS13 From b093afdb3ed804fc4eec2473cb3a4e14a502489d Mon Sep 17 00:00:00 2001 From: Stephen Cahill Date: Mon, 12 Jan 2026 21:28:49 -0500 Subject: [PATCH 4/6] add filepath tls server source in webhook init --- cmd/crossplane/core/init.go | 28 ++++++-- .../initializer/webhook_configurations.go | 69 +++++++++++++++---- .../webhook_configurations_test.go | 2 +- 3 files changed, 79 insertions(+), 20 deletions(-) diff --git a/cmd/crossplane/core/init.go b/cmd/crossplane/core/init.go index 30e83328987..4da3884e854 100644 --- a/cmd/crossplane/core/init.go +++ b/cmd/crossplane/core/init.go @@ -19,6 +19,7 @@ package core import ( "context" "fmt" + "path/filepath" admv1 "k8s.io/api/admissionregistration/v1" "k8s.io/apimachinery/pkg/runtime" @@ -53,6 +54,9 @@ type initCommand struct { TLSCASecretName string `env:"TLS_CA_SECRET_NAME" help:"The name of the Secret that the initializer will fill with TLS CA certificate."` TLSServerSecretName string `env:"TLS_SERVER_SECRET_NAME" help:"The name of the Secret that the initializer will fill with TLS server certificates."` TLSClientSecretName string `env:"TLS_CLIENT_SECRET_NAME" help:"The name of the Secret that the initializer will fill with TLS client certificates."` + + WebhookTLSCertDir string `env:"WEBHOOK_TLS_CERT_DIR" help:"Directory containing TLS certificates for webhooks. When set, certificates are read from files instead of Secrets."` + WebhookTLSCACert string `default:"ca.crt" env:"WEBHOOK_TLS_CA_CERT" help:"Filename of the CA certificate within the TLS cert directory."` } // Run starts the initialization process. @@ -91,18 +95,28 @@ func (c *initCommand) Run(s *runtime.Scheme, log logging.Logger) error { ), ) - nn := types.NamespacedName{ - Name: c.TLSServerSecretName, - Namespace: c.Namespace, - } svc := admv1.ServiceReference{ Name: c.WebhookServiceName, Namespace: c.WebhookServiceNamespace, Port: &c.WebhookServicePort, } - steps = append(steps, - initializer.NewCoreCRDs(c.CRDsPath, s, initializer.WithWebhookTLSSecretRef(nn)), - initializer.NewWebhookConfigurations(c.WebhookConfigurationsPath, s, nn, svc)) + + var caProvider initializer.WebhookCAProvider + if c.WebhookTLSCertDir != "" { + caProvider = &initializer.FileCAProvider{Path: filepath.Join(c.WebhookTLSCertDir, c.WebhookTLSCACert)} + steps = append(steps, + initializer.NewCoreCRDs(c.CRDsPath, s), + initializer.NewWebhookConfigurations(c.WebhookConfigurationsPath, s, caProvider, svc)) + } else { + nn := types.NamespacedName{ + Name: c.TLSServerSecretName, + Namespace: c.Namespace, + } + caProvider = &initializer.SecretCAProvider{SecretRef: nn} + steps = append(steps, + initializer.NewCoreCRDs(c.CRDsPath, s, initializer.WithWebhookTLSSecretRef(nn)), + initializer.NewWebhookConfigurations(c.WebhookConfigurationsPath, s, caProvider, svc)) + } } else { log.Info("Warning: Webhooks are disabled, so deprecated ValidatingWebhookConfigurations will not be automatically deleted.") steps = append(steps, diff --git a/internal/initializer/webhook_configurations.go b/internal/initializer/webhook_configurations.go index 9be0117871b..8f15bdd7578 100644 --- a/internal/initializer/webhook_configurations.go +++ b/internal/initializer/webhook_configurations.go @@ -18,6 +18,7 @@ package initializer import ( "context" + "os" "github.com/spf13/afero" admv1 "k8s.io/api/admissionregistration/v1" @@ -35,8 +36,51 @@ import ( const ( errApplyWebhookConfiguration = "cannot apply webhook configuration" errGetWebhookSecret = "cannot get webhook secret" + errReadCABundleFile = "cannot read CA bundle file" + errEmptyCABundleFile = "CA bundle file is empty" ) +// WebhookCAProvider provides a CA bundle for webhook configurations. +type WebhookCAProvider interface { + GetCABundle(ctx context.Context, kube client.Client) ([]byte, error) +} + +// SecretCAProvider loads the CA bundle from a Kubernetes Secret. +type SecretCAProvider struct { + SecretRef types.NamespacedName +} + +func (p *SecretCAProvider) GetCABundle(ctx context.Context, kube client.Client) ([]byte, error) { + s := &corev1.Secret{} + if err := kube.Get(ctx, p.SecretRef, s); err != nil { + return nil, errors.Wrap(err, errGetWebhookSecret) + } + + if len(s.Data["tls.crt"]) == 0 { + return nil, errors.Errorf(errFmtNoTLSCrtInSecret, p.SecretRef.String()) + } + + return s.Data["tls.crt"], nil +} + +// FileCAProvider loads the CA bundle from a file. +type FileCAProvider struct { + Path string +} + +func (p *FileCAProvider) GetCABundle(ctx context.Context, kube client.Client) ([]byte, error) { + data, err := os.ReadFile(p.Path) + if err != nil { + return nil, errors.Wrap(err, errReadCABundleFile) + } + + if len(data) == 0 { + return nil, errors.New(errEmptyCABundleFile) + } + + return data, nil +} + // WithWebhookConfigurationsFs is used to configure the filesystem the CRDs will // be read from. Its default is afero.OsFs. func WithWebhookConfigurationsFs(fs afero.Fs) WebhookConfigurationsOption { @@ -45,15 +89,22 @@ func WithWebhookConfigurationsFs(fs afero.Fs) WebhookConfigurationsOption { } } +// WithWebhookCAProvider sets the CA provider for webhook configurations. +func WithWebhookCAProvider(provider WebhookCAProvider) WebhookConfigurationsOption { + return func(c *WebhookConfigurations) { + c.caProvider = provider + } +} + // WebhookConfigurationsOption configures WebhookConfigurations step. type WebhookConfigurationsOption func(*WebhookConfigurations) // NewWebhookConfigurations returns a new *WebhookConfigurations. -func NewWebhookConfigurations(path string, s *runtime.Scheme, tlsSecretRef types.NamespacedName, svc admv1.ServiceReference, opts ...WebhookConfigurationsOption) *WebhookConfigurations { +func NewWebhookConfigurations(path string, s *runtime.Scheme, caProvider WebhookCAProvider, svc admv1.ServiceReference, opts ...WebhookConfigurationsOption) *WebhookConfigurations { c := &WebhookConfigurations{ Path: path, Scheme: s, - TLSSecretRef: tlsSecretRef, + caProvider: caProvider, ServiceReference: svc, fs: afero.NewOsFs(), } @@ -69,7 +120,7 @@ func NewWebhookConfigurations(path string, s *runtime.Scheme, tlsSecretRef types type WebhookConfigurations struct { Path string Scheme *runtime.Scheme - TLSSecretRef types.NamespacedName + caProvider WebhookCAProvider ServiceReference admv1.ServiceReference fs afero.Fs @@ -78,17 +129,11 @@ type WebhookConfigurations struct { // Run applies all webhook ValidatingWebhookConfigurations and // MutatingWebhookConfiguration in the given directory. func (c *WebhookConfigurations) Run(ctx context.Context, kube client.Client) error { - s := &corev1.Secret{} - if err := kube.Get(ctx, c.TLSSecretRef, s); err != nil { - return errors.Wrap(err, errGetWebhookSecret) - } - - if len(s.Data["tls.crt"]) == 0 { - return errors.Errorf(errFmtNoTLSCrtInSecret, c.TLSSecretRef.String()) + caBundle, err := c.caProvider.GetCABundle(ctx, kube) + if err != nil { + return err } - caBundle := s.Data["tls.crt"] - r, err := parser.NewFsBackend(c.fs, parser.FsDir(c.Path), parser.FsFilters( diff --git a/internal/initializer/webhook_configurations_test.go b/internal/initializer/webhook_configurations_test.go index a6f4734047c..1d4d94f6810 100644 --- a/internal/initializer/webhook_configurations_test.go +++ b/internal/initializer/webhook_configurations_test.go @@ -189,7 +189,7 @@ func TestWebhookConfigurations(t *testing.T) { err := NewWebhookConfigurations( "/webhooks", sch, - types.NamespacedName{}, + &SecretCAProvider{SecretRef: types.NamespacedName{}}, tc.args.svc, tc.opts...).Run(context.TODO(), tc.kube) if diff := cmp.Diff(tc.err, err, test.EquateErrors()); diff != "" { From 28f1a52191c2878c26a2a06737a34dc97dd7e35c Mon Sep 17 00:00:00 2001 From: Stephen Cahill Date: Wed, 25 Feb 2026 14:01:36 -0500 Subject: [PATCH 5/6] 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 --- cmd/crossplane/core/core.go | 4 +- internal/controller/pkg/controller/options.go | 2 + internal/controller/pkg/runtime/reconciler.go | 2 +- .../pkg/runtime/runtime_function.go | 19 ++++- .../pkg/runtime/runtime_function_test.go | 75 +++++++++++++++++-- 5 files changed, 89 insertions(+), 13 deletions(-) diff --git a/cmd/crossplane/core/core.go b/cmd/crossplane/core/core.go index 9c234f5a51a..cfe4cbb923e 100644 --- a/cmd/crossplane/core/core.go +++ b/cmd/crossplane/core/core.go @@ -100,7 +100,8 @@ type startCommand struct { XpkgCacheDir string `aliases:"cache-dir" default:"/cache/xpkg" env:"XPKG_CACHE_DIR,CACHE_DIR" help:"Directory used for caching package images." short:"c"` - 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"` + 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"` + FunctionEndpointSuffix string `env:"FUNCTION_ENDPOINT_SUFFIX" help:"DNS suffix appended to function service endpoints."` SyncInterval time.Duration `default:"1h" help:"How often all resources will be double-checked for drift from the desired state." short:"s"` PollInterval time.Duration `default:"1m" help:"How often individual resources will be checked for drift from the desired state."` @@ -507,6 +508,7 @@ func (c *startCommand) Run(s *runtime.Scheme, log logging.Logger) error { //noli FetcherOptions: []xpkg.FetcherOpt{xpkg.WithUserAgent(c.UserAgent)}, PackageRuntime: pr, MaxConcurrentPackageEstablishers: c.MaxConcurrentPackageEstablishers, + FunctionEndpointSuffix: c.FunctionEndpointSuffix, } // We need to set the TUF_ROOT environment variable so that the TUF client diff --git a/internal/controller/pkg/controller/options.go b/internal/controller/pkg/controller/options.go index 1d57529d332..85fb19d3fff 100644 --- a/internal/controller/pkg/controller/options.go +++ b/internal/controller/pkg/controller/options.go @@ -46,4 +46,6 @@ type Options struct { // MaxConcurrentPackageEstablishers is the maximum number of goroutines to use // for establishing Providers, Configurations and Functions. MaxConcurrentPackageEstablishers int + + FunctionEndpointSuffix string } diff --git a/internal/controller/pkg/runtime/reconciler.go b/internal/controller/pkg/runtime/reconciler.go index aab7546068e..b901e304478 100644 --- a/internal/controller/pkg/runtime/reconciler.go +++ b/internal/controller/pkg/runtime/reconciler.go @@ -220,7 +220,7 @@ func SetupFunctionRevision(mgr ctrl.Manager, o controller.Options) error { WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name), o.EventFilterFunctions...)), WithNamespace(o.Namespace), WithServiceAccount(o.ServiceAccount), - WithRuntimeHooks(NewFunctionHooks(mgr.GetClient())), + WithRuntimeHooks(NewFunctionHooks(mgr.GetClient(), o.FunctionEndpointSuffix)), WithFeatureFlags(o.Features), WithConfigStore(xpkg.NewImageConfigStore(mgr.GetClient(), o.Namespace)), ) diff --git a/internal/controller/pkg/runtime/runtime_function.go b/internal/controller/pkg/runtime/runtime_function.go index af8ac958780..85912fde755 100644 --- a/internal/controller/pkg/runtime/runtime_function.go +++ b/internal/controller/pkg/runtime/runtime_function.go @@ -48,16 +48,18 @@ const ( // FunctionHooks performs runtime operations for function packages. type FunctionHooks struct { - client resource.ClientApplicator + client resource.ClientApplicator + endpointSuffix string } // NewFunctionHooks returns a new FunctionHooks. -func NewFunctionHooks(client client.Client) *FunctionHooks { +func NewFunctionHooks(client client.Client, endpointSuffix string) *FunctionHooks { return &FunctionHooks{ client: resource.ClientApplicator{ Client: client, Applicator: resource.NewAPIPatchingApplicator(client), }, + endpointSuffix: endpointSuffix, } } @@ -101,15 +103,24 @@ func (h *FunctionHooks) Pre(ctx context.Context, pr v1.PackageRevisionWithRuntim return errors.Errorf("cannot apply function package hooks to %T", pr) } - fRev.Status.Endpoint = fmt.Sprintf(ServiceEndpointFmt, svc.Name, svc.Namespace, GRPCPort) + if h.endpointSuffix != "" { + fRev.Status.Endpoint = fmt.Sprintf(ServiceEndpointFmt, svc.Name, svc.Namespace+"."+h.endpointSuffix, GRPCPort) + } else { + fRev.Status.Endpoint = fmt.Sprintf(ServiceEndpointFmt, svc.Name, svc.Namespace, GRPCPort) + } secServer := build.TLSServerSecret() if err := h.client.Applicator.Apply(ctx, secServer); err != nil { return errors.Wrap(err, errApplyFunctionSecret) } + dnsNames := initializer.DNSNamesForService(svc.Name, svc.Namespace) + if h.endpointSuffix != "" { + dnsNames = append(dnsNames, svc.Name+"."+svc.Namespace+"."+h.endpointSuffix) + } + if err := initializer.NewTLSCertificateGenerator(secServer.Namespace, initializer.RootCACertSecretName, - initializer.TLSCertificateGeneratorWithServerSecretName(secServer.GetName(), initializer.DNSNamesForService(svc.Name, svc.Namespace)), + initializer.TLSCertificateGeneratorWithServerSecretName(secServer.GetName(), dnsNames), initializer.TLSCertificateGeneratorWithOwner([]metav1.OwnerReference{meta.AsController(meta.TypedReferenceTo(pr, pr.GetObjectKind().GroupVersionKind()))})).Run(ctx, h.client.Client); err != nil { return errors.Wrapf(err, "cannot generate TLS certificates for %q", pr.GetLabels()[v1.LabelParentPackage]) } diff --git a/internal/controller/pkg/runtime/runtime_function_test.go b/internal/controller/pkg/runtime/runtime_function_test.go index bfda2c8eb0c..2b7136c3222 100644 --- a/internal/controller/pkg/runtime/runtime_function_test.go +++ b/internal/controller/pkg/runtime/runtime_function_test.go @@ -40,10 +40,11 @@ import ( func TestFunctionPreHook(t *testing.T) { type args struct { - client client.Client - pkg runtime.Object - rev v1.PackageRevisionWithRuntime - manifests ManifestBuilder + client client.Client + pkg runtime.Object + rev v1.PackageRevisionWithRuntime + manifests ManifestBuilder + endpointSuffix string } type want struct { @@ -115,11 +116,71 @@ func TestFunctionPreHook(t *testing.T) { }, }, }, + "SuccessWithEndpointSuffix": { + reason: "Successful run of pre hook with endpoint suffix.", + args: args{ + endpointSuffix: "suffix.example.com", + pkg: &pkgmetav1.Function{ + Spec: pkgmetav1.FunctionSpec{}, + }, + rev: &v1.FunctionRevision{ + Spec: v1.FunctionRevisionSpec{ + PackageRevisionSpec: v1.PackageRevisionSpec{ + DesiredState: v1.PackageRevisionActive, + }, + PackageRevisionRuntimeSpec: v1.PackageRevisionRuntimeSpec{ + TLSServerSecretName: ptr.To("some-server-secret"), + }, + }, + }, + manifests: &MockManifestBuilder{ + ServiceFn: func(_ ...ServiceOverride) *corev1.Service { + return &corev1.Service{} + }, + TLSServerSecretFn: func() *corev1.Secret { + return &corev1.Secret{} + }, + }, + client: &test.MockClient{ + MockGet: func(_ context.Context, _ client.ObjectKey, obj client.Object) error { + if svc, ok := obj.(*corev1.Service); ok { + svc.Name = "some-service" + svc.Namespace = "some-namespace" + } + return nil + }, + MockPatch: func(_ context.Context, _ client.Object, _ client.Patch, _ ...client.PatchOption) error { + return nil + }, + MockUpdate: func(_ context.Context, _ client.Object, _ ...client.UpdateOption) error { + return nil + }, + }, + }, + want: want{ + rev: &v1.FunctionRevision{ + Spec: v1.FunctionRevisionSpec{ + PackageRevisionSpec: v1.PackageRevisionSpec{ + DesiredState: v1.PackageRevisionActive, + }, + PackageRevisionRuntimeSpec: v1.PackageRevisionRuntimeSpec{ + TLSServerSecretName: ptr.To("some-server-secret"), + }, + }, + Status: v1.FunctionRevisionStatus{ + Endpoint: fmt.Sprintf(ServiceEndpointFmt, "some-service", "some-namespace.suffix.example.com", revision.ServicePort), + PackageRevisionRuntimeStatus: v1.PackageRevisionRuntimeStatus{ + TLSServerSecretName: ptr.To("some-server-secret"), + }, + }, + }, + }, + }, } for name, tc := range cases { t.Run(name, func(t *testing.T) { - h := NewFunctionHooks(tc.args.client) + h := NewFunctionHooks(tc.args.client, tc.args.endpointSuffix) err := h.Pre(context.TODO(), tc.args.rev, tc.args.manifests) if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { @@ -584,7 +645,7 @@ func TestFunctionPostHook(t *testing.T) { for name, tc := range cases { t.Run(name, func(t *testing.T) { - h := NewFunctionHooks(tc.args.client) + h := NewFunctionHooks(tc.args.client, "") err := h.Post(context.TODO(), tc.args.rev, tc.args.manifests) if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { @@ -689,7 +750,7 @@ func TestFunctionDeactivateHook(t *testing.T) { for name, tc := range cases { t.Run(name, func(t *testing.T) { - h := NewFunctionHooks(tc.args.client) + h := NewFunctionHooks(tc.args.client, "") err := h.Deactivate(context.TODO(), tc.args.rev, tc.args.manifests) if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { From cc9eae098da89d43aafaabe36d451dd2f47515b9 Mon Sep 17 00:00:00 2001 From: Stephen Cahill Date: Wed, 25 Feb 2026 15:53:50 -0500 Subject: [PATCH 6/6] test new ennvar --- cmd/crossplane/core/core.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cmd/crossplane/core/core.go b/cmd/crossplane/core/core.go index cfe4cbb923e..c58ec4f0bb8 100644 --- a/cmd/crossplane/core/core.go +++ b/cmd/crossplane/core/core.go @@ -20,6 +20,7 @@ package core import ( "context" "crypto/tls" + "crypto/x509" "fmt" "io" "os" @@ -123,6 +124,8 @@ type startCommand struct { TLSClientSecretName string `env:"TLS_CLIENT_SECRET_NAME" help:"The name of the TLS Secret that will be store Crossplane's client certificate."` TLSClientCertsDir string `env:"TLS_CLIENT_CERTS_DIR" help:"The path of the folder which will store TLS client certificate of Crossplane."` + TLSFunctionServerCAPath string `env:"TLS_FUNCTION_SERVER_CA_PATH" help:"Path to CA certificate used to verify function server TLS certificates."` + TLSClientCACertFileName string `default:"ca.crt" env:"TLS_CLIENT_CA_CERT_FILENAME" help:"The filename of the CA certificate in the TLS client certs directory."` TLSClientCertFileName string `default:"tls.crt" env:"TLS_CLIENT_CERT_FILENAME" help:"The filename of the client certificate in the TLS client certs directory."` TLSClientKeyFileName string `default:"tls.key" env:"TLS_CLIENT_KEY_FILENAME" help:"The filename of the client private key in the TLS client certs directory."` @@ -271,6 +274,18 @@ func (c *startCommand) Run(s *runtime.Scheme, log logging.Logger) error { //noli return errors.Wrap(err, "cannot load client TLS certificates") } + if c.TLSFunctionServerCAPath != "" { + ca, err := os.ReadFile(c.TLSFunctionServerCAPath) + if err != nil { + return errors.Wrap(err, "cannot read function server CA certificate") + } + pool := x509.NewCertPool() + if !pool.AppendCertsFromPEM(ca) { + return errors.New("cannot parse function server CA certificate") + } + clienttls.RootCAs = pool + } + pfrm := xfn.NewPrometheusMetrics() metrics.Registry.MustRegister(pfrm)