diff --git a/cmd/cluster-capi-operator/main.go b/cmd/cluster-capi-operator/main.go index 8dcfab8d93..e2329e78a8 100644 --- a/cmd/cluster-capi-operator/main.go +++ b/cmd/cluster-capi-operator/main.go @@ -15,6 +15,7 @@ package main import ( "context" + "crypto/tls" "flag" "fmt" "os" @@ -65,6 +66,7 @@ import ( "github.com/openshift/cluster-capi-operator/pkg/operatorstatus" "github.com/openshift/cluster-capi-operator/pkg/util" "github.com/openshift/cluster-capi-operator/pkg/webhook" + utiltls "github.com/openshift/library-go/pkg/controllerruntime/tls" ) const ( @@ -95,6 +97,11 @@ func main() { scheme := runtime.NewScheme() initScheme(scheme) + // Create a context that can be cancelled when there is a need to shut down the manager . + ctx, cancel := context.WithCancel(ctrl.SetupSignalHandler()) + // Ensure the context is cancelled when the program exits. + defer cancel() + leaderElectionConfig := config.LeaderElectionConfiguration{ LeaderElect: true, LeaseDuration: util.LeaseDuration, @@ -148,6 +155,29 @@ func main() { capiflags.AddManagerOptions(pflag.CommandLine, &capiManagerOptions) pflag.Parse() + cfg := ctrl.GetConfigOrDie() + + k8sClient, err := client.New(cfg, client.Options{Scheme: scheme}) + if err != nil { + klog.Error(err, "unable to create Kubernetes client") + os.Exit(1) + } + + // Fetch the TLS profile from the APIServer resource. + tlsSecurityProfileSpec, err := utiltls.FetchAPIServerTLSProfile(ctx, k8sClient) + if err != nil { + klog.Error(err, "unable to get TLS profile from API server") + os.Exit(1) + } + + // Create the TLS configuration function for the server endpoints. + tlsConfig, unsupportedCiphers := utiltls.NewTLSConfigFromProfile(tlsSecurityProfileSpec) + if len(unsupportedCiphers) > 0 { + klog.Info("TLS configuration contains unsupported ciphers that will be ignored", + "unsupportedCiphers", unsupportedCiphers, + ) + } + if err := setFeatureGatesEnvVars(); err != nil { klog.Error(err, "unable to set feature gates environment variables") os.Exit(1) @@ -157,32 +187,37 @@ func main() { klog.LogToStderr(*logToStderr) } - _, diagnosticsOpts, err := capiflags.GetManagerOptions(capiManagerOptions) + _, metricsOptions, err := capiflags.GetManagerOptions(capiManagerOptions) if err != nil { klog.Error(err, "unable to get manager options") os.Exit(1) } + // Override the TLS options for the metrics server with the ones specified centrally in the APIServer resource. + tlsOptions := []func(config *tls.Config){tlsConfig} + metricsOptions.TLSOpts = tlsOptions + syncPeriod := 10 * time.Minute cacheOpts := getDefaultCacheOptions(*managedNamespace, syncPeriod) - cfg := ctrl.GetConfigOrDie() - mgr, err := ctrl.NewManager(cfg, ctrl.Options{ Scheme: scheme, - Metrics: *diagnosticsOpts, + Metrics: *metricsOptions, HealthProbeBindAddress: *healthAddr, LeaderElectionNamespace: leaderElectionConfig.ResourceNamespace, LeaderElection: leaderElectionConfig.LeaderElect, LeaseDuration: &leaderElectionConfig.LeaseDuration.Duration, LeaderElectionID: leaderElectionConfig.ResourceName, - RetryPeriod: &leaderElectionConfig.RetryPeriod.Duration, - RenewDeadline: &leaderElectionConfig.RenewDeadline.Duration, - Cache: cacheOpts, + // Release the leader election when the context is cancelled, to recover quicker on restarts. + LeaderElectionReleaseOnCancel: true, + RetryPeriod: &leaderElectionConfig.RetryPeriod.Duration, + RenewDeadline: &leaderElectionConfig.RenewDeadline.Duration, + Cache: cacheOpts, WebhookServer: crwebhook.NewServer(crwebhook.Options{ Port: *webhookPort, CertDir: *webhookCertDir, + TLSOpts: tlsOptions, }), }) if err != nil { @@ -220,7 +255,7 @@ func main() { os.Exit(1) } - setupPlatformReconcilers(mgr, infra, platform, containerImages, applyClient, apiextensionsClient, *managedNamespace) + setupPlatformReconcilers(mgr, infra, platform, containerImages, applyClient, apiextensionsClient, *managedNamespace, tlsOptions, tlsSecurityProfileSpec, cancel) // +kubebuilder:scaffold:builder @@ -236,7 +271,7 @@ func main() { klog.Info("Starting manager") - if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + if err := mgr.Start(ctx); err != nil { klog.Error(err, "problem running manager") os.Exit(1) } @@ -252,17 +287,17 @@ func getClusterOperatorStatusClient(mgr manager.Manager, controller string, plat } } -func setupPlatformReconcilers(mgr manager.Manager, infra *configv1.Infrastructure, platform configv1.PlatformType, containerImages map[string]string, applyClient *kubernetes.Clientset, apiextensionsClient *apiextensionsclient.Clientset, managedNamespace string) { +func setupPlatformReconcilers(mgr manager.Manager, infra *configv1.Infrastructure, platform configv1.PlatformType, containerImages map[string]string, applyClient *kubernetes.Clientset, apiextensionsClient *apiextensionsclient.Clientset, managedNamespace string, tlsOptions []func(config *tls.Config), tlsSecurityProfileSpec configv1.TLSProfileSpec, cancel context.CancelFunc) { // Only setup reconcile controllers and webhooks when the platform is supported. // This avoids unnecessary CAPI providers discovery, installs and reconciles when the platform is not supported. isUnsupportedPlatform := false switch platform { case configv1.AWSPlatformType: - setupReconcilers(mgr, infra, platform, &awsv1.AWSCluster{}, containerImages, applyClient, apiextensionsClient, managedNamespace) + setupReconcilers(mgr, infra, platform, &awsv1.AWSCluster{}, containerImages, applyClient, apiextensionsClient, managedNamespace, tlsOptions, tlsSecurityProfileSpec, cancel) setupWebhooks(mgr) case configv1.GCPPlatformType: - setupReconcilers(mgr, infra, platform, &gcpv1.GCPCluster{}, containerImages, applyClient, apiextensionsClient, managedNamespace) + setupReconcilers(mgr, infra, platform, &gcpv1.GCPCluster{}, containerImages, applyClient, apiextensionsClient, managedNamespace, tlsOptions, tlsSecurityProfileSpec, cancel) setupWebhooks(mgr) case configv1.AzurePlatformType: azureCloudEnvironment := getAzureCloudEnvironment(infra.Status.PlatformStatus) @@ -272,20 +307,20 @@ func setupPlatformReconcilers(mgr manager.Manager, infra *configv1.Infrastructur isUnsupportedPlatform = true } else { // The ClusterOperator Controller must run in all cases. - setupReconcilers(mgr, infra, platform, &azurev1.AzureCluster{}, containerImages, applyClient, apiextensionsClient, managedNamespace) + setupReconcilers(mgr, infra, platform, &azurev1.AzureCluster{}, containerImages, applyClient, apiextensionsClient, managedNamespace, tlsOptions, tlsSecurityProfileSpec, cancel) setupWebhooks(mgr) } case configv1.PowerVSPlatformType: - setupReconcilers(mgr, infra, platform, &ibmpowervsv1.IBMPowerVSCluster{}, containerImages, applyClient, apiextensionsClient, managedNamespace) + setupReconcilers(mgr, infra, platform, &ibmpowervsv1.IBMPowerVSCluster{}, containerImages, applyClient, apiextensionsClient, managedNamespace, tlsOptions, tlsSecurityProfileSpec, cancel) setupWebhooks(mgr) case configv1.VSpherePlatformType: - setupReconcilers(mgr, infra, platform, &vspherev1.VSphereCluster{}, containerImages, applyClient, apiextensionsClient, managedNamespace) + setupReconcilers(mgr, infra, platform, &vspherev1.VSphereCluster{}, containerImages, applyClient, apiextensionsClient, managedNamespace, tlsOptions, tlsSecurityProfileSpec, cancel) setupWebhooks(mgr) case configv1.OpenStackPlatformType: - setupReconcilers(mgr, infra, platform, &openstackv1.OpenStackCluster{}, containerImages, applyClient, apiextensionsClient, managedNamespace) + setupReconcilers(mgr, infra, platform, &openstackv1.OpenStackCluster{}, containerImages, applyClient, apiextensionsClient, managedNamespace, tlsOptions, tlsSecurityProfileSpec, cancel) setupWebhooks(mgr) case configv1.BareMetalPlatformType: - setupReconcilers(mgr, infra, platform, &metal3v1.Metal3Cluster{}, containerImages, applyClient, apiextensionsClient, managedNamespace) + setupReconcilers(mgr, infra, platform, &metal3v1.Metal3Cluster{}, containerImages, applyClient, apiextensionsClient, managedNamespace, tlsOptions, tlsSecurityProfileSpec, cancel) setupWebhooks(mgr) default: klog.Infof("Detected platform %q is not supported, skipping capi controllers setup", platform) @@ -297,7 +332,7 @@ func setupPlatformReconcilers(mgr manager.Manager, infra *configv1.Infrastructur setupClusterOperatorController(mgr, platform, managedNamespace, isUnsupportedPlatform) } -func setupReconcilers(mgr manager.Manager, infra *configv1.Infrastructure, platform configv1.PlatformType, infraClusterObject client.Object, containerImages map[string]string, applyClient *kubernetes.Clientset, apiextensionsClient *apiextensionsclient.Clientset, managedNamespace string) { +func setupReconcilers(mgr manager.Manager, infra *configv1.Infrastructure, platform configv1.PlatformType, infraClusterObject client.Object, containerImages map[string]string, applyClient *kubernetes.Clientset, apiextensionsClient *apiextensionsclient.Clientset, managedNamespace string, tlsOptions []func(config *tls.Config), tlsSecurityProfileSpec configv1.TLSProfileSpec, cancel context.CancelFunc) { if err := (&corecluster.CoreClusterController{ ClusterOperatorStatusClient: getClusterOperatorStatusClient(mgr, "cluster-capi-operator-cluster-resource-controller", platform, managedNamespace), Cluster: &clusterv1.Cluster{}, @@ -333,6 +368,7 @@ func setupReconcilers(mgr manager.Manager, infra *configv1.Infrastructure, platf Platform: platform, ApplyClient: applyClient, APIExtensionsClient: apiextensionsClient, + TLSOpts: tlsOptions, }).SetupWithManager(mgr); err != nil { klog.Error(err, "unable to create capi installer controller", "controller", "CAPIInstaller") os.Exit(1) @@ -349,6 +385,17 @@ func setupReconcilers(mgr manager.Manager, infra *configv1.Infrastructure, platf klog.Error(err, "unable to create infracluster controller", "controller", "InfraCluster") os.Exit(1) } + + // Set up the TLS security profile watcher controller. + // This will trigger a graceful shutdown when the TLS profile changes. + if err := (&utiltls.TLSSecurityProfileWatcher{ + Client: mgr.GetClient(), + InitialTLSProfileSpec: tlsSecurityProfileSpec, + Shutdown: cancel, + }).SetupWithManager(mgr); err != nil { + klog.Error(err, "unable to create controller", "controller", "TLSSecurityProfileWatcher") + os.Exit(1) + } } func setupWebhooks(mgr ctrl.Manager) { diff --git a/cmd/machine-api-migration/main.go b/cmd/machine-api-migration/main.go index 67ff79ee24..892e557fa0 100644 --- a/cmd/machine-api-migration/main.go +++ b/cmd/machine-api-migration/main.go @@ -125,7 +125,7 @@ func main() { klog.LogToStderr(*logToStderr) } - _, diagnosticsOpts, err := capiflags.GetManagerOptions(capiManagerOptions) + _, metricsOpts, err := capiflags.GetManagerOptions(capiManagerOptions) if err != nil { klog.Error(err, "unable to get manager options") os.Exit(1) @@ -139,15 +139,17 @@ func main() { mgr, err := ctrl.NewManager(cfg, ctrl.Options{ Scheme: scheme, - Metrics: *diagnosticsOpts, + Metrics: *metricsOpts, HealthProbeBindAddress: *healthAddr, LeaderElectionNamespace: leaderElectionConfig.ResourceNamespace, LeaderElection: leaderElectionConfig.LeaderElect, LeaseDuration: &leaderElectionConfig.LeaseDuration.Duration, LeaderElectionID: leaderElectionConfig.ResourceName, - RetryPeriod: &leaderElectionConfig.RetryPeriod.Duration, - RenewDeadline: &leaderElectionConfig.RenewDeadline.Duration, - Cache: cacheOpts, + // Release the leader election when the context is cancelled, to recover quicker on restarts. + LeaderElectionReleaseOnCancel: true, + RetryPeriod: &leaderElectionConfig.RetryPeriod.Duration, + RenewDeadline: &leaderElectionConfig.RenewDeadline.Duration, + Cache: cacheOpts, }) if err != nil { klog.Error(err, "unable to create manager") diff --git a/e2e/go.mod b/e2e/go.mod index 5b9b6561f2..992ff2f5a0 100644 --- a/e2e/go.mod +++ b/e2e/go.mod @@ -19,9 +19,9 @@ require ( github.com/openshift/api v0.0.0-20260105114749-aae5635a71a7 github.com/openshift/cluster-api-actuator-pkg v0.0.0-20251203134942-d9bd7b8593f3 github.com/openshift/cluster-api-provider-baremetal v0.0.0-20250619124612-fb678fec5f7e - k8s.io/api v0.34.1 - k8s.io/apimachinery v0.34.1 - k8s.io/client-go v0.34.1 + k8s.io/api v0.34.3 + k8s.io/apimachinery v0.34.3 + k8s.io/client-go v0.34.3 k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d sigs.k8s.io/cluster-api v1.11.3 sigs.k8s.io/cluster-api-provider-aws/v2 v2.10.0 @@ -30,7 +30,7 @@ require ( sigs.k8s.io/cluster-api-provider-ibmcloud v0.12.0 sigs.k8s.io/cluster-api-provider-openstack v0.13.1 sigs.k8s.io/cluster-api-provider-vsphere v1.14.0 - sigs.k8s.io/controller-runtime v0.22.4 + sigs.k8s.io/controller-runtime v0.22.5 sigs.k8s.io/yaml v1.6.0 ) @@ -123,8 +123,8 @@ require ( gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.34.1 // indirect - k8s.io/component-base v0.34.1 // indirect + k8s.io/apiextensions-apiserver v0.34.3 // indirect + k8s.io/component-base v0.34.3 // indirect k8s.io/klog v1.0.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect diff --git a/e2e/go.sum b/e2e/go.sum index 1147a8b94a..830ad1b480 100644 --- a/e2e/go.sum +++ b/e2e/go.sum @@ -359,18 +359,12 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= -k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= -k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI= -k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc= -k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= -k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= -k8s.io/apiserver v0.34.1 h1:U3JBGdgANK3dfFcyknWde1G6X1F4bg7PXuvlqt8lITA= -k8s.io/apiserver v0.34.1/go.mod h1:eOOc9nrVqlBI1AFCvVzsob0OxtPZUCPiUJL45JOTBG0= -k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY= -k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8= -k8s.io/component-base v0.34.1 h1:v7xFgG+ONhytZNFpIz5/kecwD+sUhVE6HU7qQUiRM4A= -k8s.io/component-base v0.34.1/go.mod h1:mknCpLlTSKHzAQJJnnHVKqjxR7gBeHRv0rPXA7gdtQ0= +k8s.io/api v0.34.3 h1:D12sTP257/jSH2vHV2EDYrb16bS7ULlHpdNdNhEw2S4= +k8s.io/apiextensions-apiserver v0.34.3 h1:p10fGlkDY09eWKOTeUSioxwLukJnm+KuDZdrW71y40g= +k8s.io/apimachinery v0.34.3 h1:/TB+SFEiQvN9HPldtlWOTp0hWbJ+fjU+wkxysf/aQnE= +k8s.io/apiserver v0.34.3 h1:uGH1qpDvSiYG4HVFqc6A3L4CKiX+aBWDrrsxHYK0Bdo= +k8s.io/client-go v0.34.3 h1:wtYtpzy/OPNYf7WyNBTj3iUA0XaBHVqhv4Iv3tbrF5A= +k8s.io/component-base v0.34.3 h1:zsEgw6ELqK0XncCQomgO9DpUIzlrYuZYA0Cgo+JWpVk= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= @@ -391,8 +385,7 @@ sigs.k8s.io/cluster-api-provider-openstack v0.13.1 h1:vN+NMrJMByn33EH3NiLvveZO7n sigs.k8s.io/cluster-api-provider-openstack v0.13.1/go.mod h1:8zIJiDXVHjSHgCZX49JbsR1Z03zhvCXk6a0zYpUZTcQ= sigs.k8s.io/cluster-api-provider-vsphere v1.14.0 h1:MR3Ry1DvFeeLHH4ldFi1kChpkkB5HPOWHP0viURbL8c= sigs.k8s.io/cluster-api-provider-vsphere v1.14.0/go.mod h1:QilGYtsQutBXDmdhSRyMDvJaKiuVaUaAf5LIirlZFZM= -sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A= -sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= +sigs.k8s.io/controller-runtime v0.22.5 h1:v3nfSUMowX/2WMp27J9slwGFyAt7IV0YwBxAkrUr0GE= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/kube-storage-version-migrator v0.0.6-0.20230721195810-5c8923c5ff96 h1:PFWFSkpArPNJxFX4ZKWAk9NSeRoZaXschn+ULa4xVek= diff --git a/go.mod b/go.mod index a79fc0eb17..7d4833a01c 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.24.0 // TODO: these replaces should be removed when corresponding PRs are merged replace ( github.com/openshift/cluster-api-actuator-pkg/testutils => github.com/openshift/cluster-api-actuator-pkg/testutils v0.0.0-20251211141525-c707612472dc + github.com/openshift/library-go => github.com/damdo/library-go v0.0.0-20260120133104-12bddc18ba38 sigs.k8s.io/cluster-api-provider-azure => github.com/openshift/cluster-api-provider-azure v0.0.0-20251202084521-c2e0e38d1e0e ) @@ -29,12 +30,12 @@ require ( github.com/spf13/pflag v1.0.10 golang.org/x/tools v0.38.0 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.34.1 - k8s.io/apiextensions-apiserver v0.34.1 - k8s.io/apimachinery v0.34.1 - k8s.io/apiserver v0.34.1 - k8s.io/client-go v0.34.1 - k8s.io/component-base v0.34.1 + k8s.io/api v0.34.3 + k8s.io/apiextensions-apiserver v0.34.3 + k8s.io/apimachinery v0.34.3 + k8s.io/apiserver v0.34.3 + k8s.io/client-go v0.34.3 + k8s.io/component-base v0.34.3 k8s.io/klog/v2 v2.130.1 k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d sigs.k8s.io/cluster-api v1.11.3 @@ -44,7 +45,7 @@ require ( sigs.k8s.io/cluster-api-provider-ibmcloud v0.12.0 sigs.k8s.io/cluster-api-provider-openstack v0.13.1 sigs.k8s.io/cluster-api-provider-vsphere v1.14.0 - sigs.k8s.io/controller-runtime v0.22.4 + sigs.k8s.io/controller-runtime v0.22.5 sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20240927101401-4381fa0aeee4 sigs.k8s.io/kube-storage-version-migrator v0.0.6-0.20230721195810-5c8923c5ff96 sigs.k8s.io/randfill v1.0.0 diff --git a/go.sum b/go.sum index 58292acf0d..22c7aa4876 100644 --- a/go.sum +++ b/go.sum @@ -125,6 +125,8 @@ github.com/curioswitch/go-reassign v0.3.0 h1:dh3kpQHuADL3cobV/sSGETA8DOv457dwl+f github.com/curioswitch/go-reassign v0.3.0/go.mod h1:nApPCCTtqLJN/s8HfItCcKV0jIPwluBOvZP+dsJGA88= github.com/daixiang0/gci v0.13.5 h1:kThgmH1yBmZSBCh1EJVxQ7JsHpm5Oms0AMed/0LaH4c= github.com/daixiang0/gci v0.13.5/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk= +github.com/damdo/library-go v0.0.0-20260120133104-12bddc18ba38 h1:G0e8pIhZwxvoJaDeqE3gFzEDyIT4ocUklMEorG9D2Gg= +github.com/damdo/library-go v0.0.0-20260120133104-12bddc18ba38/go.mod h1:hS7ztbAGo/uXecJwEgofGerQDVqSYnJNrN93+l+oWr8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -445,8 +447,6 @@ github.com/openshift/cluster-api-actuator-pkg/testutils v0.0.0-20251211141525-c7 github.com/openshift/cluster-api-actuator-pkg/testutils v0.0.0-20251211141525-c707612472dc/go.mod h1:Ahm3XvgKKIyKKYCG3oX2w7bE7twCT2kG71ykwwxUh18= github.com/openshift/cluster-api-provider-azure v0.0.0-20251202084521-c2e0e38d1e0e h1:yP0NOooxbZNQVd0zZQ/k2gy9T7vE7QfxmuaS8Q+oZD8= github.com/openshift/cluster-api-provider-azure v0.0.0-20251202084521-c2e0e38d1e0e/go.mod h1:x9y164Y8abORQEN+K/pz9w8oO4HNjp+trFKAOFmaYVM= -github.com/openshift/library-go v0.0.0-20251222131241-289839b3ffe8 h1:TWqbSjaYbZGgB6EmnEN6Hc8lQYYCgju2qORBX7Ix1LI= -github.com/openshift/library-go v0.0.0-20251222131241-289839b3ffe8/go.mod h1:nIzWQQE49XbiKizVnVOip9CEB7HJ0hoJwNi3g3YKnKc= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= @@ -824,18 +824,18 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.6.1 h1:R094WgE8K4JirYjBaOpz/AvTyUu/3wbmAoskKN/pxTI= honnef.co/go/tools v0.6.1/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4= -k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= -k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= -k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI= -k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc= -k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= -k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= -k8s.io/apiserver v0.34.1 h1:U3JBGdgANK3dfFcyknWde1G6X1F4bg7PXuvlqt8lITA= -k8s.io/apiserver v0.34.1/go.mod h1:eOOc9nrVqlBI1AFCvVzsob0OxtPZUCPiUJL45JOTBG0= -k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY= -k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8= -k8s.io/component-base v0.34.1 h1:v7xFgG+ONhytZNFpIz5/kecwD+sUhVE6HU7qQUiRM4A= -k8s.io/component-base v0.34.1/go.mod h1:mknCpLlTSKHzAQJJnnHVKqjxR7gBeHRv0rPXA7gdtQ0= +k8s.io/api v0.34.3 h1:D12sTP257/jSH2vHV2EDYrb16bS7ULlHpdNdNhEw2S4= +k8s.io/api v0.34.3/go.mod h1:PyVQBF886Q5RSQZOim7DybQjAbVs8g7gwJNhGtY5MBk= +k8s.io/apiextensions-apiserver v0.34.3 h1:p10fGlkDY09eWKOTeUSioxwLukJnm+KuDZdrW71y40g= +k8s.io/apiextensions-apiserver v0.34.3/go.mod h1:aujxvqGFRdb/cmXYfcRTeppN7S2XV/t7WMEc64zB5A0= +k8s.io/apimachinery v0.34.3 h1:/TB+SFEiQvN9HPldtlWOTp0hWbJ+fjU+wkxysf/aQnE= +k8s.io/apimachinery v0.34.3/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/apiserver v0.34.3 h1:uGH1qpDvSiYG4HVFqc6A3L4CKiX+aBWDrrsxHYK0Bdo= +k8s.io/apiserver v0.34.3/go.mod h1:QPnnahMO5C2m3lm6fPW3+JmyQbvHZQ8uudAu/493P2w= +k8s.io/client-go v0.34.3 h1:wtYtpzy/OPNYf7WyNBTj3iUA0XaBHVqhv4Iv3tbrF5A= +k8s.io/client-go v0.34.3/go.mod h1:OxxeYagaP9Kdf78UrKLa3YZixMCfP6bgPwPwNBQBzpM= +k8s.io/component-base v0.34.3 h1:zsEgw6ELqK0XncCQomgO9DpUIzlrYuZYA0Cgo+JWpVk= +k8s.io/component-base v0.34.3/go.mod h1:5iIlD8wPfWE/xSHTRfbjuvUul2WZbI2nOUK65XL0E/c= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-aggregator v0.34.1 h1:WNLV0dVNoFKmuyvdWLd92iDSyD/TSTjqwaPj0U9XAEU= @@ -862,8 +862,8 @@ sigs.k8s.io/cluster-api-provider-openstack v0.13.1 h1:vN+NMrJMByn33EH3NiLvveZO7n sigs.k8s.io/cluster-api-provider-openstack v0.13.1/go.mod h1:8zIJiDXVHjSHgCZX49JbsR1Z03zhvCXk6a0zYpUZTcQ= sigs.k8s.io/cluster-api-provider-vsphere v1.14.0 h1:MR3Ry1DvFeeLHH4ldFi1kChpkkB5HPOWHP0viURbL8c= sigs.k8s.io/cluster-api-provider-vsphere v1.14.0/go.mod h1:QilGYtsQutBXDmdhSRyMDvJaKiuVaUaAf5LIirlZFZM= -sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A= -sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= +sigs.k8s.io/controller-runtime v0.22.5 h1:v3nfSUMowX/2WMp27J9slwGFyAt7IV0YwBxAkrUr0GE= +sigs.k8s.io/controller-runtime v0.22.5/go.mod h1:pc5SoYWnWI6I+cBHYYdZ7B6YHZVY5xNfll88JB+vniI= sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20240927101401-4381fa0aeee4 h1:Y6/OyKMyJzeYM4eStqGxFOplVLu3tgXu79VOfxZnTH4= sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20240927101401-4381fa0aeee4/go.mod h1:IaDsO8xSPRxRG1/rm9CP7+jPmj0nMNAuNi/yiHnLX8k= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= diff --git a/manifests-gen/go.mod b/manifests-gen/go.mod index 80a86c44e1..55c1ab1e6f 100644 --- a/manifests-gen/go.mod +++ b/manifests-gen/go.mod @@ -7,10 +7,10 @@ toolchain go1.24.4 require ( github.com/cert-manager/cert-manager v1.18.2 github.com/klauspost/compress v1.18.0 - k8s.io/api v0.34.1 - k8s.io/apiextensions-apiserver v0.34.1 - k8s.io/apimachinery v0.34.1 - k8s.io/client-go v0.34.1 + k8s.io/api v0.34.3 + k8s.io/apiextensions-apiserver v0.34.3 + k8s.io/apimachinery v0.34.3 + k8s.io/client-go v0.34.3 sigs.k8s.io/cluster-api v1.11.3 // Update kustomize when updating k8s sigs.k8s.io/kustomize/api v0.20.1 @@ -96,11 +96,11 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/cluster-bootstrap v0.33.3 // indirect - k8s.io/component-base v0.34.1 // indirect + k8s.io/component-base v0.34.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d // indirect - sigs.k8s.io/controller-runtime v0.22.4 // indirect + sigs.k8s.io/controller-runtime v0.22.5 // indirect sigs.k8s.io/gateway-api v1.1.0 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect sigs.k8s.io/randfill v1.0.0 // indirect diff --git a/manifests-gen/go.sum b/manifests-gen/go.sum index 2704e30897..eb7f79b8c3 100644 --- a/manifests-gen/go.sum +++ b/manifests-gen/go.sum @@ -241,18 +241,13 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= -k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= -k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI= -k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc= -k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= -k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= -k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY= -k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8= +k8s.io/api v0.34.3 h1:D12sTP257/jSH2vHV2EDYrb16bS7ULlHpdNdNhEw2S4= +k8s.io/apiextensions-apiserver v0.34.3 h1:p10fGlkDY09eWKOTeUSioxwLukJnm+KuDZdrW71y40g= +k8s.io/apimachinery v0.34.3 h1:/TB+SFEiQvN9HPldtlWOTp0hWbJ+fjU+wkxysf/aQnE= +k8s.io/client-go v0.34.3 h1:wtYtpzy/OPNYf7WyNBTj3iUA0XaBHVqhv4Iv3tbrF5A= k8s.io/cluster-bootstrap v0.33.3 h1:u2NTxJ5CFSBFXaDxLQoOWMly8eni31psVso+caq6uwI= k8s.io/cluster-bootstrap v0.33.3/go.mod h1:p970f8u8jf273zyQ5raD8WUu2XyAl0SAWOY82o7i/ds= -k8s.io/component-base v0.34.1 h1:v7xFgG+ONhytZNFpIz5/kecwD+sUhVE6HU7qQUiRM4A= -k8s.io/component-base v0.34.1/go.mod h1:mknCpLlTSKHzAQJJnnHVKqjxR7gBeHRv0rPXA7gdtQ0= +k8s.io/component-base v0.34.3 h1:zsEgw6ELqK0XncCQomgO9DpUIzlrYuZYA0Cgo+JWpVk= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= @@ -261,8 +256,7 @@ k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d h1:wAhiDyZ4Tdtt7e46e9M5ZSAJ/MnPG k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/cluster-api v1.11.3 h1:apxfugbP1X8AG7THCM74CTarCOW4H2oOc6hlbm1hY80= sigs.k8s.io/cluster-api v1.11.3/go.mod h1:CA471SACi81M8DzRKTlWpHV33G0cfWEj7sC4fALFVok= -sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A= -sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= +sigs.k8s.io/controller-runtime v0.22.5 h1:v3nfSUMowX/2WMp27J9slwGFyAt7IV0YwBxAkrUr0GE= sigs.k8s.io/gateway-api v1.1.0 h1:DsLDXCi6jR+Xz8/xd0Z1PYl2Pn0TyaFMOPPZIj4inDM= sigs.k8s.io/gateway-api v1.1.0/go.mod h1:ZH4lHrL2sDi0FHZ9jjneb8kKnGzFWyrTya35sWUTrRs= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= diff --git a/pkg/controllers/capiinstaller/capi_installer_controller.go b/pkg/controllers/capiinstaller/capi_installer_controller.go index b2802b371e..4bfad70e43 100644 --- a/pkg/controllers/capiinstaller/capi_installer_controller.go +++ b/pkg/controllers/capiinstaller/capi_installer_controller.go @@ -17,6 +17,7 @@ package capiinstaller import ( "context" + "crypto/tls" "errors" "fmt" "regexp" @@ -25,6 +26,8 @@ import ( "github.com/drone/envsubst/v2" "github.com/go-logr/logr" + openshiftcrypto "github.com/openshift/library-go/pkg/crypto" + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -87,6 +90,7 @@ type CapiInstallerController struct { Platform configv1.PlatformType ApplyClient *kubernetes.Clientset APIExtensionsClient *apiextensionsclient.Clientset + TLSOpts []func(config *tls.Config) } // Reconcile reconciles the cluster-api ClusterOperator object. @@ -234,6 +238,8 @@ func (r *CapiInstallerController) applyProviderComponents(ctx context.Context, c return fmt.Errorf("error casting object to Deployment: %w", err) } + injectTLSConfigIntoDeployment(deployment, r.TLSOpts) + if _, _, err := resourceapply.ApplyDeployment( ctx, r.ApplyClient.AppsV1(), @@ -530,3 +536,28 @@ func yamlToUnstructured(sch *runtime.Scheme, m string) (*unstructured.Unstructur return u, nil } + +// injectTLSConfigIntoDeployment injects TLS configuration into the deployment spec, +// setting --tls-min-version and --tls-cipher-suites on the container named "manager" +// as described in https://github.com/kubernetes-sigs/cluster-api/blob/55e16f424c0ed8d3739070125d4c32a036997465/util/flags/manager.go#L59-L71. +func injectTLSConfigIntoDeployment(deployment *appsv1.Deployment, tlsOpts []func(*tls.Config)) { + tlsConfig := &tls.Config{} + for _, tlsOpt := range tlsOpts { + tlsOpt(tlsConfig) + } + + for i := range deployment.Spec.Template.Spec.Containers { + if deployment.Spec.Template.Spec.Containers[i].Name == "manager" { + deployment.Spec.Template.Spec.Containers[i].Args = append( + deployment.Spec.Template.Spec.Containers[i].Args, + fmt.Sprintf("--tls-min-version=%s", openshiftcrypto.TLSVersionToNameOrDie(tlsConfig.MinVersion)), + ) + if len(tlsConfig.CipherSuites) > 0 { + deployment.Spec.Template.Spec.Containers[i].Args = append( + deployment.Spec.Template.Spec.Containers[i].Args, + fmt.Sprintf("--tls-cipher-suites=%s", strings.Join(openshiftcrypto.CipherSuitesToNamesOrDie(tlsConfig.CipherSuites), ",")), + ) + } + } + } +} diff --git a/vendor/github.com/openshift/library-go/pkg/controllerruntime/tls/controller.go b/vendor/github.com/openshift/library-go/pkg/controllerruntime/tls/controller.go new file mode 100644 index 0000000000..b54e5d5656 --- /dev/null +++ b/vendor/github.com/openshift/library-go/pkg/controllerruntime/tls/controller.go @@ -0,0 +1,129 @@ +/* +Copyright 2026 Red Hat, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tls + +import ( + "context" + "fmt" + "reflect" + + "github.com/go-logr/logr" + apierrors "k8s.io/apimachinery/pkg/api/errors" + + configv1 "github.com/openshift/api/config/v1" + + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +// TLSSecurityProfileWatcher watches the APIServer object for TLS profile changes +// and triggers a graceful shutdown when the profile changes. +type TLSSecurityProfileWatcher struct { + client.Client + + // InitialTLSProfileSpec is the TLS profile spec that was configured when the operator started. + InitialTLSProfileSpec configv1.TLSProfileSpec + + // Shutdown is a function that will be called to trigger a graceful shutdown + // when the TLS profile changes. + Shutdown context.CancelFunc +} + +// SetupWithManager sets up the controller with the Manager. +func (r *TLSSecurityProfileWatcher) SetupWithManager(mgr ctrl.Manager) error { + if err := ctrl.NewControllerManagedBy(mgr). + Named("tlssecurityprofilewatcher"). + For(&configv1.APIServer{}, builder.WithPredicates( + predicate.Funcs{ + // Only watch the "cluster" APIServer object. + CreateFunc: func(e event.CreateEvent) bool { + return e.Object.GetName() == APIServerName + }, + UpdateFunc: func(e event.UpdateEvent) bool { + return e.ObjectNew.GetName() == APIServerName + }, + DeleteFunc: func(e event.DeleteEvent) bool { + return e.Object.GetName() == APIServerName + }, + GenericFunc: func(e event.GenericEvent) bool { + return e.Object.GetName() == APIServerName + }, + }, + )). + // Override the default log constructor as it makes the logs very chatty. + WithLogConstructor(func(req *reconcile.Request) logr.Logger { + return mgr.GetLogger().WithValues( + "controller", "tlssecurityprofilewatcher", + ) + }). + Complete(r); err != nil { + return fmt.Errorf("could not set up controller for TLS security profile watcher: %w", err) + } + + return nil +} + +// Reconcile watches for changes to the APIServer TLS profile and triggers a shutdown +// when the profile changes from the initial configuration. +func (r *TLSSecurityProfileWatcher) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx, "name", req.Name) + + logger.V(1).Info("Reconciling APIServer TLS profile") + + // Fetch the APIServer object. + apiServer := &configv1.APIServer{} + if err := r.Get(ctx, req.NamespacedName, apiServer); err != nil { + if apierrors.IsNotFound(err) { + // If the APIServer object is not found, we don't need to do anything. + // This could happen if the object was deleted. + return ctrl.Result{}, nil + } + + return ctrl.Result{}, fmt.Errorf("failed to get APIServer %s: %w", req.NamespacedName.String(), err) + } + + // Get the current TLS profile spec. + currentTLSProfileSpec, err := GetTLSProfileSpec(apiServer.Spec.TLSSecurityProfile) + if err != nil { + return ctrl.Result{}, fmt.Errorf("failed to get TLS profile from APIServer %s: %w", req.NamespacedName.String(), err) + } + + // Compare the current TLS profile spec with the initial one. + if !reflect.DeepEqual(r.InitialTLSProfileSpec, currentTLSProfileSpec) { + logger.Info("TLS security profile has changed, initiating a shutdown of the operator to make it pick up the new configuration", + "initialMinTLSVersion", r.InitialTLSProfileSpec.MinTLSVersion, + "currentMinTLSVersion", currentTLSProfileSpec.MinTLSVersion, + "initialCiphers", r.InitialTLSProfileSpec.Ciphers, + "currentCiphers", currentTLSProfileSpec.Ciphers, + ) + + // Trigger the shutdown of the operator to make it pick up the new configuration. + r.Shutdown() + + // Return immediately, no need to requeue. + return ctrl.Result{}, nil + } + + logger.V(1).Info("TLS security profile unchanged") + + return ctrl.Result{}, nil +} diff --git a/vendor/github.com/openshift/library-go/pkg/controllerruntime/tls/tls.go b/vendor/github.com/openshift/library-go/pkg/controllerruntime/tls/tls.go new file mode 100644 index 0000000000..1b3e0eedaf --- /dev/null +++ b/vendor/github.com/openshift/library-go/pkg/controllerruntime/tls/tls.go @@ -0,0 +1,156 @@ +/* +Copyright 2026 Red Hat, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tls + +import ( + "context" + "crypto/tls" + "errors" + "fmt" + + configv1 "github.com/openshift/api/config/v1" + libgocrypto "github.com/openshift/library-go/pkg/crypto" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const ( + // APIServerName is the name of the APIServer resource in the cluster. + APIServerName = "cluster" +) + +var ( + // ErrCustomProfileNil is returned when a custom TLS profile is specified but the Custom field is nil. + ErrCustomProfileNil = errors.New("custom TLS profile specified but Custom field is nil") + + // DefaultTLSProfileType is the intermediate profile type. + DefaultTLSProfileType = configv1.TLSProfileIntermediateType //nolint:gochecknoglobals + // DefaultTLSCiphers are the default TLS ciphers for API servers. + DefaultTLSCiphers = configv1.TLSProfiles[DefaultTLSProfileType].Ciphers //nolint:gochecknoglobals + // DefaultMinTLSVersion is the default minimum TLS version for API servers. + DefaultMinTLSVersion = configv1.TLSProfiles[DefaultTLSProfileType].MinTLSVersion //nolint:gochecknoglobals +) + +// FetchAPIServerTLSProfile fetches the TLS profile spec configured in APIServer. +// If no profile is configured, the default profile is returned. +func FetchAPIServerTLSProfile(ctx context.Context, k8sClient client.Client) (configv1.TLSProfileSpec, error) { + apiServer := &configv1.APIServer{} + key := client.ObjectKey{Name: APIServerName} + + if err := k8sClient.Get(ctx, key, apiServer); err != nil { + return configv1.TLSProfileSpec{}, fmt.Errorf("failed to get APIServer %q: %w", key.String(), err) + } + + profile, err := GetTLSProfileSpec(apiServer.Spec.TLSSecurityProfile) + if err != nil { + return configv1.TLSProfileSpec{}, fmt.Errorf("failed to get TLS profile from APIServer %q: %w", key.String(), err) + } + + return profile, nil +} + +// GetTLSProfileSpec returns TLSProfileSpec for the given profile. +// If no profile is configured, the default profile is returned. +func GetTLSProfileSpec(profile *configv1.TLSSecurityProfile) (configv1.TLSProfileSpec, error) { + // Define the default profile (at the time of writing, this is the intermediate profile). + defaultProfile := *configv1.TLSProfiles[DefaultTLSProfileType] + // If the profile is nil or the type is empty, return the default profile. + if profile == nil || profile.Type == "" { + return defaultProfile, nil + } + + // Get the profile type. + profileType := profile.Type + + // If the profile type is not custom, return the profile from the map. + if profileType != configv1.TLSProfileCustomType { + if tlsConfig, ok := configv1.TLSProfiles[profileType]; ok { + return *tlsConfig, nil + } + + // If the profile type is not found, return the default profile. + return defaultProfile, nil + } + + if profile.Custom == nil { + // If the custom profile is nil, return an error. + return configv1.TLSProfileSpec{}, ErrCustomProfileNil + } + + // Return the custom profile spec. + return profile.Custom.TLSProfileSpec, nil +} + +// NewTLSConfigFromProfile returns a function that configures a tls.Config based on the provided TLSProfileSpec, +// along with any cipher names from the profile that are not supported by the library-go crypto package. +// The returned function is intended to be used with controller-runtime's TLSOpts. +// +// Note: CipherSuites are only set when MinVersion is below TLS 1.3, as Go's TLS 1.3 implementation +// does not allow configuring cipher suites - all TLS 1.3 ciphers are always enabled. +// See: https://github.com/golang/go/issues/29349 +func NewTLSConfigFromProfile(profile configv1.TLSProfileSpec) (tlsConfig func(*tls.Config), unsupportedCiphers []string) { + minVersion := libgocrypto.TLSVersionOrDie(string(profile.MinTLSVersion)) + cipherSuites, unsupportedCiphers := cipherCodes(profile.Ciphers) + + return func(tlsConf *tls.Config) { + tlsConf.MinVersion = minVersion + // TODO: add curve preferences from profile once https://github.com/openshift/api/pull/2583 merges. + // tlsConf.CurvePreferences <<<<<< profile.Curves + + // TLS 1.3 cipher suites are not configurable in Go (https://github.com/golang/go/issues/29349), so only set CipherSuites accordingly. + // TODO: revisit this once we get an answer on the best way to handle this here: + // https://docs.google.com/document/d/1cMc9E8psHfnoK06ntR8kHSWB8d3rMtmldhnmM4nImjs/edit?disco=AAABu_nPcYg + if minVersion != tls.VersionTLS13 { + tlsConf.CipherSuites = cipherSuites + } + }, unsupportedCiphers +} + +// cipherCode returns the TLS cipher code for an OpenSSL or IANA cipher name. +// Returns 0 if the cipher is not supported. +func cipherCode(cipher string) uint16 { + // First try as IANA name directly. + if code, err := libgocrypto.CipherSuite(cipher); err == nil { + return code + } + + // Try converting from OpenSSL name to IANA name. + ianaCiphers := libgocrypto.OpenSSLToIANACipherSuites([]string{cipher}) + if len(ianaCiphers) == 1 { + if code, err := libgocrypto.CipherSuite(ianaCiphers[0]); err == nil { + return code + } + } + + // Return 0 if the cipher is not supported. + return 0 +} + +// cipherCodes converts a list of cipher names (OpenSSL or IANA format) to their uint16 codes. +// Returns the converted codes and a list of any unsupported cipher names. +func cipherCodes(ciphers []string) (codes []uint16, unsupportedCiphers []string) { + for _, cipher := range ciphers { + code := cipherCode(cipher) + if code == 0 { + unsupportedCiphers = append(unsupportedCiphers, cipher) + continue + } + + codes = append(codes, code) + } + + return codes, unsupportedCiphers +} diff --git a/vendor/k8s.io/apiserver/pkg/admission/plugin/policy/generic/policy_matcher.go b/vendor/k8s.io/apiserver/pkg/admission/plugin/policy/generic/policy_matcher.go index d243b0710b..08ddcbf0a0 100644 --- a/vendor/k8s.io/apiserver/pkg/admission/plugin/policy/generic/policy_matcher.go +++ b/vendor/k8s.io/apiserver/pkg/admission/plugin/policy/generic/policy_matcher.go @@ -17,6 +17,7 @@ limitations under the License. package generic import ( + "context" "fmt" admissionregistrationv1 "k8s.io/api/admissionregistration/v1" @@ -41,8 +42,8 @@ type PolicyMatcher interface { BindingMatches(a admission.Attributes, o admission.ObjectInterfaces, binding BindingAccessor) (bool, error) // GetNamespace retrieves the Namespace resource by the given name. The name may be empty, in which case - // GetNamespace must return nil, nil - GetNamespace(name string) (*corev1.Namespace, error) + // GetNamespace must return nil, NotFound + GetNamespace(ctx context.Context, name string) (*corev1.Namespace, error) } type matcher struct { @@ -82,8 +83,8 @@ func (c *matcher) BindingMatches(a admission.Attributes, o admission.ObjectInter return isMatch, err } -func (c *matcher) GetNamespace(name string) (*corev1.Namespace, error) { - return c.Matcher.GetNamespace(name) +func (c *matcher) GetNamespace(ctx context.Context, name string) (*corev1.Namespace, error) { + return c.Matcher.GetNamespace(ctx, name) } var _ matching.MatchCriteria = &matchCriteria{} diff --git a/vendor/k8s.io/apiserver/pkg/admission/plugin/policy/matching/matching.go b/vendor/k8s.io/apiserver/pkg/admission/plugin/policy/matching/matching.go index eebe769434..30a6cbebe9 100644 --- a/vendor/k8s.io/apiserver/pkg/admission/plugin/policy/matching/matching.go +++ b/vendor/k8s.io/apiserver/pkg/admission/plugin/policy/matching/matching.go @@ -17,6 +17,7 @@ limitations under the License. package matching import ( + "context" "fmt" v1 "k8s.io/api/admissionregistration/v1" @@ -44,8 +45,8 @@ type Matcher struct { objectMatcher *object.Matcher } -func (m *Matcher) GetNamespace(name string) (*corev1.Namespace, error) { - return m.namespaceMatcher.GetNamespace(name) +func (m *Matcher) GetNamespace(ctx context.Context, name string) (*corev1.Namespace, error) { + return m.namespaceMatcher.GetNamespace(ctx, name) } // NewMatcher initialize the matcher with dependencies requires diff --git a/vendor/k8s.io/apiserver/pkg/admission/plugin/policy/validating/dispatcher.go b/vendor/k8s.io/apiserver/pkg/admission/plugin/policy/validating/dispatcher.go index 8f3e22f64d..0b5474b756 100644 --- a/vendor/k8s.io/apiserver/pkg/admission/plugin/policy/validating/dispatcher.go +++ b/vendor/k8s.io/apiserver/pkg/admission/plugin/policy/validating/dispatcher.go @@ -189,7 +189,7 @@ func (c *dispatcher) Dispatch(ctx context.Context, a admission.Attributes, o adm // if it is cluster scoped, namespaceName will be empty // Otherwise, get the Namespace resource. if namespaceName != "" { - namespace, err = c.matcher.GetNamespace(namespaceName) + namespace, err = c.matcher.GetNamespace(ctx, namespaceName) if err != nil { return err } diff --git a/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/namespace/matcher.go b/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/namespace/matcher.go index 7817cb1772..d73c69bbab 100644 --- a/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/namespace/matcher.go +++ b/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/namespace/matcher.go @@ -44,8 +44,13 @@ type Matcher struct { Client clientset.Interface } -func (m *Matcher) GetNamespace(name string) (*v1.Namespace, error) { - return m.NamespaceLister.Get(name) +func (m *Matcher) GetNamespace(ctx context.Context, name string) (*v1.Namespace, error) { + ns, err := m.NamespaceLister.Get(name) + if apierrors.IsNotFound(err) && len(name) > 0 { + // in case of latency in our caches, make a call direct to storage to verify that it truly exists or not + ns, err = m.Client.CoreV1().Namespaces().Get(ctx, name, metav1.GetOptions{}) + } + return ns, err } // Validate checks if the Matcher has a NamespaceLister and Client. diff --git a/vendor/k8s.io/client-go/tools/cache/controller.go b/vendor/k8s.io/client-go/tools/cache/controller.go index 5f983b6b63..e07c04e625 100644 --- a/vendor/k8s.io/client-go/tools/cache/controller.go +++ b/vendor/k8s.io/client-go/tools/cache/controller.go @@ -596,16 +596,7 @@ func newInformer(clientState Store, options InformerOptions) Controller { // KeyLister, that way resync operations will result in the correct set // of update/delete deltas. - var fifo Queue - if clientgofeaturegate.FeatureGates().Enabled(clientgofeaturegate.InOrderInformers) { - fifo = NewRealFIFO(MetaNamespaceKeyFunc, clientState, options.Transform) - } else { - fifo = NewDeltaFIFOWithOptions(DeltaFIFOOptions{ - KnownObjects: clientState, - EmitDeltaTypeReplaced: true, - Transformer: options.Transform, - }) - } + fifo := newQueueFIFO(clientState, options.Transform) cfg := &Config{ Queue: fifo, @@ -623,3 +614,15 @@ func newInformer(clientState Store, options InformerOptions) Controller { } return New(cfg) } + +func newQueueFIFO(clientState Store, transform TransformFunc) Queue { + if clientgofeaturegate.FeatureGates().Enabled(clientgofeaturegate.InOrderInformers) { + return NewRealFIFO(MetaNamespaceKeyFunc, clientState, transform) + } else { + return NewDeltaFIFOWithOptions(DeltaFIFOOptions{ + KnownObjects: clientState, + EmitDeltaTypeReplaced: true, + Transformer: transform, + }) + } +} diff --git a/vendor/k8s.io/client-go/tools/cache/delta_fifo.go b/vendor/k8s.io/client-go/tools/cache/delta_fifo.go index 9d9e238ccc..a0d7a834aa 100644 --- a/vendor/k8s.io/client-go/tools/cache/delta_fifo.go +++ b/vendor/k8s.io/client-go/tools/cache/delta_fifo.go @@ -270,7 +270,8 @@ func NewDeltaFIFOWithOptions(opts DeltaFIFOOptions) *DeltaFIFO { } var ( - _ = Queue(&DeltaFIFO{}) // DeltaFIFO is a Queue + _ = Queue(&DeltaFIFO{}) // DeltaFIFO is a Queue + _ = TransformingStore(&DeltaFIFO{}) // DeltaFIFO implements TransformingStore to allow memory optimizations ) var ( diff --git a/vendor/k8s.io/client-go/tools/cache/reflector.go b/vendor/k8s.io/client-go/tools/cache/reflector.go index ee9be77278..6fd43375f6 100644 --- a/vendor/k8s.io/client-go/tools/cache/reflector.go +++ b/vendor/k8s.io/client-go/tools/cache/reflector.go @@ -80,7 +80,7 @@ type ReflectorStore interface { // TransformingStore is an optional interface that can be implemented by the provided store. // If implemented on the provided store reflector will use the same transformer in its internal stores. type TransformingStore interface { - Store + ReflectorStore Transformer() TransformFunc } @@ -726,9 +726,11 @@ func (r *Reflector) watchList(ctx context.Context) (watch.Interface, error) { return false } + var transformer TransformFunc storeOpts := []StoreOption{} if tr, ok := r.store.(TransformingStore); ok && tr.Transformer() != nil { - storeOpts = append(storeOpts, WithTransformer(tr.Transformer())) + transformer = tr.Transformer() + storeOpts = append(storeOpts, WithTransformer(transformer)) } initTrace := trace.New("Reflector WatchList", trace.Field{Key: "name", Value: r.name}) @@ -788,7 +790,7 @@ func (r *Reflector) watchList(ctx context.Context) (watch.Interface, error) { // we utilize the temporaryStore to ensure independence from the current store implementation. // as of today, the store is implemented as a queue and will be drained by the higher-level // component as soon as it finishes replacing the content. - checkWatchListDataConsistencyIfRequested(ctx, r.name, resourceVersion, r.listerWatcher.ListWithContext, temporaryStore.List) + checkWatchListDataConsistencyIfRequested(ctx, r.name, resourceVersion, r.listerWatcher.ListWithContext, transformer, temporaryStore.List) if err := r.store.Replace(temporaryStore.List(), resourceVersion); err != nil { return nil, fmt.Errorf("unable to sync watch-list result: %w", err) diff --git a/vendor/k8s.io/client-go/tools/cache/reflector_data_consistency_detector.go b/vendor/k8s.io/client-go/tools/cache/reflector_data_consistency_detector.go index a7e0d9c436..4119c78a6c 100644 --- a/vendor/k8s.io/client-go/tools/cache/reflector_data_consistency_detector.go +++ b/vendor/k8s.io/client-go/tools/cache/reflector_data_consistency_detector.go @@ -33,11 +33,11 @@ import ( // // Note that this function will panic when data inconsistency is detected. // This is intentional because we want to catch it in the CI. -func checkWatchListDataConsistencyIfRequested[T runtime.Object, U any](ctx context.Context, identity string, lastSyncedResourceVersion string, listFn consistencydetector.ListFunc[T], retrieveItemsFn consistencydetector.RetrieveItemsFunc[U]) { +func checkWatchListDataConsistencyIfRequested[T runtime.Object, U any](ctx context.Context, identity string, lastSyncedResourceVersion string, listFn consistencydetector.ListFunc[T], listItemTransformFunc func(interface{}) (interface{}, error), retrieveItemsFn consistencydetector.RetrieveItemsFunc[U]) { if !consistencydetector.IsDataConsistencyDetectionForWatchListEnabled() { return } // for informers we pass an empty ListOptions because // listFn might be wrapped for filtering during informer construction. - consistencydetector.CheckDataConsistency(ctx, identity, lastSyncedResourceVersion, listFn, metav1.ListOptions{}, retrieveItemsFn) + consistencydetector.CheckDataConsistency(ctx, identity, lastSyncedResourceVersion, listFn, listItemTransformFunc, metav1.ListOptions{}, retrieveItemsFn) } diff --git a/vendor/k8s.io/client-go/tools/cache/shared_informer.go b/vendor/k8s.io/client-go/tools/cache/shared_informer.go index 99e5fcd180..1c12aa2d64 100644 --- a/vendor/k8s.io/client-go/tools/cache/shared_informer.go +++ b/vendor/k8s.io/client-go/tools/cache/shared_informer.go @@ -539,16 +539,7 @@ func (s *sharedIndexInformer) RunWithContext(ctx context.Context) { s.startedLock.Lock() defer s.startedLock.Unlock() - var fifo Queue - if clientgofeaturegate.FeatureGates().Enabled(clientgofeaturegate.InOrderInformers) { - fifo = NewRealFIFO(MetaNamespaceKeyFunc, s.indexer, s.transform) - } else { - fifo = NewDeltaFIFOWithOptions(DeltaFIFOOptions{ - KnownObjects: s.indexer, - EmitDeltaTypeReplaced: true, - Transformer: s.transform, - }) - } + fifo := newQueueFIFO(s.indexer, s.transform) cfg := &Config{ Queue: fifo, diff --git a/vendor/k8s.io/client-go/tools/cache/the_real_fifo.go b/vendor/k8s.io/client-go/tools/cache/the_real_fifo.go index ef322bea85..b907410dc0 100644 --- a/vendor/k8s.io/client-go/tools/cache/the_real_fifo.go +++ b/vendor/k8s.io/client-go/tools/cache/the_real_fifo.go @@ -61,7 +61,8 @@ type RealFIFO struct { } var ( - _ = Queue(&RealFIFO{}) // RealFIFO is a Queue + _ = Queue(&RealFIFO{}) // RealFIFO is a Queue + _ = TransformingStore(&RealFIFO{}) // RealFIFO implements TransformingStore to allow memory optimizations ) // Close the queue. diff --git a/vendor/k8s.io/client-go/tools/leaderelection/resourcelock/leaselock.go b/vendor/k8s.io/client-go/tools/leaderelection/resourcelock/leaselock.go index 5d2054155c..79a748b74d 100644 --- a/vendor/k8s.io/client-go/tools/leaderelection/resourcelock/leaselock.go +++ b/vendor/k8s.io/client-go/tools/leaderelection/resourcelock/leaselock.go @@ -77,6 +77,9 @@ func (ll *LeaseLock) Update(ctx context.Context, ler LeaderElectionRecord) error ll.lease.Spec = LeaderElectionRecordToLeaseSpec(&ler) if ll.Labels != nil { + if ll.lease.Labels == nil { + ll.lease.Labels = map[string]string{} + } // Only overwrite the labels that are specifically set for k, v := range ll.Labels { ll.lease.Labels[k] = v diff --git a/vendor/k8s.io/client-go/util/cert/cert.go b/vendor/k8s.io/client-go/util/cert/cert.go index 1220461264..48c78b595e 100644 --- a/vendor/k8s.io/client-go/util/cert/cert.go +++ b/vendor/k8s.io/client-go/util/cert/cert.go @@ -75,13 +75,15 @@ func NewSelfSignedCACert(cfg Config, key crypto.Signer) (*x509.Certificate, erro CommonName: cfg.CommonName, Organization: cfg.Organization, }, - DNSNames: []string{cfg.CommonName}, NotBefore: notBefore, NotAfter: now.Add(duration365d * 10).UTC(), KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, BasicConstraintsValid: true, IsCA: true, } + if len(cfg.CommonName) > 0 { + tmpl.DNSNames = []string{cfg.CommonName} + } certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &tmpl, &tmpl, key.Public(), key) if err != nil { diff --git a/vendor/k8s.io/client-go/util/consistencydetector/data_consistency_detector.go b/vendor/k8s.io/client-go/util/consistencydetector/data_consistency_detector.go index 06f172d82b..72c0124a0f 100644 --- a/vendor/k8s.io/client-go/util/consistencydetector/data_consistency_detector.go +++ b/vendor/k8s.io/client-go/util/consistencydetector/data_consistency_detector.go @@ -45,16 +45,28 @@ func IsDataConsistencyDetectionForWatchListEnabled() bool { return dataConsistencyDetectionForWatchListEnabled } +// SetDataConsistencyDetectionForWatchListEnabledForTest allows to enable/disable data consistency detection for testing purposes. +// It returns a function that restores the original value. +func SetDataConsistencyDetectionForWatchListEnabledForTest(enabled bool) func() { + original := dataConsistencyDetectionForWatchListEnabled + dataConsistencyDetectionForWatchListEnabled = enabled + return func() { + dataConsistencyDetectionForWatchListEnabled = original + } +} + type RetrieveItemsFunc[U any] func() []U type ListFunc[T runtime.Object] func(ctx context.Context, options metav1.ListOptions) (T, error) +type TransformFunc func(interface{}) (interface{}, error) + // CheckDataConsistency exists solely for testing purposes. // we cannot use checkWatchListDataConsistencyIfRequested because // it is guarded by an environmental variable. // we cannot manipulate the environmental variable because // it will affect other tests in this package. -func CheckDataConsistency[T runtime.Object, U any](ctx context.Context, identity string, lastSyncedResourceVersion string, listFn ListFunc[T], listOptions metav1.ListOptions, retrieveItemsFn RetrieveItemsFunc[U]) { +func CheckDataConsistency[T runtime.Object, U any](ctx context.Context, identity string, lastSyncedResourceVersion string, listFn ListFunc[T], listItemTransformFunc TransformFunc, listOptions metav1.ListOptions, retrieveItemsFn RetrieveItemsFunc[U]) { if !canFormAdditionalListCall(lastSyncedResourceVersion, listOptions) { klog.V(4).Infof("data consistency check for %s is enabled but the parameters (RV, ListOptions) doesn't allow for creating a valid LIST request. Skipping the data consistency check.", identity) return @@ -84,6 +96,15 @@ func CheckDataConsistency[T runtime.Object, U any](ctx context.Context, identity if err != nil { panic(err) // this should never happen } + if listItemTransformFunc != nil { + for i := range rawListItems { + obj, err := listItemTransformFunc(rawListItems[i]) + if err != nil { + panic(err) + } + rawListItems[i] = obj.(runtime.Object) + } + } listItems := toMetaObjectSliceOrDie(rawListItems) sort.Sort(byUID(listItems)) diff --git a/vendor/modules.txt b/vendor/modules.txt index 127e337a50..d633d6aaea 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1169,12 +1169,13 @@ github.com/openshift/cluster-api-provider-baremetal/pkg/apis/baremetal/v1alpha1 # github.com/openshift/cluster-autoscaler-operator v0.0.1-0.20250702183526-4eb64d553940 ## explicit; go 1.23.0 github.com/openshift/cluster-autoscaler-operator/pkg/apis/autoscaling/v1 -# github.com/openshift/library-go v0.0.0-20251222131241-289839b3ffe8 +# github.com/openshift/library-go v0.0.0-20251222131241-289839b3ffe8 => github.com/damdo/library-go v0.0.0-20260120133104-12bddc18ba38 ## explicit; go 1.24.0 github.com/openshift/library-go/pkg/apiserver/jsonpatch github.com/openshift/library-go/pkg/certs github.com/openshift/library-go/pkg/config/clusteroperator/v1helpers github.com/openshift/library-go/pkg/controller/factory +github.com/openshift/library-go/pkg/controllerruntime/tls github.com/openshift/library-go/pkg/crypto github.com/openshift/library-go/pkg/operator/certrotation github.com/openshift/library-go/pkg/operator/condition @@ -2052,7 +2053,7 @@ honnef.co/go/tools/stylecheck/st1021 honnef.co/go/tools/stylecheck/st1022 honnef.co/go/tools/stylecheck/st1023 honnef.co/go/tools/unused -# k8s.io/api v0.34.1 +# k8s.io/api v0.34.3 ## explicit; go 1.24.0 k8s.io/api/admission/v1 k8s.io/api/admission/v1beta1 @@ -2114,7 +2115,7 @@ k8s.io/api/storage/v1 k8s.io/api/storage/v1alpha1 k8s.io/api/storage/v1beta1 k8s.io/api/storagemigration/v1alpha1 -# k8s.io/apiextensions-apiserver v0.34.1 +# k8s.io/apiextensions-apiserver v0.34.3 ## explicit; go 1.24.0 k8s.io/apiextensions-apiserver/pkg/apis/apiextensions k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1 @@ -2125,7 +2126,7 @@ k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1 k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1 -# k8s.io/apimachinery v0.34.1 +# k8s.io/apimachinery v0.34.3 ## explicit; go 1.24.0 k8s.io/apimachinery/pkg/api/apitesting k8s.io/apimachinery/pkg/api/apitesting/fuzzer @@ -2196,7 +2197,7 @@ k8s.io/apimachinery/pkg/version k8s.io/apimachinery/pkg/watch k8s.io/apimachinery/third_party/forked/golang/json k8s.io/apimachinery/third_party/forked/golang/reflect -# k8s.io/apiserver v0.34.1 +# k8s.io/apiserver v0.34.3 ## explicit; go 1.24.0 k8s.io/apiserver/pkg/admission k8s.io/apiserver/pkg/admission/initializer @@ -2285,7 +2286,7 @@ k8s.io/apiserver/pkg/warning k8s.io/apiserver/plugin/pkg/authenticator/token/webhook k8s.io/apiserver/plugin/pkg/authorizer/webhook k8s.io/apiserver/plugin/pkg/authorizer/webhook/metrics -# k8s.io/client-go v0.34.1 +# k8s.io/client-go v0.34.3 ## explicit; go 1.24.0 k8s.io/client-go/applyconfigurations k8s.io/client-go/applyconfigurations/admissionregistration/v1 @@ -2636,7 +2637,7 @@ k8s.io/client-go/util/workqueue ## explicit; go 1.24.0 k8s.io/cluster-bootstrap/token/api k8s.io/cluster-bootstrap/token/util -# k8s.io/component-base v0.34.1 +# k8s.io/component-base v0.34.3 ## explicit; go 1.24.0 k8s.io/component-base/cli/flag k8s.io/component-base/compatibility @@ -2796,7 +2797,7 @@ sigs.k8s.io/cluster-api-provider-openstack/version # sigs.k8s.io/cluster-api-provider-vsphere v1.14.0 ## explicit; go 1.24.0 sigs.k8s.io/cluster-api-provider-vsphere/apis/v1beta1 -# sigs.k8s.io/controller-runtime v0.22.4 +# sigs.k8s.io/controller-runtime v0.22.5 ## explicit; go 1.24.0 sigs.k8s.io/controller-runtime sigs.k8s.io/controller-runtime/pkg/builder @@ -3009,3 +3010,4 @@ sigs.k8s.io/yaml/goyaml.v2 # github.com/openshift/cluster-api-actuator-pkg/testutils => github.com/openshift/cluster-api-actuator-pkg/testutils v0.0.0-20251212150432-4a60119d9691 # sigs.k8s.io/cluster-api => sigs.k8s.io/cluster-api v1.11.3 # sigs.k8s.io/cluster-api-provider-azure => github.com/openshift/cluster-api-provider-azure v0.0.0-20251202084521-c2e0e38d1e0e +# github.com/openshift/library-go => github.com/damdo/library-go v0.0.0-20260120133104-12bddc18ba38