diff --git a/go.mod b/go.mod index 418bdf52ef..d4a79be7a3 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/ghodss/yaml v1.0.0 github.com/golang-jwt/jwt/v5 v5.2.1 github.com/google/go-cmp v0.6.0 - github.com/openshift/api v0.0.0-20250305225826-b8da3bfeaf77 + github.com/openshift/api v0.0.0-20250320170726-75d64d71980b github.com/openshift/build-machinery-go v0.0.0-20250102153059-e85a1a7ecb5c github.com/openshift/client-go v0.0.0-20250125113824-8e1f0b8fa9a7 github.com/openshift/library-go v0.0.0-20250319141325-07c53d93ad06 @@ -125,3 +125,5 @@ require ( sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) + +replace github.com/openshift/library-go => github.com/liouk/library-go v0.0.0-20250605114318-fc8d3885f9ce diff --git a/go.sum b/go.sum index 7f08906c68..adca473432 100644 --- a/go.sum +++ b/go.sum @@ -136,6 +136,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= +github.com/liouk/library-go v0.0.0-20250605114318-fc8d3885f9ce h1:+CrkjKjW4ZkdDnB1eohbJF6nfKRYZxfZ1hRWtY6S2mg= +github.com/liouk/library-go v0.0.0-20250605114318-fc8d3885f9ce/go.mod h1:DAa3BGl0CFtkfJn/g5rU8kDDTErfMVA/QlFm4cvU+MI= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -149,14 +151,12 @@ github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= -github.com/openshift/api v0.0.0-20250305225826-b8da3bfeaf77 h1:w6F0sEhlUB1K54Ev4EELsLo5w/xur9pFT19VtemlB4Y= -github.com/openshift/api v0.0.0-20250305225826-b8da3bfeaf77/go.mod h1:yk60tHAmHhtVpJQo3TwVYq2zpuP70iJIFDCmeKMIzPw= +github.com/openshift/api v0.0.0-20250320170726-75d64d71980b h1:GGuFSHESP0BSOu70AqV4u9IVrjYdaeu4Id+HXRIOvkw= +github.com/openshift/api v0.0.0-20250320170726-75d64d71980b/go.mod h1:yk60tHAmHhtVpJQo3TwVYq2zpuP70iJIFDCmeKMIzPw= github.com/openshift/build-machinery-go v0.0.0-20250102153059-e85a1a7ecb5c h1:6XcszPFZpan4qll5XbdLll7n1So3IsPn28aw2j1obMo= github.com/openshift/build-machinery-go v0.0.0-20250102153059-e85a1a7ecb5c/go.mod h1:8jcm8UPtg2mCAsxfqKil1xrmRMI3a+XU2TZ9fF8A7TE= github.com/openshift/client-go v0.0.0-20250125113824-8e1f0b8fa9a7 h1:4iliLcvr1P9EUMZgIaSNEKNQQzBn+L6PSequlFOuB6Q= github.com/openshift/client-go v0.0.0-20250125113824-8e1f0b8fa9a7/go.mod h1:2tcufBE4Cu6RNgDCxcUJepa530kGo5GFVfR9BSnndhI= -github.com/openshift/library-go v0.0.0-20250319141325-07c53d93ad06 h1:vM1BbOl3adyX0LgzNYYndZ+nkerEo7bKEQxxc6zLgNA= -github.com/openshift/library-go v0.0.0-20250319141325-07c53d93ad06/go.mod h1:GHwvopE5KXXCz4ULHp871sTPLLW+FB+hu/RIzlNwxx8= github.com/openshift/multi-operator-manager v0.0.0-20241205181422-20aa3906b99d h1:Rzx23P63JFNNz5D23ubhC0FCN5rK8CeJhKcq5QKcdyU= github.com/openshift/multi-operator-manager v0.0.0-20241205181422-20aa3906b99d/go.mod h1:iVi9Bopa5cLhjG5ie9DoZVVqkH8BGb1FQVTtecOLn4I= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= diff --git a/pkg/controllers/common/external_oidc.go b/pkg/controllers/common/external_oidc.go new file mode 100644 index 0000000000..0e1f3755b7 --- /dev/null +++ b/pkg/controllers/common/external_oidc.go @@ -0,0 +1,69 @@ +package common + +import ( + "fmt" + "strings" + + configv1 "github.com/openshift/api/config/v1" + configv1listers "github.com/openshift/client-go/config/listers/config/v1" + operatorv1listers "github.com/openshift/client-go/operator/listers/operator/v1" + + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/util/sets" + corelistersv1 "k8s.io/client-go/listers/core/v1" +) + +// ExternalOIDCConfigAvailable checks the kubeapiservers/cluster resource for KAS pod +// rollout status; it returns true if auth type is OIDC, all KAS pods are currently on a revision +// that includes the structured auth-config ConfigMap, and the KAS args include the respective +// arg that enables usage of the structured auth-config. It returns false otherwise. +func ExternalOIDCConfigAvailable(authLister configv1listers.AuthenticationLister, kasLister operatorv1listers.KubeAPIServerLister, cmLister corelistersv1.ConfigMapLister) (bool, error) { + auth, err := authLister.Get("cluster") + if err != nil { + return false, err + } + + if auth.Spec.Type != configv1.AuthenticationTypeOIDC { + return false, nil + } + + kas, err := kasLister.Get("cluster") + if err != nil { + return false, err + } + + observedRevisions := sets.New[int32]() + for _, nodeStatus := range kas.Status.NodeStatuses { + observedRevisions.Insert(nodeStatus.CurrentRevision) + } + + if observedRevisions.Len() == 0 { + return false, nil + } + + for _, revision := range observedRevisions.UnsortedList() { + // ensure every observed revision includes an auth-config revisioned configmap + _, err := cmLister.ConfigMaps("openshift-kube-apiserver").Get(fmt.Sprintf("auth-config-%d", revision)) + if errors.IsNotFound(err) { + return false, nil + } else if err != nil { + return false, err + } + + // every observed revision includes a copy of the KAS config configmap + cm, err := cmLister.ConfigMaps("openshift-kube-apiserver").Get(fmt.Sprintf("config-%d", revision)) + if err != nil { + return false, err + } + + // ensure the KAS config of every observed revision contains the appropriate CLI arg for OIDC + // but not the respective ones for OAuth + if !strings.Contains(cm.Data["config.yaml"], `"oauthMetadataFile":""`) || + strings.Contains(cm.Data["config.yaml"], `"authentication-token-webhook-config-file":`) || + !strings.Contains(cm.Data["config.yaml"], `"authentication-config":["/etc/kubernetes/static-pod-resources/configmaps/auth-config/auth-config.json"]`) { + return false, nil + } + } + + return true, nil +} diff --git a/pkg/controllers/common/external_oidc_test.go b/pkg/controllers/common/external_oidc_test.go new file mode 100644 index 0000000000..bd1aa7c105 --- /dev/null +++ b/pkg/controllers/common/external_oidc_test.go @@ -0,0 +1,317 @@ +package common + +import ( + "testing" + + configv1 "github.com/openshift/api/config/v1" + operatorv1 "github.com/openshift/api/operator/v1" + configv1listers "github.com/openshift/client-go/config/listers/config/v1" + operatorv1listers "github.com/openshift/client-go/operator/listers/operator/v1" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + corelistersv1 "k8s.io/client-go/listers/core/v1" + "k8s.io/client-go/tools/cache" +) + +const ( + kasConfigJSONWithOIDC = `"spec":{"apiServerArguments":{"authentication-config":["/etc/kubernetes/static-pod-resources/configmaps/auth-config/auth-config.json"]},"oauthMetadataFile":""}` + kasConfigJSONWithoutOIDC = `"spec":{"apiServerArguments":{"authentication-token-webhook-config-file":["/etc/kubernetes/static-pod-resources/secrets/webhook-authenticator/kubeConfig"]},"oauthMetadataFile":"/etc/kubernetes/static-pod-resources/configmaps/oauth-metadata/oauthMetadata"}` +) + +func TestExternalOIDCConfigAvailable(t *testing.T) { + for _, tt := range []struct { + name string + configMaps []*corev1.ConfigMap + authType configv1.AuthenticationType + nodeStatuses []operatorv1.NodeStatus + expectAvailable bool + expectError bool + }{ + { + name: "no node statuses observed", + authType: configv1.AuthenticationTypeOIDC, + expectAvailable: false, + expectError: false, + }, + { + name: "oidc disabled, no rollout", + configMaps: []*corev1.ConfigMap{cm("config-10", "config.yaml", kasConfigJSONWithoutOIDC)}, + authType: configv1.AuthenticationTypeIntegratedOAuth, + nodeStatuses: []operatorv1.NodeStatus{ + {CurrentRevision: 10}, + {CurrentRevision: 10}, + {CurrentRevision: 10}, + }, + expectAvailable: false, + expectError: false, + }, + { + name: "oidc getting enabled, rollout in progress", + configMaps: []*corev1.ConfigMap{ + cm("config-10", "config.yaml", kasConfigJSONWithoutOIDC), + cm("config-11", "config.yaml", kasConfigJSONWithOIDC), + cm("auth-config-11", "", ""), + }, + authType: configv1.AuthenticationTypeOIDC, + nodeStatuses: []operatorv1.NodeStatus{ + {CurrentRevision: 10, TargetRevision: 11}, + {CurrentRevision: 10}, + {CurrentRevision: 10}, + }, + expectAvailable: false, + expectError: false, + }, + { + name: "oidc getting enabled, rollout in progress, one node ready", + configMaps: []*corev1.ConfigMap{ + cm("config-10", "config.yaml", kasConfigJSONWithoutOIDC), + cm("config-11", "config.yaml", kasConfigJSONWithOIDC), + cm("auth-config-11", "", ""), + }, + authType: configv1.AuthenticationTypeOIDC, + nodeStatuses: []operatorv1.NodeStatus{ + {CurrentRevision: 11}, + {CurrentRevision: 10, TargetRevision: 11}, + {CurrentRevision: 10}, + }, + expectAvailable: false, + expectError: false, + }, + { + name: "oidc getting enabled, rollout in progress, two nodes ready", + configMaps: []*corev1.ConfigMap{ + cm("config-10", "config.yaml", kasConfigJSONWithoutOIDC), + cm("config-11", "config.yaml", kasConfigJSONWithOIDC), + cm("auth-config-11", "", ""), + }, + authType: configv1.AuthenticationTypeOIDC, + nodeStatuses: []operatorv1.NodeStatus{ + {CurrentRevision: 11}, + {CurrentRevision: 11}, + {CurrentRevision: 10, TargetRevision: 11}, + }, + expectAvailable: false, + expectError: false, + }, + { + name: "oidc got enabled", + configMaps: []*corev1.ConfigMap{ + cm("config-11", "config.yaml", kasConfigJSONWithOIDC), + cm("auth-config-11", "", ""), + }, + authType: configv1.AuthenticationTypeOIDC, + nodeStatuses: []operatorv1.NodeStatus{ + {CurrentRevision: 11}, + {CurrentRevision: 11}, + {CurrentRevision: 11}, + }, + expectAvailable: true, + expectError: false, + }, + { + name: "oidc enabled, rollout in progress", + configMaps: []*corev1.ConfigMap{ + cm("config-11", "config.yaml", kasConfigJSONWithOIDC), + cm("config-12", "config.yaml", kasConfigJSONWithOIDC), + cm("auth-config-11", "", ""), + cm("auth-config-12", "", ""), + }, + authType: configv1.AuthenticationTypeOIDC, + nodeStatuses: []operatorv1.NodeStatus{ + {CurrentRevision: 11, TargetRevision: 12}, + {CurrentRevision: 11}, + {CurrentRevision: 11}, + }, + expectAvailable: true, + expectError: false, + }, + { + name: "oidc enabled, rollout in progress, one node ready", + configMaps: []*corev1.ConfigMap{ + cm("config-11", "config.yaml", kasConfigJSONWithOIDC), + cm("config-12", "config.yaml", kasConfigJSONWithOIDC), + cm("auth-config-11", "", ""), + cm("auth-config-12", "", ""), + }, + authType: configv1.AuthenticationTypeOIDC, + nodeStatuses: []operatorv1.NodeStatus{ + {CurrentRevision: 12}, + {CurrentRevision: 11, TargetRevision: 12}, + {CurrentRevision: 11}, + }, + expectAvailable: true, + expectError: false, + }, + { + name: "oidc enabled, rollout in progress, two nodes ready", + configMaps: []*corev1.ConfigMap{ + cm("config-11", "config.yaml", kasConfigJSONWithOIDC), + cm("config-12", "config.yaml", kasConfigJSONWithOIDC), + cm("auth-config-11", "", ""), + cm("auth-config-12", "", ""), + }, + authType: configv1.AuthenticationTypeOIDC, + nodeStatuses: []operatorv1.NodeStatus{ + {CurrentRevision: 12}, + {CurrentRevision: 12}, + {CurrentRevision: 11, TargetRevision: 12}, + }, + expectAvailable: true, + expectError: false, + }, + { + name: "oidc still enabled", + configMaps: []*corev1.ConfigMap{ + cm("config-11", "config.yaml", kasConfigJSONWithOIDC), + cm("config-12", "config.yaml", kasConfigJSONWithOIDC), + cm("auth-config-11", "", ""), + cm("auth-config-12", "", ""), + }, + authType: configv1.AuthenticationTypeOIDC, + nodeStatuses: []operatorv1.NodeStatus{ + {CurrentRevision: 12}, + {CurrentRevision: 12}, + {CurrentRevision: 12}, + }, + expectAvailable: true, + expectError: false, + }, + { + name: "oidc getting disabled, rollout in progress", + configMaps: []*corev1.ConfigMap{ + cm("config-11", "config.yaml", kasConfigJSONWithOIDC), + cm("config-12", "config.yaml", kasConfigJSONWithOIDC), + cm("config-13", "config.yaml", kasConfigJSONWithoutOIDC), + cm("auth-config-11", "", ""), + cm("auth-config-12", "", ""), + }, + authType: configv1.AuthenticationTypeIntegratedOAuth, + nodeStatuses: []operatorv1.NodeStatus{ + {CurrentRevision: 12, TargetRevision: 13}, + {CurrentRevision: 12}, + {CurrentRevision: 12}, + }, + expectAvailable: false, + expectError: false, + }, + { + name: "oidc getting disabled, rollout in progress, one node ready", + configMaps: []*corev1.ConfigMap{ + cm("config-11", "config.yaml", kasConfigJSONWithOIDC), + cm("config-12", "config.yaml", kasConfigJSONWithOIDC), + cm("config-13", "config.yaml", kasConfigJSONWithoutOIDC), + cm("auth-config-11", "", ""), + cm("auth-config-12", "", ""), + }, + authType: configv1.AuthenticationTypeIntegratedOAuth, + nodeStatuses: []operatorv1.NodeStatus{ + {CurrentRevision: 13}, + {CurrentRevision: 12, TargetRevision: 13}, + {CurrentRevision: 12}, + }, + expectAvailable: false, + expectError: false, + }, + { + name: "oidc getting disabled, rollout in progress, two nodes ready", + authType: configv1.AuthenticationTypeIntegratedOAuth, + configMaps: []*corev1.ConfigMap{ + cm("config-11", "config.yaml", kasConfigJSONWithOIDC), + cm("config-11", "config.yaml", kasConfigJSONWithOIDC), + cm("config-13", "config.yaml", kasConfigJSONWithoutOIDC), + cm("auth-config-11", "", ""), + cm("auth-config-12", "", ""), + }, + nodeStatuses: []operatorv1.NodeStatus{ + {CurrentRevision: 13}, + {CurrentRevision: 13}, + {CurrentRevision: 12, TargetRevision: 13}, + }, + expectAvailable: false, + expectError: false, + }, + { + name: "oidc got disabled", + configMaps: []*corev1.ConfigMap{ + cm("config-11", "config.yaml", kasConfigJSONWithOIDC), + cm("config-12", "config.yaml", kasConfigJSONWithOIDC), + cm("config-13", "config.yaml", kasConfigJSONWithoutOIDC), + cm("auth-config-11", "", ""), + cm("auth-config-12", "", ""), + }, + authType: configv1.AuthenticationTypeIntegratedOAuth, + nodeStatuses: []operatorv1.NodeStatus{ + {CurrentRevision: 13}, + {CurrentRevision: 13}, + {CurrentRevision: 13}, + }, + expectAvailable: false, + expectError: false, + }, + } { + t.Run(tt.name, func(t *testing.T) { + + cmIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{}) + for _, cm := range tt.configMaps { + cmIndexer.Add(cm) + } + + kasIndexer := cache.NewIndexer(func(obj interface{}) (string, error) { + return "cluster", nil + }, cache.Indexers{}) + + kasIndexer.Add(&operatorv1.KubeAPIServer{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + Status: operatorv1.KubeAPIServerStatus{ + StaticPodOperatorStatus: operatorv1.StaticPodOperatorStatus{ + NodeStatuses: tt.nodeStatuses, + }, + }, + }) + + authIndexer := cache.NewIndexer(func(obj interface{}) (string, error) { + return "cluster", nil + }, cache.Indexers{}) + + authIndexer.Add(&configv1.Authentication{ + Spec: configv1.AuthenticationSpec{ + Type: tt.authType, + }, + }) + + available, err := ExternalOIDCConfigAvailable( + configv1listers.NewAuthenticationLister(authIndexer), + operatorv1listers.NewKubeAPIServerLister(kasIndexer), + corelistersv1.NewConfigMapLister(cmIndexer), + ) + + if tt.expectError != (err != nil) { + t.Fatalf("expected error %v; got %v", tt.expectError, err) + } + + if tt.expectAvailable != available { + t.Fatalf("expected available %v; got %v", tt.expectAvailable, available) + } + }) + } +} + +func cm(name, dataKey, dataValue string) *corev1.ConfigMap { + cm := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: "openshift-kube-apiserver", + }, + } + + if len(dataKey) > 0 { + cm.Data = map[string]string{ + dataKey: dataValue, + } + } + + return cm +} diff --git a/pkg/controllers/customroute/custom_route_controller.go b/pkg/controllers/customroute/custom_route_controller.go index b9271b4e61..1d935aff0c 100644 --- a/pkg/controllers/customroute/custom_route_controller.go +++ b/pkg/controllers/customroute/custom_route_controller.go @@ -12,6 +12,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/informers" corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/klog/v2" @@ -19,8 +20,11 @@ import ( routev1 "github.com/openshift/api/route/v1" applyconfigv1 "github.com/openshift/client-go/config/applyconfigurations/config/v1" configsetterv1 "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1" - configinformers "github.com/openshift/client-go/config/informers/externalversions/config/v1" + configinformers "github.com/openshift/client-go/config/informers/externalversions" + configinformersv1 "github.com/openshift/client-go/config/informers/externalversions/config/v1" configlistersv1 "github.com/openshift/client-go/config/listers/config/v1" + operatorv1informers "github.com/openshift/client-go/operator/informers/externalversions/operator/v1" + operatorv1listers "github.com/openshift/client-go/operator/listers/operator/v1" routeclient "github.com/openshift/client-go/route/clientset/versioned/typed/route/v1" routeinformer "github.com/openshift/client-go/route/informers/externalversions/route/v1" routev1lister "github.com/openshift/client-go/route/listers/route/v1" @@ -51,6 +55,10 @@ type customRouteController struct { secretLister corev1listers.SecretLister resourceSyncer resourcesynccontroller.ResourceSyncer operatorClient v1helpers.OperatorClient + + authLister configlistersv1.AuthenticationLister + kasLister operatorv1listers.KubeAPIServerLister + kasConfigMapLister corev1listers.ConfigMapLister } func NewCustomRouteController( @@ -58,11 +66,14 @@ func NewCustomRouteController( componentRouteName string, destSecretNamespace string, destSecretName string, - ingressInformer configinformers.IngressInformer, + ingressInformer configinformersv1.IngressInformer, ingressClient configsetterv1.IngressInterface, routeInformer routeinformer.RouteInformer, routeClient routeclient.RouteInterface, kubeInformersForNamespaces v1helpers.KubeInformersForNamespaces, + operatorConfigInformers configinformers.SharedInformerFactory, + kasInformer operatorv1informers.KubeAPIServerInformer, + kasConfigMapInformer informers.SharedInformerFactory, operatorClient v1helpers.OperatorClient, eventRecorder events.Recorder, resourceSyncer resourcesynccontroller.ResourceSyncer, @@ -83,6 +94,10 @@ func NewCustomRouteController( secretLister: kubeInformersForNamespaces.SecretLister(), operatorClient: operatorClient, resourceSyncer: resourceSyncer, + + authLister: operatorConfigInformers.Config().V1().Authentications().Lister(), + kasLister: kasInformer.Lister(), + kasConfigMapLister: kasConfigMapInformer.Core().V1().ConfigMaps().Lister(), } return factory.New(). @@ -91,6 +106,8 @@ func NewCustomRouteController( routeInformer.Informer(), kubeInformersForNamespaces.InformersFor("openshift-config").Core().V1().Secrets().Informer(), kubeInformersForNamespaces.InformersFor("openshift-authentication").Core().V1().Secrets().Informer(), + operatorConfigInformers.Config().V1().Authentications().Informer(), + kasInformer.Informer(), ). WithSyncDegradedOnError(operatorClient). WithSync(controller.sync). @@ -106,6 +123,12 @@ func (c *customRouteController) sync(ctx context.Context, syncCtx factory.SyncCo ingressConfigCopy := ingressConfig.DeepCopy() + if oidcAvailable, err := common.ExternalOIDCConfigAvailable(c.authLister, c.kasLister, c.kasConfigMapLister); err != nil { + return err + } else if oidcAvailable { + return c.removeOperands(ctx, ingressConfigCopy) + } + // configure the expected route expectedRoute, secretName, errors := c.getOAuthRouteAndSecretName(ingressConfigCopy) if errors != nil { @@ -289,3 +312,46 @@ func (c *customRouteController) getFieldManager() string { // TODO find a way to get the client name and combine it with the controller name automatically return "AuthenticationCustomRouteController" } + +func (c *customRouteController) removeOperands(ctx context.Context, ingressConfig *configv1.Ingress) error { + if _, err := c.routeLister.Routes(c.componentRoute.Namespace).Get(c.componentRoute.Name); err != nil && !errors.IsNotFound(err) { + return err + } else if !errors.IsNotFound(err) { + if err := c.routeClient.Delete(ctx, c.componentRoute.Name, metav1.DeleteOptions{}); err != nil && !errors.IsNotFound(err) { + return err + } + } + + ingressStatus, err := applyconfigv1.ExtractIngressStatus(ingressConfig, c.getFieldManager()) + if err != nil { + return err + } + + if ingressStatus != nil && ingressStatus.Status != nil { + componentRoutes := make([]applyconfigv1.ComponentRouteStatusApplyConfiguration, 0) + routeFound := false + for _, cr := range ingressStatus.Status.ComponentRoutes { + if *cr.Name == c.componentRoute.Name && *cr.Namespace == c.componentRoute.Namespace { + routeFound = true + continue + } + + componentRoutes = append(componentRoutes, cr) + } + + if routeFound { + ingressStatus.Status.ComponentRoutes = componentRoutes + ingress := applyconfigv1.Ingress(ingressConfig.Name).WithStatus(ingressStatus.Status) + if _, err := c.ingressClient.ApplyStatus(ctx, ingress, c.forceApply()); err != nil { + return err + } + } + } + + // delete secret by syncing an empty source + if err := c.syncSecret(""); err != nil { + return err + } + + return nil +} diff --git a/pkg/controllers/deployment/deployment_controller.go b/pkg/controllers/deployment/deployment_controller.go index 4889a0a932..b0297fc740 100644 --- a/pkg/controllers/deployment/deployment_controller.go +++ b/pkg/controllers/deployment/deployment_controller.go @@ -9,6 +9,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/informers" coreinformers "k8s.io/client-go/informers/core/v1" @@ -21,8 +22,11 @@ import ( configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1" configinformer "github.com/openshift/client-go/config/informers/externalversions" configv1listers "github.com/openshift/client-go/config/listers/config/v1" + operatorv1informers "github.com/openshift/client-go/operator/informers/externalversions/operator/v1" + operatorv1listers "github.com/openshift/client-go/operator/listers/operator/v1" routeinformers "github.com/openshift/client-go/route/informers/externalversions" routev1listers "github.com/openshift/client-go/route/listers/route/v1" + "github.com/openshift/cluster-authentication-operator/pkg/controllers/common" bootstrap "github.com/openshift/library-go/pkg/authentication/bootstrapauthenticator" "github.com/openshift/library-go/pkg/controller/factory" "github.com/openshift/library-go/pkg/operator/apiserver/controller/workload" @@ -60,6 +64,10 @@ type oauthServerDeploymentSyncer struct { proxyLister configv1listers.ProxyLister routeLister routev1listers.RouteLister + authLister configv1listers.AuthenticationLister + kasLister operatorv1listers.KubeAPIServerLister + kasConfigMapLister corev1listers.ConfigMapLister + bootstrapUserDataGetter bootstrap.BootstrapUserDataGetter bootstrapUserChangeRollOut bool } @@ -77,6 +85,8 @@ func NewOAuthServerWorkloadController( eventsRecorder events.Recorder, versionRecorder status.VersionGetter, kubeInformersForTargetNamespace informers.SharedInformerFactory, + kasInformer operatorv1informers.KubeAPIServerInformer, + kasConfigMapInformer informers.SharedInformerFactory, ) factory.Controller { targetNS := "openshift-authentication" @@ -94,6 +104,10 @@ func NewOAuthServerWorkloadController( proxyLister: configInformers.Config().V1().Proxies().Lister(), routeLister: routeInformersForTargetNamespace.Route().V1().Routes().Lister(), + authLister: configInformers.Config().V1().Authentications().Lister(), + kasLister: kasInformer.Lister(), + kasConfigMapLister: kasConfigMapInformer.Core().V1().ConfigMaps().Lister(), + bootstrapUserDataGetter: bootstrapUserDataGetter, } @@ -117,7 +131,9 @@ func NewOAuthServerWorkloadController( []factory.Informer{ configInformers.Config().V1().Ingresses().Informer(), configInformers.Config().V1().Proxies().Informer(), + configInformers.Config().V1().Authentications().Informer(), nodeInformer.Informer(), + kasInformer.Informer(), }, []factory.Informer{ kubeInformersForTargetNamespace.Apps().V1().Deployments().Informer(), @@ -135,6 +151,13 @@ func NewOAuthServerWorkloadController( } func (c *oauthServerDeploymentSyncer) PreconditionFulfilled(_ context.Context) (bool, error) { + if oidcAvailable, err := common.ExternalOIDCConfigAvailable(c.authLister, c.kasLister, c.kasConfigMapLister); err != nil { + return false, err + } else if oidcAvailable { + // the route is no longer a pre-requisite + return true, nil + } + route, err := c.routeLister.Routes("openshift-authentication").Get("oauth-openshift") if err != nil { return false, fmt.Errorf("waiting for the oauth-openshift route to appear: %w", err) @@ -147,17 +170,17 @@ func (c *oauthServerDeploymentSyncer) PreconditionFulfilled(_ context.Context) ( return true, nil } -func (c *oauthServerDeploymentSyncer) Sync(ctx context.Context, syncContext factory.SyncContext) (*appsv1.Deployment, bool, []error) { +func (c *oauthServerDeploymentSyncer) Sync(ctx context.Context, syncContext factory.SyncContext) (*appsv1.Deployment, bool, bool, string, string, []error) { errs := []error{} operatorSpec, operatorStatus, _, err := c.operatorClient.GetOperatorState() if err != nil { - return nil, false, append(errs, err) + return nil, false, false, "", "", append(errs, err) } proxyConfig, err := c.getProxyConfig() if err != nil { - return nil, false, append(errs, err) + return nil, false, false, "", "", append(errs, err) } // resourceVersions serves to store versions of config resources so that we @@ -174,7 +197,7 @@ func (c *oauthServerDeploymentSyncer) Sync(ctx context.Context, syncContext fact configResourceVersions, err := c.getConfigResourceVersions() if err != nil { - return nil, false, append(errs, err) + return nil, false, false, "", "", append(errs, err) } resourceVersions = append(resourceVersions, configResourceVersions...) @@ -192,7 +215,16 @@ func (c *oauthServerDeploymentSyncer) Sync(ctx context.Context, syncContext fact // deployment, have RV of all resources expectedDeployment, err := getOAuthServerDeployment(operatorSpec, proxyConfig, c.bootstrapUserChangeRollOut, resourceVersions...) if err != nil { - return nil, false, append(errs, err) + return nil, false, false, "", "", append(errs, err) + } + + if oidcAvailable, err := common.ExternalOIDCConfigAvailable(c.authLister, c.kasLister, c.kasConfigMapLister); err != nil { + return nil, false, false, "", "", append(errs, fmt.Errorf("error while checking OIDC config: %v", err)) + } else if oidcAvailable { + if err := c.deployments.Deployments(expectedDeployment.Namespace).Delete(ctx, expectedDeployment.Name, metav1.DeleteOptions{}); err != nil && !errors.IsNotFound(err) { + return nil, false, false, "", "", append(errs, fmt.Errorf("error while gracefully deleting deployment: %v", err)) + } + return nil, false, true, expectedDeployment.Name, expectedDeployment.Namespace, nil } if _, err := c.secretLister.Secrets("openshift-authentication").Get("v4-0-config-system-custom-router-certs"); err == nil { @@ -213,13 +245,13 @@ func (c *oauthServerDeploymentSyncer) Sync(ctx context.Context, syncContext fact err = c.ensureAtMostOnePodPerNode(&expectedDeployment.Spec, "oauth-openshift") if err != nil { - return nil, false, append(errs, fmt.Errorf("unable to ensure at most one pod per node: %v", err)) + return nil, false, false, "", "", append(errs, fmt.Errorf("unable to ensure at most one pod per node: %v", err)) } // Set the replica count to the number of master nodes. masterNodeCount, err := c.countNodes(expectedDeployment.Spec.Template.Spec.NodeSelector) if err != nil { - return nil, false, append(errs, fmt.Errorf("failed to determine number of master nodes: %v", err)) + return nil, false, false, "", "", append(errs, fmt.Errorf("failed to determine number of master nodes: %v", err)) } expectedDeployment.Spec.Replicas = masterNodeCount @@ -229,10 +261,10 @@ func (c *oauthServerDeploymentSyncer) Sync(ctx context.Context, syncContext fact resourcemerge.ExpectedDeploymentGeneration(expectedDeployment, operatorStatus.Generations), ) if err != nil { - return nil, false, append(errs, fmt.Errorf("applying deployment of the integrated OAuth server failed: %w", err)) + return nil, false, false, "", "", append(errs, fmt.Errorf("applying deployment of the integrated OAuth server failed: %w", err)) } - return deployment, true, errs + return deployment, true, false, "", "", errs } func (c *oauthServerDeploymentSyncer) getProxyConfig() (*configv1.Proxy, error) { diff --git a/pkg/controllers/ingressnodesavailable/ingress_nodes_available_controller.go b/pkg/controllers/ingressnodesavailable/ingress_nodes_available_controller.go index f8c15fba90..c8e1150c63 100644 --- a/pkg/controllers/ingressnodesavailable/ingress_nodes_available_controller.go +++ b/pkg/controllers/ingressnodesavailable/ingress_nodes_available_controller.go @@ -6,18 +6,22 @@ import ( "time" operatorv1 "github.com/openshift/api/operator/v1" + configinformer "github.com/openshift/client-go/config/informers/externalversions" + configv1listers "github.com/openshift/client-go/config/listers/config/v1" operatorv1informers "github.com/openshift/client-go/operator/informers/externalversions/operator/v1" operatorv1listers "github.com/openshift/client-go/operator/listers/operator/v1" "github.com/openshift/cluster-authentication-operator/pkg/controllers/common" "github.com/openshift/library-go/pkg/controller/factory" "github.com/openshift/library-go/pkg/operator/events" "github.com/openshift/library-go/pkg/operator/v1helpers" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/informers" corev1informers "k8s.io/client-go/informers/core/v1" corev1listers "k8s.io/client-go/listers/core/v1" ) @@ -32,6 +36,10 @@ type ingressNodesAvailableController struct { operatorClient v1helpers.OperatorClient ingressLister operatorv1listers.IngressControllerLister nodeLister corev1listers.NodeLister + + authLister configv1listers.AuthenticationLister + kasLister operatorv1listers.KubeAPIServerLister + kasConfigMapLister corev1listers.ConfigMapLister } func NewIngressNodesAvailableController( @@ -40,12 +48,19 @@ func NewIngressNodesAvailableController( ingressControllerInformer operatorv1informers.IngressControllerInformer, eventRecorder events.Recorder, nodeInformer corev1informers.NodeInformer, + configInformers configinformer.SharedInformerFactory, + kasInformer operatorv1informers.KubeAPIServerInformer, + kasConfigMapInformer informers.SharedInformerFactory, ) factory.Controller { controller := &ingressNodesAvailableController{ controllerInstanceName: factory.ControllerInstanceName(instanceName, "IngressNodesAvailable"), operatorClient: operatorClient, ingressLister: ingressControllerInformer.Lister(), nodeLister: nodeInformer.Lister(), + + authLister: configInformers.Config().V1().Authentications().Lister(), + kasLister: kasInformer.Lister(), + kasConfigMapLister: kasConfigMapInformer.Core().V1().ConfigMaps().Lister(), } return factory.New(). @@ -72,6 +87,12 @@ func countReadyWorkerNodes(nodes []*corev1.Node) int { } func (c *ingressNodesAvailableController) sync(ctx context.Context, syncCtx factory.SyncContext) error { + if oidcAvailable, err := common.ExternalOIDCConfigAvailable(c.authLister, c.kasLister, c.kasConfigMapLister); err != nil { + return err + } else if oidcAvailable { + return common.ApplyControllerConditions(ctx, c.operatorClient, c.controllerInstanceName, knownConditionNames, nil) + } + foundConditions := []operatorv1.OperatorCondition{} workers, err := c.nodeLister.List(labels.SelectorFromSet(labels.Set{"node-role.kubernetes.io/worker": ""})) diff --git a/pkg/controllers/ingressstate/ingress_state_controller.go b/pkg/controllers/ingressstate/ingress_state_controller.go index 190b3bc28c..4f192fabcb 100644 --- a/pkg/controllers/ingressstate/ingress_state_controller.go +++ b/pkg/controllers/ingressstate/ingress_state_controller.go @@ -7,9 +7,15 @@ import ( "time" operatorv1 "github.com/openshift/api/operator/v1" + configinformer "github.com/openshift/client-go/config/informers/externalversions" + configv1listers "github.com/openshift/client-go/config/listers/config/v1" + operatorv1informers "github.com/openshift/client-go/operator/informers/externalversions/operator/v1" + operatorv1listers "github.com/openshift/client-go/operator/listers/operator/v1" + "github.com/openshift/cluster-authentication-operator/pkg/controllers/common" "github.com/openshift/library-go/pkg/controller/factory" "github.com/openshift/library-go/pkg/operator/events" "github.com/openshift/library-go/pkg/operator/v1helpers" + corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -17,8 +23,7 @@ import ( "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/informers" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" - - "github.com/openshift/cluster-authentication-operator/pkg/controllers/common" + corev1listers "k8s.io/client-go/listers/core/v1" ) const ( @@ -37,11 +42,18 @@ type ingressStateController struct { podsGetter corev1client.PodsGetter targetNamespace string operatorClient v1helpers.OperatorClient + + authLister configv1listers.AuthenticationLister + kasLister operatorv1listers.KubeAPIServerLister + kasConfigMapLister corev1listers.ConfigMapLister } func NewIngressStateController( instanceName string, kubeInformersForTargetNamespace informers.SharedInformerFactory, + configInformers configinformer.SharedInformerFactory, + kasInformer operatorv1informers.KubeAPIServerInformer, + kasConfigMapInformer informers.SharedInformerFactory, endpointsGetter corev1client.EndpointsGetter, podsGetter corev1client.PodsGetter, operatorClient v1helpers.OperatorClient, @@ -54,6 +66,10 @@ func NewIngressStateController( podsGetter: podsGetter, targetNamespace: targetNamespace, operatorClient: operatorClient, + + authLister: configInformers.Config().V1().Authentications().Lister(), + kasLister: kasInformer.Lister(), + kasConfigMapLister: kasConfigMapInformer.Core().V1().ConfigMaps().Lister(), } return factory.New(). @@ -76,6 +92,13 @@ func (c *ingressStateController) checkPodStatus(ctx context.Context, reference * } func (c *ingressStateController) sync(ctx context.Context, syncCtx factory.SyncContext) error { + if oidcAvailable, err := common.ExternalOIDCConfigAvailable(c.authLister, c.kasLister, c.kasConfigMapLister); err != nil { + return err + } else if oidcAvailable { + // clear all operator conditions + return common.ApplyControllerConditions(ctx, c.operatorClient, c.controllerInstanceName, degradedConditionTypes, nil) + } + endpoints, err := c.endpointsGetter.Endpoints(c.targetNamespace).Get(context.TODO(), "oauth-openshift", metav1.GetOptions{}) if apierrors.IsNotFound(err) { // Clear the error to allow checkSubset to report degraded because endpoints == nil diff --git a/pkg/controllers/metadata/metadata_controller.go b/pkg/controllers/metadata/metadata_controller.go index 267c2ea9ab..d3d3d30819 100644 --- a/pkg/controllers/metadata/metadata_controller.go +++ b/pkg/controllers/metadata/metadata_controller.go @@ -7,6 +7,7 @@ import ( "time" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/wait" @@ -19,6 +20,8 @@ import ( configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1" configinformers "github.com/openshift/client-go/config/informers/externalversions" configv1listers "github.com/openshift/client-go/config/listers/config/v1" + operatorv1informers "github.com/openshift/client-go/operator/informers/externalversions/operator/v1" + operatorv1listers "github.com/openshift/client-go/operator/listers/operator/v1" routeclient "github.com/openshift/client-go/route/clientset/versioned/typed/route/v1" routeinformer "github.com/openshift/client-go/route/informers/externalversions" "github.com/openshift/library-go/pkg/controller/factory" @@ -43,36 +46,66 @@ type metadataController struct { ingressLister configv1listers.IngressLister route routeclient.RouteInterface secretLister corev1listers.SecretLister + configMapLister corev1listers.ConfigMapLister configMaps corev1client.ConfigMapsGetter authentication configv1client.AuthenticationInterface operatorClient v1helpers.OperatorClient + + authLister configv1listers.AuthenticationLister + kasLister operatorv1listers.KubeAPIServerLister + kasConfigMapLister corev1listers.ConfigMapLister } // NewMetadataController assure that ingress configuration is available to determine the domain suffix that this controller use to create // a route for oauth. The controller then update the oauth metadata config map and update the cluster authentication config. // The controller use degraded condition if any part of the process fail and use the "AuthMetadataProgressing=false" condition when the controller job is done // and all resources exists. -func NewMetadataController(instanceName string, kubeInformersForTargetNamespace informers.SharedInformerFactory, configInformer configinformers.SharedInformerFactory, routeInformer routeinformer.SharedInformerFactory, - configMaps corev1client.ConfigMapsGetter, route routeclient.RouteInterface, authentication configv1client.AuthenticationInterface, operatorClient v1helpers.OperatorClient, - recorder events.Recorder) factory.Controller { +func NewMetadataController(instanceName string, + kubeInformersForTargetNamespace informers.SharedInformerFactory, + configInformer configinformers.SharedInformerFactory, + routeInformer routeinformer.SharedInformerFactory, + kasInformer operatorv1informers.KubeAPIServerInformer, + kasConfigMapInformer informers.SharedInformerFactory, + configMaps corev1client.ConfigMapsGetter, + route routeclient.RouteInterface, + authentication configv1client.AuthenticationInterface, + operatorClient v1helpers.OperatorClient, + recorder events.Recorder, +) factory.Controller { c := &metadataController{ controllerInstanceName: factory.ControllerInstanceName(instanceName, "Metadata"), ingressLister: configInformer.Config().V1().Ingresses().Lister(), secretLister: kubeInformersForTargetNamespace.Core().V1().Secrets().Lister(), + configMapLister: kubeInformersForTargetNamespace.Core().V1().ConfigMaps().Lister(), configMaps: configMaps, route: route, authentication: authentication, operatorClient: operatorClient, + + authLister: configInformer.Config().V1().Authentications().Lister(), + kasLister: kasInformer.Lister(), + kasConfigMapLister: kasConfigMapInformer.Core().V1().ConfigMaps().Lister(), } return factory.New().WithInformers( kubeInformersForTargetNamespace.Core().V1().Secrets().Informer(), configInformer.Config().V1().Authentications().Informer(), configInformer.Config().V1().Ingresses().Informer(), routeInformer.Route().V1().Routes().Informer(), + kasInformer.Informer(), ).ResyncEvery(wait.Jitter(time.Minute, 1.0)).WithSync(c.sync).ToController(c.controllerInstanceName, recorder.WithComponentSuffix("metadata-controller")) } func (c *metadataController) sync(ctx context.Context, syncCtx factory.SyncContext) error { + if oidcAvailable, err := common.ExternalOIDCConfigAvailable(c.authLister, c.kasLister, c.kasConfigMapLister); err != nil { + return err + } else if oidcAvailable { + if err := c.removeOperands(ctx); err != nil { + return err + } + + return common.ApplyControllerConditions(ctx, c.operatorClient, c.controllerInstanceName, knownConditionNames, nil) + } + foundConditions := []operatorv1.OperatorCondition{} foundConditions = append(foundConditions, c.handleOAuthMetadataConfigMap(ctx, syncCtx.Recorder())...) @@ -156,6 +189,21 @@ func (c *metadataController) handleAuthConfig(ctx context.Context) []operatorv1. return nil } +func (c *metadataController) removeOperands(ctx context.Context) error { + if _, err := c.configMapLister.ConfigMaps("openshift-authentication").Get("v4-0-config-system-metadata"); errors.IsNotFound(err) { + return nil + } else if err != nil { + return err + } + + err := c.configMaps.ConfigMaps("openshift-authentication").Delete(ctx, "v4-0-config-system-metadata", metav1.DeleteOptions{}) + if err != nil && !errors.IsNotFound(err) { + return err + } + + return nil +} + func getOAuthMetadata(host string) string { stubMetadata := ` { diff --git a/pkg/controllers/oauthclientscontroller/oauthclientscontroller.go b/pkg/controllers/oauthclientscontroller/oauthclientscontroller.go index df080ea9a9..62acdfd7c3 100644 --- a/pkg/controllers/oauthclientscontroller/oauthclientscontroller.go +++ b/pkg/controllers/oauthclientscontroller/oauthclientscontroller.go @@ -8,9 +8,13 @@ import ( "time" "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/informers" + corev1listers "k8s.io/client-go/listers/core/v1" + "k8s.io/client-go/tools/cache" "k8s.io/client-go/util/retry" configv1 "github.com/openshift/api/config/v1" @@ -18,8 +22,9 @@ import ( configinformers "github.com/openshift/client-go/config/informers/externalversions" configv1listers "github.com/openshift/client-go/config/listers/config/v1" oauthclient "github.com/openshift/client-go/oauth/clientset/versioned/typed/oauth/v1" - oauthinformers "github.com/openshift/client-go/oauth/informers/externalversions" oauthv1listers "github.com/openshift/client-go/oauth/listers/oauth/v1" + operatorv1informers "github.com/openshift/client-go/operator/informers/externalversions/operator/v1" + operatorv1listers "github.com/openshift/client-go/operator/listers/operator/v1" routeinformers "github.com/openshift/client-go/route/informers/externalversions" routev1listers "github.com/openshift/client-go/route/listers/route/v1" "github.com/openshift/library-go/pkg/controller/factory" @@ -30,30 +35,43 @@ import ( "github.com/openshift/cluster-authentication-operator/pkg/controllers/common" "github.com/openshift/cluster-authentication-operator/pkg/controllers/customroute" + "github.com/openshift/cluster-authentication-operator/pkg/controllers/oauthclientsswitchedinformer" ) type oauthsClientsController struct { oauthClientClient oauthclient.OAuthClientInterface - oauthClientLister oauthv1listers.OAuthClientLister - routeLister routev1listers.RouteLister - ingressLister configv1listers.IngressLister + oauthClientInformer cache.SharedIndexInformer + oauthClientLister oauthv1listers.OAuthClientLister + routeLister routev1listers.RouteLister + ingressLister configv1listers.IngressLister + + authLister configv1listers.AuthenticationLister + kasLister operatorv1listers.KubeAPIServerLister + kasConfigMapLister corev1listers.ConfigMapLister } func NewOAuthClientsController( operatorClient v1helpers.OperatorClient, oauthsClientClient oauthclient.OAuthClientInterface, - oauthInformers oauthinformers.SharedInformerFactory, + oauthClientsSwitchedInformer *oauthclientsswitchedinformer.InformerWithSwitch, routeInformers routeinformers.SharedInformerFactory, - ingressInformers configinformers.SharedInformerFactory, + operatorConfigInformers configinformers.SharedInformerFactory, + kasInformer operatorv1informers.KubeAPIServerInformer, + kasConfigMapInformer informers.SharedInformerFactory, eventRecorder events.Recorder, ) factory.Controller { c := &oauthsClientsController{ oauthClientClient: oauthsClientClient, - oauthClientLister: oauthInformers.Oauth().V1().OAuthClients().Lister(), - routeLister: routeInformers.Route().V1().Routes().Lister(), - ingressLister: ingressInformers.Config().V1().Ingresses().Lister(), + oauthClientInformer: oauthClientsSwitchedInformer.Informer(), + oauthClientLister: oauthv1listers.NewOAuthClientLister(oauthClientsSwitchedInformer.Informer().GetIndexer()), + routeLister: routeInformers.Route().V1().Routes().Lister(), + ingressLister: operatorConfigInformers.Config().V1().Ingresses().Lister(), + + authLister: operatorConfigInformers.Config().V1().Authentications().Lister(), + kasLister: kasInformer.Lister(), + kasConfigMapLister: kasConfigMapInformer.Core().V1().ConfigMaps().Lister(), } return factory.New(). @@ -61,18 +79,28 @@ func NewOAuthClientsController( WithSyncDegradedOnError(operatorClient). WithFilteredEventsInformers( factory.NamesFilter("openshift-browser-client", "openshift-challenging-client", "openshift-cli-client"), - oauthInformers.Oauth().V1().OAuthClients().Informer(), + oauthClientsSwitchedInformer.Informer(), ). WithFilteredEventsInformers( factory.NamesFilter("oauth-openshift"), routeInformers.Route().V1().Routes().Informer(), ). - WithInformers(ingressInformers.Config().V1().Ingresses().Informer()). + WithInformers( + operatorConfigInformers.Config().V1().Ingresses().Informer(), + operatorConfigInformers.Config().V1().Authentications().Informer(), + kasInformer.Informer(), + ). ResyncEvery(wait.Jitter(time.Minute, 1.0)). ToController("OAuthClientsController", eventRecorder.WithComponentSuffix("oauth-clients-controller")) } func (c *oauthsClientsController) sync(ctx context.Context, syncCtx factory.SyncContext) error { + if oidcAvailable, err := common.ExternalOIDCConfigAvailable(c.authLister, c.kasLister, c.kasConfigMapLister); err != nil { + return err + } else if oidcAvailable { + return c.ensureBootstrappedOAuthClientsMissing(ctx) + } + ingress, err := c.getIngressConfig() if err != nil { return err @@ -89,6 +117,12 @@ func (c *oauthsClientsController) sync(ctx context.Context, syncCtx factory.Sync return err } + waitCtx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + if !cache.WaitForCacheSync(waitCtx.Done(), c.oauthClientInformer.HasSynced) { + return fmt.Errorf("timed out waiting for OAuthClients informer cache sync") + } + return c.ensureBootstrappedOAuthClients(ctx, "https://"+routeHost) } @@ -146,6 +180,27 @@ func (c *oauthsClientsController) ensureBootstrappedOAuthClients(ctx context.Con return nil } +func (c *oauthsClientsController) ensureBootstrappedOAuthClientsMissing(ctx context.Context) error { + for _, clientName := range []string{ + "openshift-browser-client", + "openshift-challenging-client", + "openshift-cli-client", + } { + _, err := c.oauthClientLister.Get(clientName) + if errors.IsNotFound(err) { + continue + } else if err != nil { + return err + } + + if err := c.oauthClientClient.Delete(ctx, clientName, metav1.DeleteOptions{}); err != nil && !errors.IsNotFound(err) { + return err + } + } + + return nil +} + func randomBits(bits uint) []byte { size := bits / 8 if bits%8 != 0 { diff --git a/pkg/controllers/oauthclientscontroller/oauthclientscontroller_test.go b/pkg/controllers/oauthclientscontroller/oauthclientscontroller_test.go index 4c2fd37600..45de84e40b 100644 --- a/pkg/controllers/oauthclientscontroller/oauthclientscontroller_test.go +++ b/pkg/controllers/oauthclientscontroller/oauthclientscontroller_test.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "fmt" "testing" + "time" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" @@ -14,14 +15,17 @@ import ( clienttesting "k8s.io/client-go/testing" "k8s.io/client-go/tools/cache" "k8s.io/client-go/util/workqueue" + clocktesting "k8s.io/utils/clock/testing" configv1 "github.com/openshift/api/config/v1" oauthv1 "github.com/openshift/api/oauth/v1" routev1 "github.com/openshift/api/route/v1" configv1listers "github.com/openshift/client-go/config/listers/config/v1" fakeoauthclient "github.com/openshift/client-go/oauth/clientset/versioned/fake" + oauthinformers "github.com/openshift/client-go/oauth/informers/externalversions" oauthv1listers "github.com/openshift/client-go/oauth/listers/oauth/v1" routev1listers "github.com/openshift/client-go/route/listers/route/v1" + "github.com/openshift/cluster-authentication-operator/pkg/controllers/oauthclientsswitchedinformer" "github.com/openshift/library-go/pkg/oauth/oauthdiscovery" "github.com/openshift/library-go/pkg/operator/events" ) @@ -104,13 +108,39 @@ func newRouteLister(t *testing.T, routes ...*routev1.Route) routev1listers.Route return routev1listers.NewRouteLister(routeIndexer) } +func newAuthLister(t *testing.T) configv1listers.AuthenticationLister { + indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{}) + indexer.Add(&configv1.Authentication{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + Spec: configv1.AuthenticationSpec{ + Type: configv1.AuthenticationTypeIntegratedOAuth, + }, + }) + return configv1listers.NewAuthenticationLister(indexer) +} + func newTestOAuthsClientsController(t *testing.T) (*oauthsClientsController, cache.Indexer) { indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{}) + oauthClientset := fakeoauthclient.NewSimpleClientset() + switchedInformer := oauthclientsswitchedinformer.NewSwitchedInformer( + "TestOAuthClientsInformerWithSwitchController", + context.TODO(), + func() (bool, error) { return false, nil }, + oauthinformers.NewSharedInformerFactoryWithOptions(oauthClientset, 1*time.Minute).Oauth().V1().OAuthClients(), + 0, + nil, + events.NewInMemoryRecorder("oauthclientscontroller_test", clocktesting.NewFakePassiveClock(time.Now())), + ) + return &oauthsClientsController{ - oauthClientClient: fakeoauthclient.NewSimpleClientset().OauthV1().OAuthClients(), - oauthClientLister: oauthv1listers.NewOAuthClientLister(indexer), - routeLister: newRouteLister(t, defaultRoute), - ingressLister: newIngressLister(t, defaultIngress), + authLister: newAuthLister(t), + ingressLister: newIngressLister(t, defaultIngress), + oauthClientClient: oauthClientset.OauthV1().OAuthClients(), + oauthClientInformer: switchedInformer.Informer(), + oauthClientLister: oauthv1listers.NewOAuthClientLister(indexer), + routeLister: newRouteLister(t, defaultRoute), }, indexer } diff --git a/pkg/controllers/oauthclientsswitchedinformer/oauthclientsswitchedinformer_controller.go b/pkg/controllers/oauthclientsswitchedinformer/oauthclientsswitchedinformer_controller.go new file mode 100644 index 0000000000..0bf106e8e7 --- /dev/null +++ b/pkg/controllers/oauthclientsswitchedinformer/oauthclientsswitchedinformer_controller.go @@ -0,0 +1,130 @@ +package oauthclientsswitchedinformer + +import ( + "context" + "time" + + oauthinformersv1 "github.com/openshift/client-go/oauth/informers/externalversions/oauth/v1" + "github.com/openshift/library-go/pkg/controller/factory" + "github.com/openshift/library-go/pkg/operator/events" + + "k8s.io/client-go/tools/cache" + "k8s.io/klog/v2" +) + +// InformerWithSwitch is a controller that can start and stop an informer based on +// a condition func (shouldStopFn), that returns a bool and an error. If an error +// is returned, then the controller's sync will fail with that error. If no error +// is returned, then the controller stops/starts the informer based on the bool value +// (true means stop). +type InformerWithSwitch struct { + delegateInformer oauthinformersv1.OAuthClientInformer + switchController factory.Controller + shouldStopFn func() (bool, error) + parentCtx context.Context + runCtx context.Context + stopFunc func() +} + +type alwaysSyncedInformer struct { + isRunning func() bool + cache.SharedIndexInformer +} + +// HasSynced returns true when the informer's caches have synced, false otherwise. +// Since the SwitchedInformer can be stopped, waiting for its cache to sync can lead to +// timeouts, as a stopped informer will never sync. We override the HasSynced() +// method to always return true when stopped; clients should explicitly call cache.WaitForCacheSync. +func (s *alwaysSyncedInformer) HasSynced() bool { + if s.isRunning() { + return s.SharedIndexInformer.HasSynced() + } + return true +} + +func NewSwitchedInformer( + name string, + ctx context.Context, + shouldStopFn func() (bool, error), + delegateInformer oauthinformersv1.OAuthClientInformer, + resync time.Duration, + informers []factory.Informer, + recorder events.Recorder, +) *InformerWithSwitch { + + s := &InformerWithSwitch{ + parentCtx: ctx, + delegateInformer: delegateInformer, + shouldStopFn: shouldStopFn, + } + + controllerFactory := factory.New().WithSync(s.sync) + + if len(informers) > 0 { + controllerFactory.WithInformers(informers...) + } + + if resync > 0 { + controllerFactory.ResyncEvery(resync) + } + + s.switchController = controllerFactory.ToController(name, recorder) + return s +} + +func (s *InformerWithSwitch) Controller() factory.Controller { + return s.switchController +} + +func (s *InformerWithSwitch) Informer() cache.SharedIndexInformer { + return &alwaysSyncedInformer{ + isRunning: func() bool { return s.runCtx != nil }, + SharedIndexInformer: s.delegateInformer.Informer(), + } +} + +func (s *InformerWithSwitch) Start(stopCh <-chan struct{}) { + go s.switchController.Run(s.parentCtx, 1) + go func() { + <-stopCh + s.stop() + }() +} + +func (s *InformerWithSwitch) ensureRunning() { + if s.runCtx != nil { + return + } + + klog.Infof("%s delegate informer starting", s.switchController.Name()) + s.runCtx, s.stopFunc = context.WithCancel(s.parentCtx) + go s.delegateInformer.Informer().Run(s.runCtx.Done()) +} + +func (s *InformerWithSwitch) stop() { + if s.runCtx == nil { + return + } + + klog.Infof("%s delegate informer stopping", s.switchController.Name()) + s.stopFunc() + s.runCtx = nil + s.stopFunc = nil +} + +func (s *InformerWithSwitch) sync(ctx context.Context, syncCtx factory.SyncContext) error { + if s.shouldStopFn != nil { + shouldStop, err := s.shouldStopFn() + if err != nil { + return err + } + + if shouldStop { + s.stop() + return nil + } + } + + s.ensureRunning() + return nil +} diff --git a/pkg/controllers/oauthclientsswitchedinformer/oauthclientsswitchedinformer_controller_test.go b/pkg/controllers/oauthclientsswitchedinformer/oauthclientsswitchedinformer_controller_test.go new file mode 100644 index 0000000000..2ef68d9b8d --- /dev/null +++ b/pkg/controllers/oauthclientsswitchedinformer/oauthclientsswitchedinformer_controller_test.go @@ -0,0 +1,131 @@ +package oauthclientsswitchedinformer + +import ( + "context" + "fmt" + "testing" + "time" + + oauthv1 "github.com/openshift/api/oauth/v1" + fakeoauthclient "github.com/openshift/client-go/oauth/clientset/versioned/fake" + oauthinformers "github.com/openshift/client-go/oauth/informers/externalversions" + "github.com/openshift/library-go/pkg/operator/events" + "k8s.io/apimachinery/pkg/util/wait" + clocktesting "k8s.io/utils/clock/testing" +) + +func TestSync(t *testing.T) { + testCtx, testCancel := context.WithCancel(context.TODO()) + defer testCancel() + + testOAuthClient := &oauthv1.OAuthClient{} + testClient := fakeoauthclient.NewSimpleClientset(testOAuthClient) + testInformer := oauthinformers.NewSharedInformerFactory(testClient, 0).Oauth().V1().OAuthClients() + + var makeItStop bool + var shouldStopFnErr error + shouldStopFn := func() (bool, error) { + return makeItStop, shouldStopFnErr + } + + informerSwitch := NewSwitchedInformer( + "TestInformerWithSwitchController", + testCtx, + shouldStopFn, + testInformer, + 0, + nil, + events.NewInMemoryRecorder("oauthclientscontroller_test", clocktesting.NewFakePassiveClock(time.Now())), + ) + + t.Run("start informer", func(tt *testing.T) { + makeItStop = false + shouldStopFnErr = nil + err := informerSwitch.sync(testCtx, nil) + if err != nil { + tt.Errorf("unexpected sync error: %v", err) + } + waitForInformerSynced(tt, testCtx, informerSwitch) + + // informer should be running + + if informerSwitch.runCtx == nil { + tt.Error("EnsureRunning: runCtx is nil when it should be non-nil") + } + + if informerSwitch.stopFunc == nil { + tt.Error("EnsureRunning: stopFunc is nil when it should be non-nil") + } + + if informerSwitch.Informer().IsStopped() { + tt.Error("EnsureRunning: informer is stopped when it should be started") + } + }) + + t.Run("stop informer with error", func(tt *testing.T) { + makeItStop = true + shouldStopFnErr = fmt.Errorf("stop fails") + err := informerSwitch.sync(testCtx, nil) + if err == nil { + tt.Errorf("got no error while expecting one") + } + waitForInformerSynced(tt, testCtx, informerSwitch) + + // informer should still be running + + if informerSwitch.runCtx == nil { + tt.Error("EnsureRunning: runCtx is nil when it should be non-nil") + } + + if informerSwitch.stopFunc == nil { + tt.Error("EnsureRunning: stopFunc is nil when it should be non-nil") + } + + if informerSwitch.Informer().IsStopped() { + tt.Error("EnsureRunning: informer is stopped when it should be started") + } + + }) + + t.Run("stop informer without error", func(tt *testing.T) { + makeItStop = true + shouldStopFnErr = nil + err := informerSwitch.sync(testCtx, nil) + if err != nil { + tt.Errorf("unexpected sync error: %v", err) + } + waitForInformerStopped(tt, testCtx, informerSwitch) + + // informer should stop + + if informerSwitch.runCtx != nil { + tt.Error("Stop: runCtx is not nil when it should be nil") + } + + if informerSwitch.stopFunc != nil { + tt.Error("Stop: stopFunc is not nil when it should be nil") + } + + if !informerSwitch.Informer().IsStopped() { + tt.Error("Stop: informer is started when it should be stopped") + } + }) +} + +func waitForInformerSynced(t *testing.T, ctx context.Context, informerSwitch *InformerWithSwitch) { + err := wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, 1*time.Second, true, func(ctx context.Context) (done bool, err error) { + return informerSwitch.Informer().HasSynced(), nil + }) + if err != nil { + t.Fatalf("unexpected error while waiting for informer to sync: %v", err) + } +} + +func waitForInformerStopped(t *testing.T, ctx context.Context, informerSwitch *InformerWithSwitch) { + err := wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, 1*time.Second, true, func(ctx context.Context) (done bool, err error) { + return informerSwitch.Informer().IsStopped(), nil + }) + if err != nil { + t.Fatalf("unexpected error while waiting for informer to stop: %v", err) + } +} diff --git a/pkg/controllers/oauthendpoints/oauth_endpoints_controller.go b/pkg/controllers/oauthendpoints/oauth_endpoints_controller.go index dfb2e2d898..a5165d09b0 100644 --- a/pkg/controllers/oauthendpoints/oauth_endpoints_controller.go +++ b/pkg/controllers/oauthendpoints/oauth_endpoints_controller.go @@ -15,8 +15,10 @@ import ( corev1listers "k8s.io/client-go/listers/core/v1" routev1 "github.com/openshift/api/route/v1" + configinformer "github.com/openshift/client-go/config/informers/externalversions" configv1informers "github.com/openshift/client-go/config/informers/externalversions/config/v1" configv1lister "github.com/openshift/client-go/config/listers/config/v1" + operatorv1 "github.com/openshift/client-go/operator/informers/externalversions/operator/v1" routev1informers "github.com/openshift/client-go/route/informers/externalversions/route/v1" routev1listers "github.com/openshift/client-go/route/listers/route/v1" "github.com/openshift/library-go/pkg/controller/factory" @@ -34,6 +36,9 @@ func NewOAuthRouteCheckController( kubeInformersForConfigManagedNS informers.SharedInformerFactory, routeInformerNamespaces routev1informers.RouteInformer, ingressInformerAllNamespaces configv1informers.IngressInformer, + operatorConfigInformer configinformer.SharedInformerFactory, + kasInformer operatorv1.KubeAPIServerInformer, + kasConfigMapInformer informers.SharedInformerFactory, systemCABundle []byte, recorder events.Recorder, ) factory.Controller { @@ -55,10 +60,18 @@ func NewOAuthRouteCheckController( return getOAuthRouteTLSConfig(cmLister, secretLister, ingressLister, systemCABundle) } + endpointCheckDisabledFunc := func() (bool, error) { + return common.ExternalOIDCConfigAvailable( + operatorConfigInformer.Config().V1().Authentications().Lister(), + kasInformer.Lister(), + kasConfigMapInformer.Core().V1().ConfigMaps().Lister(), + ) + } + return endpointaccessible.NewEndpointAccessibleController( "OAuthServerRoute", operatorClient, - endpointListFunc, getTLSConfigFunc, + endpointListFunc, getTLSConfigFunc, endpointCheckDisabledFunc, []factory.Informer{ cmInformer, secretInformer, @@ -72,6 +85,9 @@ func NewOAuthRouteCheckController( func NewOAuthServiceCheckController( operatorClient v1helpers.OperatorClient, kubeInformersForTargetNS informers.SharedInformerFactory, + operatorConfigInformer configinformer.SharedInformerFactory, + kasInformer operatorv1.KubeAPIServerInformer, + kasConfigMapInformer informers.SharedInformerFactory, recorder events.Recorder, ) factory.Controller { endpointsListFunc := func() ([]string, error) { @@ -82,10 +98,18 @@ func NewOAuthServiceCheckController( return getOAuthEndpointTLSConfig(kubeInformersForTargetNS.Core().V1().ConfigMaps().Lister()) } + endpointCheckDisabledFunc := func() (bool, error) { + return common.ExternalOIDCConfigAvailable( + operatorConfigInformer.Config().V1().Authentications().Lister(), + kasInformer.Lister(), + kasConfigMapInformer.Core().V1().ConfigMaps().Lister(), + ) + } + return endpointaccessible.NewEndpointAccessibleController( "OAuthServerService", operatorClient, - endpointsListFunc, getTLSConfigFunc, + endpointsListFunc, getTLSConfigFunc, endpointCheckDisabledFunc, []factory.Informer{ kubeInformersForTargetNS.Core().V1().ConfigMaps().Informer(), kubeInformersForTargetNS.Core().V1().Services().Informer(), @@ -98,6 +122,9 @@ func NewOAuthServiceCheckController( func NewOAuthServiceEndpointsCheckController( operatorClient v1helpers.OperatorClient, kubeInformersForTargetNS informers.SharedInformerFactory, + operatorConfigInformer configinformer.SharedInformerFactory, + kasInformer operatorv1.KubeAPIServerInformer, + kasConfigMapInformer informers.SharedInformerFactory, recorder events.Recorder, ) factory.Controller { endpointsListFn := func() ([]string, error) { @@ -108,10 +135,18 @@ func NewOAuthServiceEndpointsCheckController( return getOAuthEndpointTLSConfig(kubeInformersForTargetNS.Core().V1().ConfigMaps().Lister()) } + endpointCheckDisabledFunc := func() (bool, error) { + return common.ExternalOIDCConfigAvailable( + operatorConfigInformer.Config().V1().Authentications().Lister(), + kasInformer.Lister(), + kasConfigMapInformer.Core().V1().ConfigMaps().Lister(), + ) + } + return endpointaccessible.NewEndpointAccessibleController( "OAuthServerServiceEndpoints", operatorClient, - endpointsListFn, getTLSConfigFunc, + endpointsListFn, getTLSConfigFunc, endpointCheckDisabledFunc, []factory.Informer{ kubeInformersForTargetNS.Core().V1().Endpoints().Informer(), kubeInformersForTargetNS.Core().V1().ConfigMaps().Informer(), diff --git a/pkg/controllers/payload/payload_config_controller.go b/pkg/controllers/payload/payload_config_controller.go index bfa72b374e..6f862da95b 100644 --- a/pkg/controllers/payload/payload_config_controller.go +++ b/pkg/controllers/payload/payload_config_controller.go @@ -10,6 +10,7 @@ import ( "time" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer" @@ -25,6 +26,10 @@ import ( operatorv1 "github.com/openshift/api/operator/v1" osinv1 "github.com/openshift/api/osin/v1" routev1 "github.com/openshift/api/route/v1" + configinformers "github.com/openshift/client-go/config/informers/externalversions" + configv1listers "github.com/openshift/client-go/config/listers/config/v1" + operatorv1informers "github.com/openshift/client-go/operator/informers/externalversions/operator/v1" + operatorv1listers "github.com/openshift/client-go/operator/listers/operator/v1" routeinformer "github.com/openshift/client-go/route/informers/externalversions/route/v1" routev1lister "github.com/openshift/client-go/route/listers/route/v1" "github.com/openshift/library-go/pkg/controller/factory" @@ -66,11 +71,18 @@ type payloadConfigController struct { configMaps corev1client.ConfigMapsGetter secrets corev1client.SecretsGetter operatorClient v1helpers.OperatorClient + + authLister configv1listers.AuthenticationLister + kasLister operatorv1listers.KubeAPIServerLister + kasConfigMapLister corev1lister.ConfigMapLister } func NewPayloadConfigController( instanceName string, kubeInformersForTargetNamespace informers.SharedInformerFactory, + operatorConfigInformers configinformers.SharedInformerFactory, + kasInformer operatorv1informers.KubeAPIServerInformer, + kasConfigMapInformer informers.SharedInformerFactory, secrets corev1client.SecretsGetter, configMaps corev1client.ConfigMapsGetter, operatorClient v1helpers.OperatorClient, @@ -83,6 +95,10 @@ func NewPayloadConfigController( secrets: secrets, configMaps: configMaps, operatorClient: operatorClient, + + authLister: operatorConfigInformers.Config().V1().Authentications().Lister(), + kasLister: kasInformer.Lister(), + kasConfigMapLister: kasConfigMapInformer.Core().V1().ConfigMaps().Lister(), } return factory.New().WithInformers( kubeInformersForTargetNamespace.Core().V1().Secrets().Informer(), @@ -90,6 +106,8 @@ func NewPayloadConfigController( kubeInformersForTargetNamespace.Core().V1().ConfigMaps().Informer(), routeInformer.Informer(), operatorClient.Informer(), + operatorConfigInformers.Config().V1().Authentications().Informer(), + kasInformer.Informer(), ).ResyncEvery(wait.Jitter(time.Minute, 1.0)).WithSync(c.sync).ToController(c.controllerInstanceName, recorder.WithComponentSuffix("payload-config-controller")) } @@ -138,6 +156,16 @@ func (c *payloadConfigController) getSessionSecret(ctx context.Context, recorder } func (c *payloadConfigController) sync(ctx context.Context, syncContext factory.SyncContext) error { + if oidcAvailable, err := common.ExternalOIDCConfigAvailable(c.authLister, c.kasLister, c.kasConfigMapLister); err != nil { + return err + } else if oidcAvailable { + if err := c.removeOperands(ctx); err != nil { + return err + } + + return common.ApplyControllerConditions(ctx, c.operatorClient, c.controllerInstanceName, knownConditionNames, nil) + } + foundConditions := []operatorv1.OperatorCondition{} foundConditions = append(foundConditions, c.getSessionSecret(ctx, syncContext.Recorder())...) @@ -159,6 +187,18 @@ func (c *payloadConfigController) sync(ctx context.Context, syncContext factory. return common.ApplyControllerConditions(ctx, c.operatorClient, c.controllerInstanceName, knownConditionNames, foundConditions) } +func (c *payloadConfigController) removeOperands(ctx context.Context) error { + if err := c.secrets.Secrets("openshift-authentication").Delete(ctx, "v4-0-config-system-session", metav1.DeleteOptions{}); err != nil && !errors.IsNotFound(err) { + return err + } + + if err := c.configMaps.ConfigMaps("openshift-authentication").Delete(ctx, "v4-0-config-system-cliconfig", metav1.DeleteOptions{}); err != nil && !errors.IsNotFound(err) { + return err + } + + return nil +} + func (c *payloadConfigController) handleOAuthConfig(ctx context.Context, operatorSpec *operatorv1.OperatorSpec, route *routev1.Route, service *corev1.Service, recorder events.Recorder) []operatorv1.OperatorCondition { ca := "/var/config/system/configmaps/v4-0-config-system-service-ca/service-ca.crt" cliConfig := &osinv1.OsinServerConfig{ diff --git a/pkg/controllers/proxyconfig/proxyconfig_controller.go b/pkg/controllers/proxyconfig/proxyconfig_controller.go index 897dd9b2f1..f776544323 100644 --- a/pkg/controllers/proxyconfig/proxyconfig_controller.go +++ b/pkg/controllers/proxyconfig/proxyconfig_controller.go @@ -12,11 +12,17 @@ import ( "golang.org/x/net/http/httpproxy" + "k8s.io/client-go/informers" corev1lister "k8s.io/client-go/listers/core/v1" "k8s.io/klog/v2" + configinformer "github.com/openshift/client-go/config/informers/externalversions" + configv1listers "github.com/openshift/client-go/config/listers/config/v1" + operatorv1 "github.com/openshift/client-go/operator/informers/externalversions/operator/v1" + operatorv1listers "github.com/openshift/client-go/operator/listers/operator/v1" routeinformer "github.com/openshift/client-go/route/informers/externalversions/route/v1" v1 "github.com/openshift/client-go/route/listers/route/v1" + "github.com/openshift/cluster-authentication-operator/pkg/controllers/common" "github.com/openshift/library-go/pkg/controller/factory" "github.com/openshift/library-go/pkg/operator/events" "github.com/openshift/library-go/pkg/operator/v1helpers" @@ -30,11 +36,18 @@ type proxyConfigChecker struct { routeName string routeNamespace string caConfigMaps map[string][]string // ns -> []configmapNames + + authLister configv1listers.AuthenticationLister + kasLister operatorv1listers.KubeAPIServerLister + kasConfigMapLister corev1lister.ConfigMapLister } func NewProxyConfigChecker( routeInformer routeinformer.RouteInformer, configMapInformers v1helpers.KubeInformersForNamespaces, + configInformers configinformer.SharedInformerFactory, + kasInformer operatorv1.KubeAPIServerInformer, + kasConfigMapInformer informers.SharedInformerFactory, routeNamespace string, routeName string, caConfigMaps map[string][]string, @@ -46,6 +59,10 @@ func NewProxyConfigChecker( routeName: routeName, routeNamespace: routeNamespace, caConfigMaps: caConfigMaps, + + authLister: configInformers.Config().V1().Authentications().Lister(), + kasLister: kasInformer.Lister(), + kasConfigMapLister: kasConfigMapInformer.Core().V1().ConfigMaps().Lister(), } c := factory.New(). @@ -68,6 +85,12 @@ func NewProxyConfigChecker( // sync attempts to connect to route using configured proxy settings and reports any error. func (p *proxyConfigChecker) sync(ctx context.Context, _ factory.SyncContext) error { + if oidcAvailable, err := common.ExternalOIDCConfigAvailable(p.authLister, p.kasLister, p.kasConfigMapLister); err != nil { + return err + } else if oidcAvailable { + return nil + } + proxyConfig := httpproxy.FromEnvironment() if !isProxyConfigured(proxyConfig) { // If proxy is not configured, then it is a no-op. diff --git a/pkg/controllers/readiness/wellknown_ready_controller.go b/pkg/controllers/readiness/wellknown_ready_controller.go index 578ab9e8f9..1904bbcdd6 100644 --- a/pkg/controllers/readiness/wellknown_ready_controller.go +++ b/pkg/controllers/readiness/wellknown_ready_controller.go @@ -19,6 +19,8 @@ import ( configinformer "github.com/openshift/client-go/config/informers/externalversions" configv1lister "github.com/openshift/client-go/config/listers/config/v1" applyoperatorv1 "github.com/openshift/client-go/operator/applyconfigurations/operator/v1" + operatorv1informers "github.com/openshift/client-go/operator/informers/externalversions/operator/v1" + operatorv1listers "github.com/openshift/client-go/operator/listers/operator/v1" routeinformer "github.com/openshift/client-go/route/informers/externalversions/route/v1" routev1lister "github.com/openshift/client-go/route/listers/route/v1" "github.com/openshift/cluster-authentication-operator/pkg/controllers/common" @@ -26,12 +28,14 @@ import ( "github.com/openshift/library-go/pkg/controller/factory" "github.com/openshift/library-go/pkg/operator/events" "github.com/openshift/library-go/pkg/operator/v1helpers" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors" netutil "k8s.io/apimachinery/pkg/util/net" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/informers" corev1lister "k8s.io/client-go/listers/core/v1" "k8s.io/klog/v2" ) @@ -58,12 +62,23 @@ type wellKnownReadyController struct { configMapLister corev1lister.ConfigMapLister routeLister routev1lister.RouteLister infrastructureLister configv1lister.InfrastructureLister + + kasLister operatorv1listers.KubeAPIServerLister + kasConfigMapLister corev1lister.ConfigMapLister } const controllerName = "WellKnownReadyController" -func NewWellKnownReadyController(instanceName string, kubeInformers v1helpers.KubeInformersForNamespaces, configInformers configinformer.SharedInformerFactory, routeInformer routeinformer.RouteInformer, - operatorClient v1helpers.OperatorClient, recorder events.Recorder) factory.Controller { +func NewWellKnownReadyController( + instanceName string, + kubeInformers v1helpers.KubeInformersForNamespaces, + configInformers configinformer.SharedInformerFactory, + routeInformer routeinformer.RouteInformer, + kasInformer operatorv1informers.KubeAPIServerInformer, + kasConfigMapInformer informers.SharedInformerFactory, + operatorClient v1helpers.OperatorClient, + recorder events.Recorder, +) factory.Controller { nsOpenshiftConfigManagedInformers := kubeInformers.InformersFor("openshift-config-managed") nsDefaultInformers := kubeInformers.InformersFor("default") @@ -77,6 +92,9 @@ func NewWellKnownReadyController(instanceName string, kubeInformers v1helpers.Ku configMapLister: nsOpenshiftConfigManagedInformers.Core().V1().ConfigMaps().Lister(), routeLister: routeInformer.Lister(), operatorClient: operatorClient, + + kasLister: kasInformer.Lister(), + kasConfigMapLister: kasConfigMapInformer.Core().V1().ConfigMaps().Lister(), } return factory.New().WithInformers( @@ -128,6 +146,15 @@ func (c *wellKnownReadyController) sync(ctx context.Context, controllerContext f } }() + // if OIDC is enabled, clear operator conditions and skip checks + if oidcAvailable, err := common.ExternalOIDCConfigAvailable(c.authLister, c.kasLister, c.kasConfigMapLister); err != nil { + return err + } else if oidcAvailable { + available = available.WithStatus(operatorv1.ConditionTrue) + progressing = progressing.WithStatus(operatorv1.ConditionFalse) + return nil + } + // the well-known endpoint cannot be ready until we know the oauth-server's hostname _, err = c.routeLister.Routes("openshift-authentication").Get("oauth-openshift") if apierrors.IsNotFound(err) { @@ -185,7 +212,7 @@ func (c *wellKnownReadyController) isWellknownEndpointsReady(ctx context.Context return nil } - caData, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/ca.crt") + caData, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/ca.crt") if err != nil { return fmt.Errorf("failed to read SA ca.crt: %v", err) } diff --git a/pkg/controllers/routercerts/controller.go b/pkg/controllers/routercerts/controller.go index 4327b009b5..c3fc7b190d 100644 --- a/pkg/controllers/routercerts/controller.go +++ b/pkg/controllers/routercerts/controller.go @@ -15,9 +15,12 @@ import ( "k8s.io/klog/v2" operatorv1 "github.com/openshift/api/operator/v1" + configinformers "github.com/openshift/client-go/config/informers/externalversions" configv1informers "github.com/openshift/client-go/config/informers/externalversions/config/v1" configv1listers "github.com/openshift/client-go/config/listers/config/v1" applyoperatorv1 "github.com/openshift/client-go/operator/applyconfigurations/operator/v1" + operatorv1informers "github.com/openshift/client-go/operator/informers/externalversions/operator/v1" + operatorv1listers "github.com/openshift/client-go/operator/listers/operator/v1" "github.com/openshift/library-go/pkg/controller/factory" "github.com/openshift/library-go/pkg/crypto" "github.com/openshift/library-go/pkg/operator/events" @@ -45,6 +48,10 @@ type routerCertsDomainValidationController struct { customSecretName string routeName string + authLister configv1listers.AuthenticationLister + kasLister operatorv1listers.KubeAPIServerLister + kasConfigMapLister corev1listers.ConfigMapLister + systemCertPool func() (*x509.CertPool, error) // enables unit testing } @@ -57,6 +64,9 @@ func NewRouterCertsDomainValidationController( targetNSsecretInformer corev1informers.SecretInformer, machineConfigNSSecretInformer corev1informers.SecretInformer, configMapInformer corev1informers.ConfigMapInformer, + operatorConfigInformers configinformers.SharedInformerFactory, + kasInformer operatorv1informers.KubeAPIServerInformer, + kasConfigMapInformer corev1informers.ConfigMapInformer, secretNamespace string, defaultSecretName string, customSecretName string, @@ -74,6 +84,10 @@ func NewRouterCertsDomainValidationController( customSecretName: customSecretName, routeName: routeName, systemCertPool: x509.SystemCertPool, + + authLister: operatorConfigInformers.Config().V1().Authentications().Lister(), + kasLister: kasInformer.Lister(), + kasConfigMapLister: kasConfigMapInformer.Lister(), } return factory.New(). @@ -81,7 +95,9 @@ func NewRouterCertsDomainValidationController( operatorClient.Informer(), ingressInformer.Informer(), targetNSsecretInformer.Informer(), - configMapInformer.Informer()). + configMapInformer.Informer(), + operatorConfigInformers.Config().V1().Authentications().Informer(), + kasInformer.Informer()). WithFilteredEventsInformers( factory.NamesFilter("router-certs"), machineConfigNSSecretInformer.Informer(), @@ -117,6 +133,14 @@ func (c *routerCertsDomainValidationController) sync(ctx context.Context, syncCt WithMessage(condition.Message))) }() + if oidcAvailable, err := common.ExternalOIDCConfigAvailable(c.authLister, c.kasLister, c.kasConfigMapLister); err != nil { + return err + } else if oidcAvailable { + // do not remove secret "v4-0-config-system-router-certs" as the ConfigObserver controller + // monitors it and it will go degraded if missing + return nil + } + // get ingress ingress, err := c.ingressLister.Get("cluster") if err != nil { diff --git a/pkg/controllers/routercerts/controller_test.go b/pkg/controllers/routercerts/controller_test.go index 999e529658..52edc5ce4d 100644 --- a/pkg/controllers/routercerts/controller_test.go +++ b/pkg/controllers/routercerts/controller_test.go @@ -238,11 +238,21 @@ func TestValidateRouterCertificates(t *testing.T) { secretsClient = fake.NewSimpleClientset() } + authIndexer := cache.NewIndexer(func(_ interface{}) (string, error) { + return "cluster", nil + }, cache.Indexers{}) + authIndexer.Add(&configv1.Authentication{ + Spec: configv1.AuthenticationSpec{ + Type: configv1.AuthenticationTypeIntegratedOAuth, + }, + }) + controller := routerCertsDomainValidationController{ operatorClient: operatorClient, ingressLister: configv1listers.NewIngressLister(ingresses), secretLister: corev1listers.NewSecretLister(secrets), configMapLister: corev1listers.NewConfigMapLister(configMapsIndexer), + authLister: configv1listers.NewAuthenticationLister(authIndexer), secretNamespace: "openshift-authentication", defaultSecretName: "v4-0-config-system-router-certs", customSecretName: "v4-0-config-system-custom-router-certs", diff --git a/pkg/controllers/serviceca/service_ca_controller.go b/pkg/controllers/serviceca/service_ca_controller.go index 5f1725f3bf..9606a46fcf 100644 --- a/pkg/controllers/serviceca/service_ca_controller.go +++ b/pkg/controllers/serviceca/service_ca_controller.go @@ -18,6 +18,9 @@ import ( operatorv1 "github.com/openshift/api/operator/v1" configinformers "github.com/openshift/client-go/config/informers/externalversions" + configv1listers "github.com/openshift/client-go/config/listers/config/v1" + operatorv1informers "github.com/openshift/client-go/operator/informers/externalversions/operator/v1" + operatorv1listers "github.com/openshift/client-go/operator/listers/operator/v1" "github.com/openshift/library-go/pkg/controller/factory" "github.com/openshift/library-go/pkg/operator/events" "github.com/openshift/library-go/pkg/operator/v1helpers" @@ -37,18 +40,36 @@ type serviceCAController struct { controllerInstanceName string serviceLister corev1lister.ServiceLister secretLister corev1lister.SecretLister + configMapLister corev1lister.ConfigMapLister configMaps corev1client.ConfigMapsGetter operatorClient v1helpers.OperatorClient + + authLister configv1listers.AuthenticationLister + kasLister operatorv1listers.KubeAPIServerLister + kasConfigMapLister corev1lister.ConfigMapLister } -func NewServiceCAController(instanceName string, kubeInformersForTargetNamespace informers.SharedInformerFactory, configInformer configinformers.SharedInformerFactory, configMaps corev1client.ConfigMapsGetter, - operatorClient v1helpers.OperatorClient, recorder events.Recorder) factory.Controller { +func NewServiceCAController( + instanceName string, + kubeInformersForTargetNamespace informers.SharedInformerFactory, + configInformer configinformers.SharedInformerFactory, + kasInformer operatorv1informers.KubeAPIServerInformer, + kasConfigMapInformer informers.SharedInformerFactory, + configMaps corev1client.ConfigMapsGetter, + operatorClient v1helpers.OperatorClient, + recorder events.Recorder, +) factory.Controller { c := &serviceCAController{ controllerInstanceName: factory.ControllerInstanceName(instanceName, "ServiceCA"), serviceLister: kubeInformersForTargetNamespace.Core().V1().Services().Lister(), secretLister: kubeInformersForTargetNamespace.Core().V1().Secrets().Lister(), + configMapLister: kubeInformersForTargetNamespace.Core().V1().ConfigMaps().Lister(), configMaps: configMaps, operatorClient: operatorClient, + + authLister: configInformer.Config().V1().Authentications().Lister(), + kasLister: kasInformer.Lister(), + kasConfigMapLister: kasConfigMapInformer.Core().V1().ConfigMaps().Lister(), } return factory.New().WithInformers( kubeInformersForTargetNamespace.Core().V1().Secrets().Informer(), @@ -56,6 +77,7 @@ func NewServiceCAController(instanceName string, kubeInformersForTargetNamespace kubeInformersForTargetNamespace.Core().V1().ConfigMaps().Informer(), configInformer.Config().V1().Authentications().Informer(), configInformer.Config().V1().Ingresses().Informer(), + kasInformer.Informer(), ).ResyncEvery( wait.Jitter(time.Minute, 1.0), ).WithSync( @@ -66,6 +88,22 @@ func NewServiceCAController(instanceName string, kubeInformersForTargetNamespace } func (c *serviceCAController) sync(ctx context.Context, syncCtx factory.SyncContext) error { + if oidcAvailable, err := common.ExternalOIDCConfigAvailable(c.authLister, c.kasLister, c.kasConfigMapLister); err != nil { + return err + } else if oidcAvailable { + if _, err := c.configMapLister.ConfigMaps("openshift-authentication").Get("v4-0-config-system-service-ca"); errors.IsNotFound(err) { + return nil + } else if err != nil { + return err + } + + if err := c.configMaps.ConfigMaps("openshift-authentication").Delete(ctx, "v4-0-config-system-service-ca", metav1.DeleteOptions{}); err != nil && !errors.IsNotFound(err) { + return err + } + + return common.ApplyControllerConditions(ctx, c.operatorClient, c.controllerInstanceName, knownConditionNames, nil) + } + foundConditions := []operatorv1.OperatorCondition{} _, serviceConditions := common.GetOAuthServerService(c.serviceLister, "OAuthService") diff --git a/pkg/controllers/trustdistribution/trustdistribution_controller.go b/pkg/controllers/trustdistribution/trustdistribution_controller.go index 10c7a5e700..9405946027 100644 --- a/pkg/controllers/trustdistribution/trustdistribution_controller.go +++ b/pkg/controllers/trustdistribution/trustdistribution_controller.go @@ -7,6 +7,7 @@ import ( "time" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/wait" @@ -16,6 +17,7 @@ import ( "github.com/openshift/api/annotations" configinformers "github.com/openshift/client-go/config/informers/externalversions/config/v1" configlistersv1 "github.com/openshift/client-go/config/listers/config/v1" + operatorv1listers "github.com/openshift/client-go/operator/listers/operator/v1" "github.com/openshift/library-go/pkg/controller/factory" "github.com/openshift/library-go/pkg/operator/events" "github.com/openshift/library-go/pkg/operator/resource/resourceapply" @@ -24,30 +26,48 @@ import ( "github.com/openshift/cluster-authentication-operator/pkg/controllers/common" ) +const ( + managedNamespace = "openshift-config-managed" + authNamespace = "openshift-authentication" + servingCertConfigMap = "oauth-serving-cert" +) + type trustDistributionController struct { - configMaps corev1client.ConfigMapsGetter - secretsLister corev1listers.SecretLister + configMaps corev1client.ConfigMapsGetter + secretsLister corev1listers.SecretLister + configMapsLister corev1listers.ConfigMapLister ingressLister configlistersv1.IngressLister + + authLister configlistersv1.AuthenticationLister + kasLister operatorv1listers.KubeAPIServerLister + kasConfigMapLister corev1listers.ConfigMapLister } func NewTrustDistributionController( cmClient corev1client.ConfigMapsGetter, kubeInformersForNamespaces v1helpers.KubeInformersForNamespaces, ingressInformer configinformers.IngressInformer, + authLister configlistersv1.AuthenticationLister, + kasLister operatorv1listers.KubeAPIServerLister, + kasConfigMapLister corev1listers.ConfigMapLister, eventsRecorder events.Recorder, ) factory.Controller { c := &trustDistributionController{ - configMaps: cmClient, - secretsLister: kubeInformersForNamespaces.SecretLister(), - ingressLister: ingressInformer.Lister(), + configMaps: cmClient, + secretsLister: kubeInformersForNamespaces.SecretLister(), + configMapsLister: kubeInformersForNamespaces.ConfigMapLister(), + ingressLister: ingressInformer.Lister(), + authLister: authLister, + kasLister: kasLister, + kasConfigMapLister: kasConfigMapLister, } return factory.New(). WithInformers( ingressInformer.Informer(), - kubeInformersForNamespaces.InformersFor("openshift-authentication").Core().V1().Secrets().Informer(), - kubeInformersForNamespaces.InformersFor("openshift-config-managed").Core().V1().Secrets().Informer(), + kubeInformersForNamespaces.InformersFor(authNamespace).Core().V1().Secrets().Informer(), + kubeInformersForNamespaces.InformersFor(managedNamespace).Core().V1().Secrets().Informer(), ). WithSync(c.sync). ResyncEvery(wait.Jitter(time.Minute, 1.0)). @@ -55,6 +75,12 @@ func NewTrustDistributionController( } func (c *trustDistributionController) sync(ctx context.Context, syncContext factory.SyncContext) error { + if oidcAvailable, err := common.ExternalOIDCConfigAvailable(c.authLister, c.kasLister, c.kasConfigMapLister); err != nil { + return err + } else if oidcAvailable { + return c.removeOperands(ctx) + } + ingressConfig, err := c.ingressLister.Get("cluster") if err != nil { return err @@ -62,7 +88,7 @@ func (c *trustDistributionController) sync(ctx context.Context, syncContext fact certBundle, _, _, err := common.GetActiveRouterCertKeyBytes(c.secretsLister, ingressConfig, - "openshift-authentication", + authNamespace, "v4-0-config-system-router-certs", "v4-0-config-system-custom-router-certs", ) @@ -94,8 +120,8 @@ func (c *trustDistributionController) sync(ctx context.Context, syncContext fact syncContext.Recorder(), &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ - Name: "oauth-serving-cert", - Namespace: "openshift-config-managed", + Name: servingCertConfigMap, + Namespace: managedNamespace, Annotations: map[string]string{ annotations.OpenShiftComponent: "apiserver-auth", }, @@ -107,3 +133,13 @@ func (c *trustDistributionController) sync(ctx context.Context, syncContext fact return err } + +func (c *trustDistributionController) removeOperands(ctx context.Context) error { + if _, err := c.configMapsLister.ConfigMaps(managedNamespace).Get(servingCertConfigMap); errors.IsNotFound(err) { + return nil + } else if err != nil { + return err + } + + return c.configMaps.ConfigMaps(managedNamespace).Delete(ctx, servingCertConfigMap, metav1.DeleteOptions{}) +} diff --git a/pkg/controllers/webhookauthenticator/webhookauthenticator_controller.go b/pkg/controllers/webhookauthenticator/webhookauthenticator_controller.go index 9382472384..69c94b9e8a 100644 --- a/pkg/controllers/webhookauthenticator/webhookauthenticator_controller.go +++ b/pkg/controllers/webhookauthenticator/webhookauthenticator_controller.go @@ -4,7 +4,6 @@ import ( "context" "encoding/base64" "fmt" - "io/ioutil" "net" "os" "strconv" @@ -12,6 +11,7 @@ import ( "time" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" @@ -20,13 +20,16 @@ import ( corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/util/flowcontrol" "k8s.io/klog/v2" + "k8s.io/utils/ptr" "github.com/openshift/api/annotations" configv1 "github.com/openshift/api/config/v1" operatorv1 "github.com/openshift/api/operator/v1" configv1client "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1" configinformers "github.com/openshift/client-go/config/informers/externalversions" + configv1listers "github.com/openshift/client-go/config/listers/config/v1" applyoperatorv1 "github.com/openshift/client-go/operator/applyconfigurations/operator/v1" + operatorv1listers "github.com/openshift/client-go/operator/listers/operator/v1" "github.com/openshift/library-go/pkg/controller/factory" "github.com/openshift/library-go/pkg/operator/events" "github.com/openshift/library-go/pkg/operator/resource/resourceapply" @@ -34,6 +37,12 @@ import ( "github.com/openshift/library-go/pkg/operator/v1helpers" "github.com/openshift/cluster-authentication-operator/bindata" + "github.com/openshift/cluster-authentication-operator/pkg/controllers/common" +) + +const ( + configNamespace = "openshift-config" + webhookSecretName = "webhook-authentication-integrated-oauth" ) // webhookAuthenticatorController makes sure that the webhook token authenticators @@ -50,6 +59,10 @@ type webhookAuthenticatorController struct { secrets corev1client.SecretsGetter secretsLister corev1listers.SecretLister + authLister configv1listers.AuthenticationLister + kasLister operatorv1listers.KubeAPIServerLister + kasConfigMapLister corev1listers.ConfigMapLister + operatorClient v1helpers.OperatorClient apiServerVersionWaitEventsLimiter flowcontrol.RateLimiter @@ -63,6 +76,9 @@ func NewWebhookAuthenticatorController( secrets corev1client.SecretsGetter, serviceAccounts corev1client.ServiceAccountsGetter, authentication configv1client.AuthenticationInterface, + authLister configv1listers.AuthenticationLister, + kasLister operatorv1listers.KubeAPIServerLister, + kasConfigMapLister corev1listers.ConfigMapLister, operatorClient v1helpers.OperatorClient, versionGetter status.VersionGetter, recorder events.Recorder, @@ -75,6 +91,9 @@ func NewWebhookAuthenticatorController( svcLister: kubeInformersForTargetNamespace.Core().V1().Services().Lister(), saLister: kubeInformersForTargetNamespace.Core().V1().ServiceAccounts().Lister(), authentication: authentication, + authLister: authLister, + kasLister: kasLister, + kasConfigMapLister: kasConfigMapLister, operatorClient: operatorClient, apiServerVersionWaitEventsLimiter: flowcontrol.NewTokenBucketRateLimiter(0.0167, 1), // set it so that the event may only occur once per minute versionGetter: versionGetter, @@ -93,6 +112,12 @@ func NewWebhookAuthenticatorController( } func (c *webhookAuthenticatorController) sync(ctx context.Context, syncCtx factory.SyncContext) error { + if oidcAvailable, err := common.ExternalOIDCConfigAvailable(c.authLister, c.kasLister, c.kasConfigMapLister); err != nil { + return err + } else if oidcAvailable { + return c.removeOperands(ctx) + } + // TODO: remove in 4.9; this is to ensure we don't configure webhook authenticators // before the oauth-apiserver revision that's capable of handling it is ready // during upgrade @@ -154,7 +179,7 @@ func (c *webhookAuthenticatorController) ensureKubeConfigSecret(ctx context.Cont return nil, err } - caBundle, err := ioutil.ReadFile("/var/run/configmaps/service-ca-bundle/service-ca.crt") + caBundle, err := os.ReadFile("/var/run/configmaps/service-ca-bundle/service-ca.crt") if err != nil { return nil, fmt.Errorf("failed to read service-ca crt bundle: %w", err) } @@ -175,8 +200,8 @@ func (c *webhookAuthenticatorController) ensureKubeConfigSecret(ctx context.Cont requiredSecret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: "webhook-authentication-integrated-oauth", - Namespace: "openshift-config", + Name: webhookSecretName, + Namespace: configNamespace, Annotations: map[string]string{ annotations.OpenShiftComponent: "apiserver-auth", }, @@ -223,7 +248,7 @@ func (c *webhookAuthenticatorController) getAuthenticatorCertKeyPair(ctx context certSecret, err := c.secretsLister.Secrets("openshift-oauth-apiserver").Get("openshift-authenticator-certs") if err != nil { if apierrors.IsNotFound(err) { - waitingForCertKeyMsg = pstr("waiting for the cert/key secret openshift-oauth-apiserver/openshift-authenticator-certs to appear") + waitingForCertKeyMsg = ptr.To("waiting for the cert/key secret openshift-oauth-apiserver/openshift-authenticator-certs to appear") return nil, nil, nil } return nil, nil, err @@ -231,21 +256,28 @@ func (c *webhookAuthenticatorController) getAuthenticatorCertKeyPair(ctx context key, ok := certSecret.Data["tls.key"] if !ok { - waitingForCertKeyMsg = pstr("the authenticator's client cert secret is missing the 'tls.key' key") + waitingForCertKeyMsg = ptr.To("the authenticator's client cert secret is missing the 'tls.key' key") return nil, nil, nil } cert, ok = certSecret.Data["tls.crt"] if !ok { - waitingForCertKeyMsg = pstr("the authenticator's client cert secret is missing the 'tls.crt' key") + waitingForCertKeyMsg = ptr.To("the authenticator's client cert secret is missing the 'tls.crt' key") return nil, nil, nil } // stop progressing on the cert/key secret - waitingForCertKeyMsg = pstr("") + waitingForCertKeyMsg = ptr.To("") return key, cert, nil } -func pstr(s string) *string { - return &s +func (c *webhookAuthenticatorController) removeOperands(ctx context.Context) error { + _, err := c.secretsLister.Secrets(configNamespace).Get(webhookSecretName) + if errors.IsNotFound(err) { + return nil + } else if err != nil { + return err + } + + return c.secrets.Secrets(configNamespace).Delete(ctx, webhookSecretName, metav1.DeleteOptions{}) } diff --git a/pkg/libs/endpointaccessible/endpoint_accessible_controller.go b/pkg/libs/endpointaccessible/endpoint_accessible_controller.go index e4a6dd96dc..2d7bc58d96 100644 --- a/pkg/libs/endpointaccessible/endpoint_accessible_controller.go +++ b/pkg/libs/endpointaccessible/endpoint_accessible_controller.go @@ -21,15 +21,17 @@ import ( ) type endpointAccessibleController struct { - controllerInstanceName string - operatorClient v1helpers.OperatorClient - endpointListFn EndpointListFunc - getTLSConfigFn EndpointTLSConfigFunc - availableConditionName string + controllerInstanceName string + operatorClient v1helpers.OperatorClient + endpointListFn EndpointListFunc + getTLSConfigFn EndpointTLSConfigFunc + availableConditionName string + endpointCheckDisabledFunc EndpointCheckDisabledFunc } type EndpointListFunc func() ([]string, error) type EndpointTLSConfigFunc func() (*tls.Config, error) +type EndpointCheckDisabledFunc func() (bool, error) // NewEndpointAccessibleController returns a controller that checks if the endpoints // listed by endpointListFn are reachable @@ -38,17 +40,19 @@ func NewEndpointAccessibleController( operatorClient v1helpers.OperatorClient, endpointListFn EndpointListFunc, getTLSConfigFn EndpointTLSConfigFunc, + endpointCheckDisabledFunc EndpointCheckDisabledFunc, triggers []factory.Informer, recorder events.Recorder, ) factory.Controller { controllerName := name + "EndpointAccessibleController" c := &endpointAccessibleController{ - controllerInstanceName: factory.ControllerInstanceName(name, "EndpointAccessible"), - operatorClient: operatorClient, - endpointListFn: endpointListFn, - getTLSConfigFn: getTLSConfigFn, - availableConditionName: name + "EndpointAccessibleControllerAvailable", + controllerInstanceName: factory.ControllerInstanceName(name, "EndpointAccessible"), + operatorClient: operatorClient, + endpointListFn: endpointListFn, + getTLSConfigFn: getTLSConfigFn, + availableConditionName: name + "EndpointAccessibleControllerAvailable", + endpointCheckDisabledFunc: endpointCheckDisabledFunc, } return factory.New(). @@ -73,6 +77,12 @@ func humanizeError(err error) error { } func (c *endpointAccessibleController) sync(ctx context.Context, syncCtx factory.SyncContext) error { + if c.endpointCheckDisabledFunc != nil { + if skip, err := c.endpointCheckDisabledFunc(); skip || err != nil { + return err + } + } + endpoints, err := c.endpointListFn() if err != nil { if apierrors.IsNotFound(err) { diff --git a/pkg/operator/replacement_starter.go b/pkg/operator/replacement_starter.go index 4a8d388896..be11f742a3 100644 --- a/pkg/operator/replacement_starter.go +++ b/pkg/operator/replacement_starter.go @@ -24,7 +24,6 @@ import ( configclient "github.com/openshift/client-go/config/clientset/versioned" configinformer "github.com/openshift/client-go/config/informers/externalversions" oauthclient "github.com/openshift/client-go/oauth/clientset/versioned" - oauthinformers "github.com/openshift/client-go/oauth/informers/externalversions" operatorclient "github.com/openshift/client-go/operator/clientset/versioned" operatorinformer "github.com/openshift/client-go/operator/informers/externalversions" routeclient "github.com/openshift/client-go/route/clientset/versioned" @@ -232,7 +231,6 @@ type authenticationOperatorInformerFactories struct { kubeInformersForNamespaces v1helpers.KubeInformersForNamespaces operatorConfigInformer configinformer.SharedInformerFactory operatorInformer operatorinformer.SharedInformerFactory - oauthInformers oauthinformers.SharedInformerFactory apiregistrationInformers apiregistrationinformers.SharedInformerFactory migrationInformer migrationv1alpha1informer.SharedInformerFactory // TODO remove @@ -251,13 +249,13 @@ func newInformerFactories(authOperatorInput *authenticationOperatorInput) authen "openshift-config-managed", "openshift-oauth-apiserver", "openshift-authentication-operator", + "openshift-kube-apiserver", "", // an informer for non-namespaced resources "kube-system", libgoetcd.EtcdEndpointNamespace, ), operatorConfigInformer: configinformer.NewSharedInformerFactoryWithOptions(authOperatorInput.configClient, resync), operatorInformer: operatorinformer.NewSharedInformerFactory(authOperatorInput.operatorClient, 24*time.Hour), - oauthInformers: oauthinformers.NewSharedInformerFactory(authOperatorInput.oauthClient, resync), apiregistrationInformers: apiregistrationinformers.NewSharedInformerFactory(authOperatorInput.apiregistrationv1Client, 10*time.Minute), migrationInformer: migrationv1alpha1informer.NewSharedInformerFactory(authOperatorInput.migrationClient, time.Minute*30), kubeInformers: kubeinformers.NewSharedInformerFactory(authOperatorInput.kubeClient, resync), @@ -274,7 +272,6 @@ func (a authenticationOperatorInformerFactories) simplifiedInformerFactories() [ libraryapplyconfiguration.GeneratedNamespacedInformerFactoryAdapter(a.kubeInformersForNamespaces), libraryapplyconfiguration.GeneratedInformerFactoryAdapter(a.operatorInformer), libraryapplyconfiguration.GeneratedInformerFactoryAdapter(a.operatorConfigInformer), - libraryapplyconfiguration.GeneratedInformerFactoryAdapter(a.oauthInformers), libraryapplyconfiguration.GeneratedInformerFactoryAdapter(a.apiregistrationInformers), libraryapplyconfiguration.GeneratedInformerFactoryAdapter(a.migrationInformer), libraryapplyconfiguration.GeneratedInformerFactoryAdapter(a.kubeInformers), diff --git a/pkg/operator/starter.go b/pkg/operator/starter.go index 95e27123bd..096eda1f2a 100644 --- a/pkg/operator/starter.go +++ b/pkg/operator/starter.go @@ -5,7 +5,6 @@ import ( "crypto/x509" "crypto/x509/pkix" "fmt" - "io/ioutil" "os" "time" @@ -15,8 +14,12 @@ import ( "github.com/openshift/api/features" operatorv1 "github.com/openshift/api/operator/v1" routev1 "github.com/openshift/api/route/v1" + configv1listers "github.com/openshift/client-go/config/listers/config/v1" + oauthinformers "github.com/openshift/client-go/oauth/informers/externalversions" applyoperatorv1 "github.com/openshift/client-go/operator/applyconfigurations/operator/v1" + operatorv1listers "github.com/openshift/client-go/operator/listers/operator/v1" "github.com/openshift/cluster-authentication-operator/bindata" + "github.com/openshift/cluster-authentication-operator/pkg/controllers/common" "github.com/openshift/cluster-authentication-operator/pkg/controllers/configobservation/configobservercontroller" componentroutesecretsync "github.com/openshift/cluster-authentication-operator/pkg/controllers/customroute" "github.com/openshift/cluster-authentication-operator/pkg/controllers/deployment" @@ -25,6 +28,7 @@ import ( "github.com/openshift/cluster-authentication-operator/pkg/controllers/ingressstate" "github.com/openshift/cluster-authentication-operator/pkg/controllers/metadata" "github.com/openshift/cluster-authentication-operator/pkg/controllers/oauthclientscontroller" + "github.com/openshift/cluster-authentication-operator/pkg/controllers/oauthclientsswitchedinformer" "github.com/openshift/cluster-authentication-operator/pkg/controllers/oauthendpoints" "github.com/openshift/cluster-authentication-operator/pkg/controllers/payload" "github.com/openshift/cluster-authentication-operator/pkg/controllers/proxyconfig" @@ -38,6 +42,7 @@ import ( "github.com/openshift/cluster-authentication-operator/pkg/operator/workload" "github.com/openshift/library-go/pkg/authentication/bootstrapauthenticator" "github.com/openshift/library-go/pkg/controller/controllercmd" + "github.com/openshift/library-go/pkg/controller/factory" workloadcontroller "github.com/openshift/library-go/pkg/operator/apiserver/controller/workload" apiservercontrollerset "github.com/openshift/library-go/pkg/operator/apiserver/controllerset" "github.com/openshift/library-go/pkg/operator/certrotation" @@ -66,9 +71,10 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/selection" "k8s.io/apimachinery/pkg/util/sets" + corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/klog/v2" apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" - utilpointer "k8s.io/utils/pointer" + "k8s.io/utils/ptr" ) const ( @@ -130,25 +136,38 @@ func prepareOauthOperator( authOperatorInput.eventRecorder, ) + authLister := informerFactories.operatorConfigInformer.Config().V1().Authentications().Lister() + kasLister := informerFactories.operatorInformer.Operator().V1().KubeAPIServers().Lister() + kasConfigMapLister := informerFactories.kubeInformersForNamespaces.InformersFor("openshift-kube-apiserver").Core().V1().ConfigMaps().Lister() + staticResourceController := staticresourcecontroller.NewStaticResourceController( "OpenshiftAuthenticationStaticResources", bindata.Asset, - []string{ + []string{ // required resources "oauth-openshift/audit-policy.yaml", "oauth-openshift/ns.yaml", "oauth-openshift/authentication-clusterrolebinding.yaml", - "oauth-openshift/cabundle.yaml", - "oauth-openshift/branding-secret.yaml", "oauth-openshift/serviceaccount.yaml", - "oauth-openshift/oauth-service.yaml", - "oauth-openshift/trust_distribution_role.yaml", - "oauth-openshift/trust_distribution_rolebinding.yaml", - "oauth-openshift/authorization.openshift.io_rolebindingrestrictions.yaml", }, resourceapply.NewKubeClientHolder(authOperatorInput.kubeClient).WithAPIExtensionsClient(authOperatorInput.apiextensionClient), authOperatorInput.authenticationOperatorClient, authOperatorInput.eventRecorder, - ).AddKubeInformers(informerFactories.kubeInformersForNamespaces) + ).AddKubeInformers(informerFactories.kubeInformersForNamespaces). + WithConditionalResources(bindata.Asset, + []string{ // OAuth specific resources; deleted when OIDC is enabled + "oauth-openshift/cabundle.yaml", + "oauth-openshift/branding-secret.yaml", + "oauth-openshift/oauth-service.yaml", + "oauth-openshift/authorization.openshift.io_rolebindingrestrictions.yaml", + "oauth-openshift/trust_distribution_role.yaml", + "oauth-openshift/trust_distribution_rolebinding.yaml", + }, + func() bool { + oidcAvailable, _ := common.ExternalOIDCConfigAvailable(authLister, kasLister, kasConfigMapLister) + return !oidcAvailable + }, + nil, + ) configObserver := configobservercontroller.NewConfigObserver( authOperatorInput.authenticationOperatorClient, @@ -168,6 +187,9 @@ func prepareOauthOperator( informerFactories.kubeInformersForNamespaces.InformersFor("openshift-authentication").Core().V1().Secrets(), informerFactories.kubeInformersForNamespaces.InformersFor("openshift-config-managed").Core().V1().Secrets(), informerFactories.kubeInformersForNamespaces.InformersFor("openshift-config-managed").Core().V1().ConfigMaps(), + informerFactories.operatorConfigInformer, + informerFactories.operatorInformer.Operator().V1().KubeAPIServers(), + informerFactories.kubeInformersForNamespaces.InformersFor("openshift-kube-apiserver").Core().V1().ConfigMaps(), "openshift-authentication", "v4-0-config-system-router-certs", "v4-0-config-system-custom-router-certs", @@ -177,6 +199,9 @@ func prepareOauthOperator( ingressStateController := ingressstate.NewIngressStateController( "openshift-authentication", informerFactories.kubeInformersForNamespaces.InformersFor("openshift-authentication"), + informerFactories.operatorConfigInformer, + informerFactories.operatorInformer.Operator().V1().KubeAPIServers(), + informerFactories.kubeInformersForNamespaces.InformersFor("openshift-kube-apiserver"), authOperatorInput.kubeClient.CoreV1(), authOperatorInput.kubeClient.CoreV1(), authOperatorInput.authenticationOperatorClient, @@ -188,6 +213,8 @@ func prepareOauthOperator( informerFactories.kubeInformersForNamespaces, informerFactories.operatorConfigInformer, informerFactories.namespacedOpenshiftAuthenticationRoutes.Route().V1().Routes(), + informerFactories.operatorInformer.Operator().V1().KubeAPIServers(), + informerFactories.kubeInformersForNamespaces.InformersFor("openshift-kube-apiserver"), authOperatorInput.authenticationOperatorClient, authOperatorInput.eventRecorder, ) @@ -197,6 +224,8 @@ func prepareOauthOperator( informerFactories.kubeInformersForNamespaces.InformersFor("openshift-authentication"), informerFactories.operatorConfigInformer, informerFactories.namespacedOpenshiftAuthenticationRoutes, + informerFactories.operatorInformer.Operator().V1().KubeAPIServers(), + informerFactories.kubeInformersForNamespaces.InformersFor("openshift-kube-apiserver"), authOperatorInput.kubeClient.CoreV1(), authOperatorInput.routeClient.RouteV1().Routes("openshift-authentication"), authOperatorInput.configClient.ConfigV1().Authentications(), @@ -208,6 +237,8 @@ func prepareOauthOperator( "openshift-authentication", informerFactories.kubeInformersForNamespaces.InformersFor("openshift-authentication"), informerFactories.operatorConfigInformer, + informerFactories.operatorInformer.Operator().V1().KubeAPIServers(), + informerFactories.kubeInformersForNamespaces.InformersFor("openshift-kube-apiserver"), authOperatorInput.kubeClient.CoreV1(), authOperatorInput.authenticationOperatorClient, authOperatorInput.eventRecorder, @@ -216,6 +247,9 @@ func prepareOauthOperator( payloadConfigController := payload.NewPayloadConfigController( "openshift-authentication", informerFactories.kubeInformersForNamespaces.InformersFor("openshift-authentication"), + informerFactories.operatorConfigInformer, + informerFactories.operatorInformer.Operator().V1().KubeAPIServers(), + informerFactories.kubeInformersForNamespaces.InformersFor("openshift-kube-apiserver"), authOperatorInput.kubeClient.CoreV1(), authOperatorInput.kubeClient.CoreV1(), authOperatorInput.authenticationOperatorClient, @@ -223,12 +257,29 @@ func prepareOauthOperator( authOperatorInput.eventRecorder, ) + oauthClientsSwitchedInformer := oauthclientsswitchedinformer.NewSwitchedInformer( + "OAuthClientsInformerWithSwitchController", + ctx, + func() (bool, error) { + return common.ExternalOIDCConfigAvailable(authLister, kasLister, kasConfigMapLister) + }, + oauthinformers.NewSharedInformerFactoryWithOptions(authOperatorInput.oauthClient, 1*time.Minute).Oauth().V1().OAuthClients(), + 0, + []factory.Informer{ + informerFactories.operatorInformer.Operator().V1().KubeAPIServers().Informer(), + informerFactories.operatorConfigInformer.Config().V1().Authentications().Informer(), + }, + authOperatorInput.eventRecorder, + ) + oauthClientsController := oauthclientscontroller.NewOAuthClientsController( authOperatorInput.authenticationOperatorClient, authOperatorInput.oauthClient.OauthV1().OAuthClients(), - informerFactories.oauthInformers, + oauthClientsSwitchedInformer, informerFactories.namespacedOpenshiftAuthenticationRoutes, informerFactories.operatorConfigInformer, + informerFactories.operatorInformer.Operator().V1().KubeAPIServers(), + informerFactories.kubeInformersForNamespaces.InformersFor("openshift-kube-apiserver"), authOperatorInput.eventRecorder, ) @@ -245,6 +296,8 @@ func prepareOauthOperator( authOperatorInput.eventRecorder, versionRecorder, informerFactories.kubeInformersForNamespaces.InformersFor("openshift-authentication"), + informerFactories.operatorInformer.Operator().V1().KubeAPIServers(), + informerFactories.kubeInformersForNamespaces.InformersFor("openshift-kube-apiserver"), ) workersAvailableController := ingressnodesavailable.NewIngressNodesAvailableController( @@ -253,6 +306,9 @@ func prepareOauthOperator( informerFactories.operatorInformer.Operator().V1().IngressControllers(), authOperatorInput.eventRecorder, informerFactories.kubeInformersForNamespaces.InformersFor("").Core().V1().Nodes(), + informerFactories.operatorConfigInformer, + informerFactories.operatorInformer.Operator().V1().KubeAPIServers(), + informerFactories.kubeInformersForNamespaces.InformersFor("openshift-kube-apiserver"), ) systemCABundle, err := loadSystemCACertBundle() @@ -266,6 +322,9 @@ func prepareOauthOperator( informerFactories.kubeInformersForNamespaces.InformersFor("openshift-config-managed"), informerFactories.namespacedOpenshiftAuthenticationRoutes.Route().V1().Routes(), informerFactories.operatorConfigInformer.Config().V1().Ingresses(), + informerFactories.operatorConfigInformer, + informerFactories.operatorInformer.Operator().V1().KubeAPIServers(), + informerFactories.kubeInformersForNamespaces.InformersFor("openshift-kube-apiserver"), systemCABundle, authOperatorInput.eventRecorder, ) @@ -273,18 +332,27 @@ func prepareOauthOperator( authServiceCheckController := oauthendpoints.NewOAuthServiceCheckController( authOperatorInput.authenticationOperatorClient, informerFactories.kubeInformersForNamespaces.InformersFor("openshift-authentication"), + informerFactories.operatorConfigInformer, + informerFactories.operatorInformer.Operator().V1().KubeAPIServers(), + informerFactories.kubeInformersForNamespaces.InformersFor("openshift-kube-apiserver"), authOperatorInput.eventRecorder, ) authServiceEndpointCheckController := oauthendpoints.NewOAuthServiceEndpointsCheckController( authOperatorInput.authenticationOperatorClient, informerFactories.kubeInformersForNamespaces.InformersFor("openshift-authentication"), + informerFactories.operatorConfigInformer, + informerFactories.operatorInformer.Operator().V1().KubeAPIServers(), + informerFactories.kubeInformersForNamespaces.InformersFor("openshift-kube-apiserver"), authOperatorInput.eventRecorder, ) proxyConfigController := proxyconfig.NewProxyConfigChecker( informerFactories.namespacedOpenshiftAuthenticationRoutes.Route().V1().Routes(), informerFactories.kubeInformersForNamespaces, + informerFactories.operatorConfigInformer, + informerFactories.operatorInformer.Operator().V1().KubeAPIServers(), + informerFactories.kubeInformersForNamespaces.InformersFor("openshift-kube-apiserver"), "openshift-authentication", "oauth-openshift", map[string][]string{ @@ -305,6 +373,9 @@ func prepareOauthOperator( informerFactories.namespacedOpenshiftAuthenticationRoutes.Route().V1().Routes(), authOperatorInput.routeClient.RouteV1().Routes("openshift-authentication"), informerFactories.kubeInformersForNamespaces, + informerFactories.operatorConfigInformer, + informerFactories.operatorInformer.Operator().V1().KubeAPIServers(), + informerFactories.kubeInformersForNamespaces.InformersFor("openshift-kube-apiserver"), authOperatorInput.authenticationOperatorClient, authOperatorInput.eventRecorder, resourceSyncController, @@ -318,6 +389,9 @@ func prepareOauthOperator( authOperatorInput.kubeClient.CoreV1(), informerFactories.kubeInformersForNamespaces, informerFactories.operatorConfigInformer.Config().V1().Ingresses(), + authLister, + kasLister, + kasConfigMapLister, authOperatorInput.eventRecorder, ) @@ -326,6 +400,7 @@ func prepareOauthOperator( libraryapplyconfiguration.AdaptSyncFn(authOperatorInput.eventRecorder, "TODO-deploymentController", deploymentController.Sync), libraryapplyconfiguration.AdaptSyncFn(authOperatorInput.eventRecorder, "TODO-managementStateController", managementStateController.Sync), libraryapplyconfiguration.AdaptSyncFn(authOperatorInput.eventRecorder, "TODO-metadataController", metadataController.Sync), + libraryapplyconfiguration.AdaptSyncFn(authOperatorInput.eventRecorder, "TODO-oauthClientsSwitchedInformerController", oauthClientsSwitchedInformer.Controller().Sync), libraryapplyconfiguration.AdaptSyncFn(authOperatorInput.eventRecorder, "TODO-oauthClientsController", oauthClientsController.Sync), libraryapplyconfiguration.AdaptSyncFn(authOperatorInput.eventRecorder, "TODO-payloadConfigController", payloadConfigController.Sync), libraryapplyconfiguration.AdaptSyncFn(authOperatorInput.eventRecorder, "TODO-routerCertsController", routerCertsController.Sync), @@ -348,6 +423,7 @@ func prepareOauthOperator( libraryapplyconfiguration.AdaptRunFn(deploymentController.Run), libraryapplyconfiguration.AdaptRunFn(managementStateController.Run), libraryapplyconfiguration.AdaptRunFn(metadataController.Run), + libraryapplyconfiguration.AdaptRunFn(oauthClientsSwitchedInformer.Controller().Run), libraryapplyconfiguration.AdaptRunFn(oauthClientsController.Run), libraryapplyconfiguration.AdaptRunFn(payloadConfigController.Run), libraryapplyconfiguration.AdaptRunFn(routerCertsController.Run), @@ -410,6 +486,10 @@ func prepareOauthAPIServerOperator( } migrator := migrators.NewKubeStorageVersionMigrator(authOperatorInput.migrationClient, informerFactories.migrationInformer.Migration().V1alpha1(), authOperatorInput.kubeClient.Discovery()) + authLister := informerFactories.operatorConfigInformer.Config().V1().Authentications().Lister() + kasLister := informerFactories.operatorInformer.Operator().V1().KubeAPIServers().Lister() + kasConfigMapLister := informerFactories.kubeInformersForNamespaces.InformersFor("openshift-kube-apiserver").Core().V1().ConfigMaps().Lister() + authAPIServerWorkload := workload.NewOAuthAPIServerWorkload( authOperatorInput.authenticationOperatorClient, workloadcontroller.CountNodesFuncWrapper(informerFactories.kubeInformersForNamespaces.InformersFor("").Core().V1().Nodes().Lister()), @@ -418,6 +498,9 @@ func prepareOauthAPIServerOperator( os.Getenv("IMAGE_OAUTH_APISERVER"), os.Getenv("OPERATOR_IMAGE"), authOperatorInput.kubeClient, + authLister, + kasLister, + kasConfigMapLister, versionRecorder) infra, err := authOperatorInput.configClient.ConfigV1().Infrastructures().Get(ctx, "cluster", metav1.GetOptions{}) @@ -519,9 +602,7 @@ func prepareOauthAPIServerOperator( ).WithAPIServiceController( "openshift-apiserver", "openshift-oauth-apiserver", - func() ([]*apiregistrationv1.APIService, []*apiregistrationv1.APIService, error) { - return apiServices(), nil, nil - }, + apiServicesFuncWrapper(authLister, kasLister, kasConfigMapLister), informerFactories.apiregistrationInformers, authOperatorInput.apiregistrationv1Client.ApiregistrationV1(), informerFactories.kubeInformersForNamespaces, @@ -601,6 +682,9 @@ func prepareOauthAPIServerOperator( authOperatorInput.kubeClient.CoreV1(), authOperatorInput.kubeClient.CoreV1(), authOperatorInput.configClient.ConfigV1().Authentications(), + authLister, + kasLister, + kasConfigMapLister, authOperatorInput.authenticationOperatorClient, versionRecorder, eventRecorder, @@ -735,7 +819,7 @@ func apiServices() []*apiregistrationv1.APIService { Service: &apiregistrationv1.ServiceReference{ Namespace: "openshift-oauth-apiserver", Name: "api", - Port: utilpointer.Int32Ptr(443), + Port: ptr.To(int32(443)), }, GroupPriorityMinimum: 9900, VersionPriority: 15, @@ -753,7 +837,7 @@ func apiServices() []*apiregistrationv1.APIService { // nil if it fails to load. It is to be used for controllers that generally require a // cert bundle and not necessary the system trust store contents. func loadSystemCACertBundle() ([]byte, error) { - systemCABundle, err := ioutil.ReadFile("/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem") + systemCABundle, err := os.ReadFile("/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem") if err != nil { // this may fail route-health checks in proxy environments klog.Warningf("unable to read system CA from /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem: %v", err) @@ -801,3 +885,17 @@ func extractOperatorStatus(obj *unstructured.Unstructured, fieldManager string) } return &ret.Status.OperatorStatusApplyConfiguration, nil } + +func apiServicesFuncWrapper(authLister configv1listers.AuthenticationLister, kasLister operatorv1listers.KubeAPIServerLister, kasConfigMapLister corev1listers.ConfigMapLister) func() ([]*apiregistrationv1.APIService, []*apiregistrationv1.APIService, error) { + return func() ([]*apiregistrationv1.APIService, []*apiregistrationv1.APIService, error) { + apiServices := apiServices() + if oidcAvailable, err := common.ExternalOIDCConfigAvailable(authLister, kasLister, kasConfigMapLister); err != nil { + return nil, nil, err + } else if oidcAvailable { + // return apiServices as disabled + return nil, apiServices, nil + } + + return apiServices, nil, nil + } +} diff --git a/pkg/operator/workload/sync_openshift_oauth_apiserver.go b/pkg/operator/workload/sync_openshift_oauth_apiserver.go index b9b1738215..0aa7a99224 100644 --- a/pkg/operator/workload/sync_openshift_oauth_apiserver.go +++ b/pkg/operator/workload/sync_openshift_oauth_apiserver.go @@ -11,11 +11,16 @@ import ( "github.com/openshift/library-go/pkg/operator/v1helpers" appsv1 "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/kubernetes" + corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/klog/v2" operatorv1 "github.com/openshift/api/operator/v1" + configv1listers "github.com/openshift/client-go/config/listers/config/v1" + operatorv1listers "github.com/openshift/client-go/operator/listers/operator/v1" "github.com/openshift/library-go/pkg/controller/factory" libgoetcd "github.com/openshift/library-go/pkg/operator/configobserver/etcd" "github.com/openshift/library-go/pkg/operator/events" @@ -51,6 +56,10 @@ type OAuthAPIServerWorkload struct { operatorImagePullSpec string kubeClient kubernetes.Interface versionRecorder status.VersionGetter + + authLister configv1listers.AuthenticationLister + kasLister operatorv1listers.KubeAPIServerLister + kasConfigMapLister corev1listers.ConfigMapLister } // NewOAuthAPIServerWorkload creates new OAuthAPIServerWorkload struct @@ -62,6 +71,9 @@ func NewOAuthAPIServerWorkload( targetImagePullSpec string, operatorImagePullSpec string, kubeClient kubernetes.Interface, + authLister configv1listers.AuthenticationLister, + kasLister operatorv1listers.KubeAPIServerLister, + kasConfigMapLister corev1listers.ConfigMapLister, versionRecorder status.VersionGetter, ) *OAuthAPIServerWorkload { return &OAuthAPIServerWorkload{ @@ -72,6 +84,9 @@ func NewOAuthAPIServerWorkload( targetImagePullSpec: targetImagePullSpec, operatorImagePullSpec: operatorImagePullSpec, kubeClient: kubeClient, + authLister: authLister, + kasLister: kasLister, + kasConfigMapLister: kasConfigMapLister, versionRecorder: versionRecorder, } } @@ -117,41 +132,46 @@ func (c *OAuthAPIServerWorkload) preconditionFulfilledInternal(operatorSpec *ope } // Sync essentially manages OAuthAPI server. -func (c *OAuthAPIServerWorkload) Sync(ctx context.Context, syncCtx factory.SyncContext) (*appsv1.Deployment, bool, []error) { +func (c *OAuthAPIServerWorkload) Sync(ctx context.Context, syncCtx factory.SyncContext) (*appsv1.Deployment, bool, bool, string, string, []error) { errs := []error{} operatorSpec, operatorStatus, _, err := c.operatorClient.GetOperatorState() if err != nil { errs = append(errs, err) - return nil, false, errs + return nil, false, false, "", "", errs } - actualDeployment, err := c.syncDeployment(ctx, operatorSpec, operatorStatus, syncCtx.Recorder()) + actualDeployment, deploymentDeleted, err := c.syncDeployment(ctx, operatorSpec, operatorStatus, syncCtx.Recorder()) if err != nil { errs = append(errs, fmt.Errorf("%q: %v", "deployments", err)) } - return actualDeployment, true, errs + + if deploymentDeleted { + return nil, false, true, actualDeployment.Name, actualDeployment.Namespace, nil + } + + return actualDeployment, true, false, "", "", errs } -func (c *OAuthAPIServerWorkload) syncDeployment(ctx context.Context, operatorSpec *operatorv1.OperatorSpec, operatorStatus *operatorv1.OperatorStatus, eventRecorder events.Recorder) (*appsv1.Deployment, error) { +func (c *OAuthAPIServerWorkload) syncDeployment(ctx context.Context, operatorSpec *operatorv1.OperatorSpec, operatorStatus *operatorv1.OperatorStatus, eventRecorder events.Recorder) (*appsv1.Deployment, bool, error) { if operatorStatus.LatestAvailableRevision == 0 { // this a backstop during the migration from 4.17 whe this information is in .status.oauthAPIServer.latestAvailableRevision - return nil, fmt.Errorf(".status.latestAvailableRevision is not yet available") + return nil, false, fmt.Errorf(".status.latestAvailableRevision is not yet available") } tmpl, err := bindata.Asset("oauth-apiserver/deploy.yaml") if err != nil { - return nil, err + return nil, false, err } argsRaw, err := GetAPIServerArgumentsRaw(*operatorSpec) if err != nil { - return nil, err + return nil, false, err } args, err := arguments.Parse(argsRaw) if err != nil { - return nil, err + return nil, false, err } // log level verbosity is taken from the spec always @@ -167,11 +187,20 @@ func (c *OAuthAPIServerWorkload) syncDeployment(ctx context.Context, operatorSpe tmpl = []byte(r.Replace(string(tmpl))) re := regexp.MustCompile(`\$\{[^}]*}`) if match := re.Find(tmpl); len(match) > 0 && !excludedReferences.Has(string(match)) { - return nil, fmt.Errorf("invalid template reference %q", string(match)) + return nil, false, fmt.Errorf("invalid template reference %q", string(match)) } required := resourceread.ReadDeploymentV1OrDie(tmpl) + if oidcAvailable, err := common.ExternalOIDCConfigAvailable(c.authLister, c.kasLister, c.kasConfigMapLister); err != nil { + return nil, false, fmt.Errorf("error while checking OIDC config: %v", err) + } else if oidcAvailable { + if err := c.kubeClient.AppsV1().Deployments(required.Namespace).Delete(ctx, required.Name, metav1.DeleteOptions{}); err != nil && !errors.IsNotFound(err) { + return nil, false, fmt.Errorf("error while gracefully deleting deployment: %v", err) + } + return required, true, nil + } + // use the following routine for things that would require special formatting/padding (yaml) encodedArgs := arguments.EncodeWithDelimiter(args, " \\\n ") r = strings.NewReplacer( @@ -208,7 +237,7 @@ func (c *OAuthAPIServerWorkload) syncDeployment(ctx context.Context, operatorSpe resourcehash.NewObjectRef().ForConfigMap().InNamespace(c.targetNamespace).Named("trusted-ca-bundle"), ) if err != nil { - return nil, fmt.Errorf("invalid dependency reference: %q", err) + return nil, false, fmt.Errorf("invalid dependency reference: %q", err) } for k, v := range inputHashes { @@ -222,18 +251,18 @@ func (c *OAuthAPIServerWorkload) syncDeployment(ctx context.Context, operatorSpe err = c.ensureAtMostOnePodPerNode(&required.Spec, "oauth-apiserver") if err != nil { - return nil, fmt.Errorf("unable to ensure at most one pod per node: %v", err) + return nil, false, fmt.Errorf("unable to ensure at most one pod per node: %v", err) } // Set the replica count to the number of master nodes. masterNodeCount, err := c.countNodes(required.Spec.Template.Spec.NodeSelector) if err != nil { - return nil, fmt.Errorf("failed to determine number of master nodes: %v", err) + return nil, false, fmt.Errorf("failed to determine number of master nodes: %v", err) } required.Spec.Replicas = masterNodeCount deployment, _, err := resourceapply.ApplyDeployment(ctx, c.kubeClient.AppsV1(), eventRecorder, required, resourcemerge.ExpectedDeploymentGeneration(required, operatorStatus.Generations)) - return deployment, err + return deployment, false, err } func loglevelToKlog(logLevel operatorv1.LogLevel) string { diff --git a/pkg/operator/workload/sync_openshift_oauth_apiserver_test.go b/pkg/operator/workload/sync_openshift_oauth_apiserver_test.go index 715d45068e..e6a0da2409 100644 --- a/pkg/operator/workload/sync_openshift_oauth_apiserver_test.go +++ b/pkg/operator/workload/sync_openshift_oauth_apiserver_test.go @@ -10,16 +10,23 @@ import ( "github.com/google/go-cmp/cmp" + configv1 "github.com/openshift/api/config/v1" operatorv1 "github.com/openshift/api/operator/v1" + configv1listers "github.com/openshift/client-go/config/listers/config/v1" + operatorv1listers "github.com/openshift/client-go/operator/listers/operator/v1" "github.com/openshift/library-go/pkg/operator/events" + corev1 "k8s.io/api/core/v1" appsv1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/scheme" + corev1listers "k8s.io/client-go/listers/core/v1" clientgotesting "k8s.io/client-go/testing" + "k8s.io/client-go/tools/cache" clocktesting "k8s.io/utils/clock/testing" ) @@ -62,10 +69,13 @@ var unsupportedConfigOverridesAPIServerArgsJSON = ` func TestSyncOAuthAPIServerDeployment(t *testing.T) { scenarios := []struct { - name string - goldenFile string - operator *operatorv1.Authentication - expectedActions []string + name string + goldenFile string + operator *operatorv1.Authentication + listersFunc func() (configv1listers.AuthenticationLister, operatorv1listers.KubeAPIServerLister, corev1listers.ConfigMapLister) + deleteReactor func(action clientgotesting.Action) (handled bool, ret runtime.Object, err error) + expectWorkloadDeleted bool + expectedActions []string }{ // scenario 1 { @@ -79,6 +89,7 @@ func TestSyncOAuthAPIServerDeployment(t *testing.T) { }, }, }, + listersFunc: listersForOAuth, expectedActions: []string{ "get:secrets:etcd-client", "get:configmaps:etcd-serving-ca", @@ -102,6 +113,7 @@ func TestSyncOAuthAPIServerDeployment(t *testing.T) { }, }, }, + listersFunc: listersForOAuth, expectedActions: []string{ "get:secrets:etcd-client", "get:configmaps:etcd-serving-ca", @@ -126,6 +138,7 @@ func TestSyncOAuthAPIServerDeployment(t *testing.T) { }, }, }, + listersFunc: listersForOAuth, expectedActions: []string{ "get:secrets:etcd-client", "get:configmaps:etcd-serving-ca", @@ -134,6 +147,31 @@ func TestSyncOAuthAPIServerDeployment(t *testing.T) { "create:deployments:openshift-oauth-apiserver:apiserver", }, }, + + // scenario 4 + { + name: "deployment gracefully deleted when OIDC available", + goldenFile: "", + operator: &operatorv1.Authentication{ + Spec: operatorv1.AuthenticationSpec{OperatorSpec: operatorv1.OperatorSpec{ + ObservedConfig: runtime.RawExtension{Raw: []byte(customAPIServerArgsJSON)}, + UnsupportedConfigOverrides: runtime.RawExtension{Raw: []byte(unsupportedConfigOverridesAPIServerArgsJSON)}, + }}, + Status: operatorv1.AuthenticationStatus{ + OperatorStatus: operatorv1.OperatorStatus{ + LatestAvailableRevision: 1, + }, + }, + }, + listersFunc: listersForOIDC, + deleteReactor: func(action clientgotesting.Action) (handled bool, ret runtime.Object, err error) { + return true, nil, nil + }, + expectWorkloadDeleted: true, + expectedActions: []string{ + "delete:deployments:openshift-oauth-apiserver:apiserver", + }, + }, } for _, scenario := range scenarios { @@ -141,16 +179,27 @@ func TestSyncOAuthAPIServerDeployment(t *testing.T) { eventRecorder := events.NewInMemoryRecorder("", clocktesting.NewFakePassiveClock(time.Now())) fakeKubeClient := fake.NewSimpleClientset() + if scenario.deleteReactor != nil { + fakeKubeClient.PrependReactor("delete", "deployments", scenario.deleteReactor) + } + target := &OAuthAPIServerWorkload{ countNodes: func(nodeSelector map[string]string) (*int32, error) { var i int32; i = 1; return &i, nil }, ensureAtMostOnePodPerNode: func(spec *appsv1.DeploymentSpec, componentName string) error { return nil }, kubeClient: fakeKubeClient, } - actualDeployment, err := target.syncDeployment(context.TODO(), &scenario.operator.Spec.OperatorSpec, &scenario.operator.Status.OperatorStatus, eventRecorder) + if scenario.listersFunc != nil { + target.authLister, target.kasLister, target.kasConfigMapLister = scenario.listersFunc() + } + + actualDeployment, actualWorkloadDeleted, err := target.syncDeployment(context.TODO(), &scenario.operator.Spec.OperatorSpec, &scenario.operator.Status.OperatorStatus, eventRecorder) if err != nil { t.Fatal(err) } + if scenario.expectWorkloadDeleted != actualWorkloadDeleted { + t.Fatalf("expectWorkloadDeleted does not match actual: %t != %t", scenario.expectWorkloadDeleted, actualWorkloadDeleted) + } if err := validateActionsVerbs(fakeKubeClient.Actions(), scenario.expectedActions); err != nil { t.Fatal(err) } @@ -338,3 +387,71 @@ func readBytesFromFile(t *testing.T, filename string) []byte { return data } + +func listersForOAuth() (configv1listers.AuthenticationLister, operatorv1listers.KubeAPIServerLister, corev1listers.ConfigMapLister) { + authIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{}) + authIndexer.Add(&configv1.Authentication{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + Spec: configv1.AuthenticationSpec{ + Type: configv1.AuthenticationTypeIntegratedOAuth, + }, + }) + + return configv1listers.NewAuthenticationLister(authIndexer), + operatorv1listers.NewKubeAPIServerLister(cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})), + corev1listers.NewConfigMapLister(cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})) +} + +func listersForOIDC() (configv1listers.AuthenticationLister, operatorv1listers.KubeAPIServerLister, corev1listers.ConfigMapLister) { + authIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{}) + authIndexer.Add(&configv1.Authentication{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + Spec: configv1.AuthenticationSpec{ + Type: configv1.AuthenticationTypeOIDC, + }, + }) + + revision := int32(1) + kasIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{}) + kasIndexer.Add(&operatorv1.KubeAPIServer{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + }, + Status: operatorv1.KubeAPIServerStatus{ + StaticPodOperatorStatus: operatorv1.StaticPodOperatorStatus{ + NodeStatuses: []operatorv1.NodeStatus{ + { + CurrentRevision: revision, + }, + }, + }, + }, + }) + + cmIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{}) + cmIndexer.Add(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("auth-config-%d", revision), + Namespace: "openshift-kube-apiserver", + }, + }) + + cmIndexer.Add(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("config-%d", revision), + Namespace: "openshift-kube-apiserver", + }, + Data: map[string]string{ + "config.yaml": `{"oauthMetadataFile":"","apiServerArguments":{"authentication-config":["/etc/kubernetes/static-pod-resources/configmaps/auth-config/auth-config.json"]} + }`, + }, + }) + + return configv1listers.NewAuthenticationLister(authIndexer), + operatorv1listers.NewKubeAPIServerLister(kasIndexer), + corev1listers.NewConfigMapLister(cmIndexer) +} diff --git a/test/e2e-oidc/external_oidc_test.go b/test/e2e-oidc/external_oidc_test.go index 5ce34e7f8e..14297b09ba 100644 --- a/test/e2e-oidc/external_oidc_test.go +++ b/test/e2e-oidc/external_oidc_test.go @@ -79,10 +79,7 @@ func TestExternalOIDCWithKeycloak(t *testing.T) { err = test.WaitForNewKASRollout(t, testCtx, testClient.operatorConfigClient.OperatorV1().KubeAPIServers(), kasOriginalRevision) require.NoError(t, err, "failed to wait for KAS rollout during cleanup") - err = wait.PollUntilContextTimeout(testCtx, 30*time.Second, 5*time.Minute, false, func(ctx context.Context) (done bool, err error) { - return testClient.validateOAuthResources(testCtx, false) - }) - require.NoError(t, err, "failed to wait for OAuth resources to be present during cleanup") + testClient.validateOAuthResources(t, testCtx, false) }) // keycloak setup @@ -168,6 +165,8 @@ func TestExternalOIDCWithKeycloak(t *testing.T) { testClient.updateAuthResource(t, testCtx, testSpec, tt.specUpdate) require.NoError(t, test.WaitForClusterOperatorDegraded(t, testClient.configClient.ConfigV1(), "authentication")) + + testClient.validateOAuthResources(t, testCtx, false) }) } }) @@ -211,6 +210,8 @@ func TestExternalOIDCWithKeycloak(t *testing.T) { testClient.requireKASRolloutSuccessful(t, testCtx, &auth.Spec, kasOriginalRevision, tt.expectedPrefix) + testClient.validateOAuthResources(t, testCtx, true) + testClient.testOIDCAuthentication(t, testCtx, kcClient, tt.claim, tt.expectedPrefix, true) }) } @@ -259,6 +260,8 @@ func TestExternalOIDCWithKeycloak(t *testing.T) { testClient.requireKASRolloutSuccessful(t, testCtx, &auth.Spec, kasOriginalRevision, "") + testClient.validateOAuthResources(t, testCtx, true) + testClient.testOIDCAuthentication(t, testCtx, kcClient, "", "", false) }) } @@ -611,73 +614,76 @@ func (tc *testClient) validateAuthConfigJSON(t *testing.T, ctx context.Context, } } -func (tc *testClient) validateOAuthResources(ctx context.Context, requireMissing bool) (bool, error) { +func (tc *testClient) validateOAuthResources(t *testing.T, ctx context.Context, requireMissing bool) { dynamicClient, err := dynamic.NewForConfig(tc.kubeConfig) - if err != nil { - return false, fmt.Errorf("unexpected error while creating dynamic client: %v", err) - } + require.NoError(t, err, "unexpected error while creating dynamic client") - for _, obj := range []struct { - gvr schema.GroupVersionResource - namespace string - name string - }{ - {schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}, "openshift-authentication", "oauth-openshift"}, - {schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}, "openshift-oauth-apiserver", "apiserver"}, - {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}, "openshift-authentication", "v4-0-config-system-metadata"}, - {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}, "openshift-authentication", "v4-0-config-system-trusted-ca-bundle"}, - {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}, "openshift-authentication", "v4-0-config-system-service-ca"}, - {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}, "openshift-authentication", "v4-0-config-system-cliconfig"}, - {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}, "openshift-authentication", "v4-0-config-system-ocp-branding-template"}, - {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}, "openshift-authentication", "v4-0-config-system-session"}, - {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "services"}, "openshift-authentication", "oauth-openshift"}, - {schema.GroupVersionResource{Group: "apiregistration.k8s.io", Version: "v1", Resource: "apiservices"}, "", "v1.oauth.openshift.io"}, - {schema.GroupVersionResource{Group: "apiregistration.k8s.io", Version: "v1", Resource: "apiservices"}, "", "v1.user.openshift.io"}, - {schema.GroupVersionResource{Group: "oauth.openshift.io", Version: "v1", Resource: "oauthclients"}, "", "openshift-browser-client"}, - {schema.GroupVersionResource{Group: "oauth.openshift.io", Version: "v1", Resource: "oauthclients"}, "", "openshift-challenging-client"}, - {schema.GroupVersionResource{Group: "oauth.openshift.io", Version: "v1", Resource: "oauthclients"}, "", "openshift-cli-client"}, - } { - _, err := dynamicClient.Resource(obj.gvr).Namespace(obj.namespace).Get(ctx, obj.name, metav1.GetOptions{}) - if err != nil && !errors.IsNotFound(err) { - return false, fmt.Errorf("unexpected error while getting resource %s/%s: %v", obj.namespace, obj.name, err) - } else if requireMissing != errors.IsNotFound(err) { - return false, fmt.Errorf("resource %s/%s wanted missing: %v; got: %v", obj.namespace, obj.name, requireMissing, !errors.IsNotFound((err))) + err = wait.PollUntilContextTimeout(ctx, 30*time.Second, 5*time.Minute, false, func(ctx context.Context) (done bool, err error) { + for _, obj := range []struct { + gvr schema.GroupVersionResource + namespace string + name string + }{ + {schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}, "openshift-authentication", "oauth-openshift"}, + {schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}, "openshift-oauth-apiserver", "apiserver"}, + {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}, "openshift-authentication", "v4-0-config-system-metadata"}, + {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}, "openshift-authentication", "v4-0-config-system-trusted-ca-bundle"}, + {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}, "openshift-authentication", "v4-0-config-system-service-ca"}, + {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}, "openshift-authentication", "v4-0-config-system-cliconfig"}, + {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}, "openshift-config-managed", "oauth-serving-cert"}, + {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}, "openshift-authentication", "v4-0-config-system-ocp-branding-template"}, + {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}, "openshift-authentication", "v4-0-config-system-session"}, + {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}, "openshift-config", "webhook-authentication-integrated-oauth"}, + {schema.GroupVersionResource{Group: "", Version: "v1", Resource: "services"}, "openshift-authentication", "oauth-openshift"}, + {schema.GroupVersionResource{Group: "apiregistration.k8s.io", Version: "v1", Resource: "apiservices"}, "", "v1.oauth.openshift.io"}, + {schema.GroupVersionResource{Group: "apiregistration.k8s.io", Version: "v1", Resource: "apiservices"}, "", "v1.user.openshift.io"}, + {schema.GroupVersionResource{Group: "oauth.openshift.io", Version: "v1", Resource: "oauthclients"}, "", "openshift-browser-client"}, + {schema.GroupVersionResource{Group: "oauth.openshift.io", Version: "v1", Resource: "oauthclients"}, "", "openshift-challenging-client"}, + {schema.GroupVersionResource{Group: "oauth.openshift.io", Version: "v1", Resource: "oauthclients"}, "", "openshift-cli-client"}, + } { + _, err := dynamicClient.Resource(obj.gvr).Namespace(obj.namespace).Get(ctx, obj.name, metav1.GetOptions{}) + if err != nil && !errors.IsNotFound(err) { + return false, fmt.Errorf("unexpected error while getting resource %s/%s: %v", obj.namespace, obj.name, err) + } else if requireMissing != errors.IsNotFound(err) { + return false, fmt.Errorf("resource %s/%s wanted missing: %v; got: %v", obj.namespace, obj.name, requireMissing, !errors.IsNotFound((err))) + } } - } - // routes - for _, obj := range []struct{ namespace, name string }{ - {"openshift-authentication", "oauth-openshift"}, - } { - _, err := tc.routeClient.RouteV1().Routes(obj.namespace).Get(ctx, obj.name, metav1.GetOptions{}) - if err != nil && !errors.IsNotFound(err) { - return false, fmt.Errorf("unexpected error while getting route %s/%s: %v", obj.namespace, obj.name, err) - } else if requireMissing != errors.IsNotFound(err) { - return false, fmt.Errorf("route %s/%s wanted missing: %v; got: %v", obj.namespace, obj.name, requireMissing, !errors.IsNotFound((err))) - } + // routes + for _, obj := range []struct{ namespace, name string }{ + {"openshift-authentication", "oauth-openshift"}, + } { + _, err := tc.routeClient.RouteV1().Routes(obj.namespace).Get(ctx, obj.name, metav1.GetOptions{}) + if err != nil && !errors.IsNotFound(err) { + return false, fmt.Errorf("unexpected error while getting route %s/%s: %v", obj.namespace, obj.name, err) + } else if requireMissing != errors.IsNotFound(err) { + return false, fmt.Errorf("route %s/%s wanted missing: %v; got: %v", obj.namespace, obj.name, requireMissing, !errors.IsNotFound((err))) + } - // ingress status - ingress, err := tc.configClient.ConfigV1().Ingresses().Get(ctx, "cluster", metav1.GetOptions{}) - if err != nil { - return false, err - } + // ingress status + ingress, err := tc.configClient.ConfigV1().Ingresses().Get(ctx, "cluster", metav1.GetOptions{}) + if err != nil { + return false, err + } - found := false - for _, route := range ingress.Status.ComponentRoutes { - if route.Name == obj.name && route.Namespace == obj.namespace { - found = true - break + found := false + for _, route := range ingress.Status.ComponentRoutes { + if route.Name == obj.name && route.Namespace == obj.namespace { + found = true + break + } } - } - if !requireMissing && !found { - return false, fmt.Errorf("route %s required but was not found", obj) - } else if requireMissing && found { - return false, fmt.Errorf("route %s required to be missing but was found", obj) + if !requireMissing && !found { + return false, fmt.Errorf("route %s required but was not found", obj) + } else if requireMissing && found { + return false, fmt.Errorf("route %s required to be missing but was found", obj) + } } - } - return true, nil + return true, nil + }) + require.NoError(t, err, "failed to wait for OAuth resources to be present during cleanup") } func (tc *testClient) testOIDCAuthentication(t *testing.T, ctx context.Context, kcClient *test.KeycloakClient, usernameClaim, usernamePrefix string, expectAuthSuccess bool) { diff --git a/vendor/github.com/openshift/api/.golangci.yaml b/vendor/github.com/openshift/api/.golangci.yaml index 19746532ca..848960e946 100644 --- a/vendor/github.com/openshift/api/.golangci.yaml +++ b/vendor/github.com/openshift/api/.golangci.yaml @@ -13,6 +13,7 @@ linters-settings: conditions: isFirstField: Warn useProtobuf: Ignore + usePatchStrategy: Ignore linters: disable-all: true enable: diff --git a/vendor/github.com/openshift/api/OWNERS b/vendor/github.com/openshift/api/OWNERS index 2e956a47dd..ff904b63a3 100644 --- a/vendor/github.com/openshift/api/OWNERS +++ b/vendor/github.com/openshift/api/OWNERS @@ -1,19 +1,7 @@ reviewers: - deads2k - - derekwaynecarr - JoelSpeed - - knobunc - - sjenning - - mfojtik - - soltysh - - bparees + - everettraven approvers: - - bparees - deads2k - - derekwaynecarr - JoelSpeed - - knobunc - - mfojtik - - sjenning - - soltysh - - spadgett diff --git a/vendor/github.com/openshift/api/apiserver/v1/types_apirequestcount.go b/vendor/github.com/openshift/api/apiserver/v1/types_apirequestcount.go index 645d796f77..3771fa21d0 100644 --- a/vendor/github.com/openshift/api/apiserver/v1/types_apirequestcount.go +++ b/vendor/github.com/openshift/api/apiserver/v1/types_apirequestcount.go @@ -57,9 +57,10 @@ type APIRequestCountSpec struct { type APIRequestCountStatus struct { // conditions contains details of the current status of this API Resource. - // +patchMergeKey=type - // +patchStrategy=merge - Conditions []metav1.Condition `json:"conditions" patchStrategy:"merge" patchMergeKey:"type"` + // +listType=map + // +listMapKey=type + // +optional + Conditions []metav1.Condition `json:"conditions"` // removedInRelease is when the API will be removed. // +kubebuilder:validation:MinLength=0 diff --git a/vendor/github.com/openshift/api/cloudnetwork/v1/generated.proto b/vendor/github.com/openshift/api/cloudnetwork/v1/generated.proto index 328de7c5a2..aee82514e0 100644 --- a/vendor/github.com/openshift/api/cloudnetwork/v1/generated.proto +++ b/vendor/github.com/openshift/api/cloudnetwork/v1/generated.proto @@ -79,6 +79,10 @@ message CloudPrivateIPConfigStatus { // condition is the assignment condition of the private IP and its status // +required + // +listType=map + // +listMapKey=type + // +patchMergeKey=type + // +patchStrategy=merge repeated .k8s.io.apimachinery.pkg.apis.meta.v1.Condition conditions = 2; } diff --git a/vendor/github.com/openshift/api/cloudnetwork/v1/types.go b/vendor/github.com/openshift/api/cloudnetwork/v1/types.go index de27f8eb60..7508e15053 100644 --- a/vendor/github.com/openshift/api/cloudnetwork/v1/types.go +++ b/vendor/github.com/openshift/api/cloudnetwork/v1/types.go @@ -56,7 +56,11 @@ type CloudPrivateIPConfigStatus struct { Node string `json:"node" protobuf:"bytes,1,opt,name=node"` // condition is the assignment condition of the private IP and its status // +required - Conditions []metav1.Condition `json:"conditions" protobuf:"bytes,2,rep,name=conditions"` + // +listType=map + // +listMapKey=type + // +patchMergeKey=type + // +patchStrategy=merge + Conditions []metav1.Condition `json:"conditions" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,2,rep,name=conditions"` } // CloudPrivateIPConfigConditionType specifies the current condition type of the CloudPrivateIPConfig diff --git a/vendor/github.com/openshift/api/config/v1/types_authentication.go b/vendor/github.com/openshift/api/config/v1/types_authentication.go index 65dffddb00..a2af4d6544 100644 --- a/vendor/github.com/openshift/api/config/v1/types_authentication.go +++ b/vendor/github.com/openshift/api/config/v1/types_authentication.go @@ -343,6 +343,7 @@ type OIDCClientStatus struct { // // +listType=map // +listMapKey=type + // +optional Conditions []metav1.Condition `json:"conditions,omitempty"` } diff --git a/vendor/github.com/openshift/api/config/v1/types_cluster_version.go b/vendor/github.com/openshift/api/config/v1/types_cluster_version.go index 70f1649876..092bebff09 100644 --- a/vendor/github.com/openshift/api/config/v1/types_cluster_version.go +++ b/vendor/github.com/openshift/api/config/v1/types_cluster_version.go @@ -796,11 +796,10 @@ type ConditionalUpdate struct { // conditions represents the observations of the conditional update's // current status. Known types are: // * Recommended, for whether the update is recommended for the current cluster. - // +patchMergeKey=type - // +patchStrategy=merge // +listType=map // +listMapKey=type - Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` } // ConditionalUpdateRisk represents a reason and cluster-state diff --git a/vendor/github.com/openshift/api/config/v1/types_feature.go b/vendor/github.com/openshift/api/config/v1/types_feature.go index 81bc14f2c7..0709a75ae8 100644 --- a/vendor/github.com/openshift/api/config/v1/types_feature.go +++ b/vendor/github.com/openshift/api/config/v1/types_feature.go @@ -99,6 +99,7 @@ type FeatureGateStatus struct { // Known .status.conditions.type are: "DeterminationDegraded" // +listType=map // +listMapKey=type + // +optional Conditions []metav1.Condition `json:"conditions,omitempty"` // featureGates contains a list of enabled and disabled featureGates that are keyed by payloadVersion. diff --git a/vendor/github.com/openshift/api/config/v1/types_network.go b/vendor/github.com/openshift/api/config/v1/types_network.go index 95e55a7ffc..41dc2eb97b 100644 --- a/vendor/github.com/openshift/api/config/v1/types_network.go +++ b/vendor/github.com/openshift/api/config/v1/types_network.go @@ -112,12 +112,10 @@ type NetworkStatus struct { // conditions represents the observations of a network.config current state. // Known .status.conditions.type are: "NetworkDiagnosticsAvailable" // +optional - // +patchMergeKey=type - // +patchStrategy=merge // +listType=map // +listMapKey=type // +openshift:enable:FeatureGate=NetworkDiagnosticsConfig - Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + Conditions []metav1.Condition `json:"conditions,omitempty"` } // ClusterNetworkEntry is a contiguous block of IP addresses from which pod IPs diff --git a/vendor/github.com/openshift/api/config/v1/types_node.go b/vendor/github.com/openshift/api/config/v1/types_node.go index 3fc7bc0c39..3977f9f14b 100644 --- a/vendor/github.com/openshift/api/config/v1/types_node.go +++ b/vendor/github.com/openshift/api/config/v1/types_node.go @@ -68,12 +68,10 @@ type NodeSpec struct { type NodeStatus struct { // conditions contain the details and the current state of the nodes.config object - // +patchMergeKey=type - // +patchStrategy=merge // +listType=map // +listMapKey=type // +optional - Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + Conditions []metav1.Condition `json:"conditions,omitempty"` } // +kubebuilder:validation:Enum=v1;v2;"" diff --git a/vendor/github.com/openshift/api/config/v1alpha1/types_cluster_image_policy.go b/vendor/github.com/openshift/api/config/v1alpha1/types_cluster_image_policy.go index 5eaeeea736..107b9e29a4 100644 --- a/vendor/github.com/openshift/api/config/v1alpha1/types_cluster_image_policy.go +++ b/vendor/github.com/openshift/api/config/v1alpha1/types_cluster_image_policy.go @@ -59,6 +59,7 @@ type ClusterImagePolicyStatus struct { // conditions provide details on the status of this API Resource. // +listType=map // +listMapKey=type + // +optional Conditions []metav1.Condition `json:"conditions,omitempty"` } diff --git a/vendor/github.com/openshift/api/features.md b/vendor/github.com/openshift/api/features.md index 4b9d8bcadf..2ebfc0b829 100644 --- a/vendor/github.com/openshift/api/features.md +++ b/vendor/github.com/openshift/api/features.md @@ -11,6 +11,7 @@ | NewOLMCatalogdAPIV1Metas| | | | Enabled | | Enabled | | SELinuxChangePolicy| | | Enabled | Enabled | | | | SELinuxMount| | | Enabled | Enabled | | | +| ShortCertRotation| | | Enabled | Enabled | | | | SigstoreImageVerificationPKI| | | Enabled | Enabled | | | | NewOLM| | Enabled | | Enabled | | Enabled | | AWSClusterHostedDNS| | | Enabled | Enabled | Enabled | Enabled | @@ -59,6 +60,7 @@ | UpgradeStatus| | | Enabled | Enabled | Enabled | Enabled | | UserNamespacesPodSecurityStandards| | | Enabled | Enabled | Enabled | Enabled | | UserNamespacesSupport| | | Enabled | Enabled | Enabled | Enabled | +| VSphereConfigurableMaxAllowedBlockVolumesPerNode| | | Enabled | Enabled | Enabled | Enabled | | VSphereHostVMGroupZonal| | | Enabled | Enabled | Enabled | Enabled | | VSphereMultiDisk| | | Enabled | Enabled | Enabled | Enabled | | VSphereMultiNetworks| | | Enabled | Enabled | Enabled | Enabled | diff --git a/vendor/github.com/openshift/api/features/features.go b/vendor/github.com/openshift/api/features/features.go index 6b360316f8..8f30373e92 100644 --- a/vendor/github.com/openshift/api/features/features.go +++ b/vendor/github.com/openshift/api/features/features.go @@ -809,4 +809,20 @@ var ( enhancementPR("https://github.com/openshift/enhancements/pull/1756"). enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). mustRegister() + + FeatureShortCertRotation = newFeatureGate("ShortCertRotation"). + reportProblemsToJiraComponent("kube-apiserver"). + contactPerson("vrutkovs"). + productScope(ocpSpecific). + enableIn(configv1.DevPreviewNoUpgrade). + enhancementPR("https://github.com/openshift/enhancements/pull/1670"). + mustRegister() + + FeatureGateVSphereConfigurableMaxAllowedBlockVolumesPerNode = newFeatureGate("VSphereConfigurableMaxAllowedBlockVolumesPerNode"). + reportProblemsToJiraComponent("Storage / Kubernetes External Components"). + contactPerson("rbednar"). + productScope(ocpSpecific). + enhancementPR("https://github.com/openshift/enhancements/pull/1748"). + enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + mustRegister() ) diff --git a/vendor/github.com/openshift/api/helm/v1beta1/types_helm_chart_repository.go b/vendor/github.com/openshift/api/helm/v1beta1/types_helm_chart_repository.go index 4700f91140..793cb1938f 100644 --- a/vendor/github.com/openshift/api/helm/v1beta1/types_helm_chart_repository.go +++ b/vendor/github.com/openshift/api/helm/v1beta1/types_helm_chart_repository.go @@ -99,5 +99,7 @@ type HelmChartRepositoryStatus struct { // conditions is a list of conditions and their statuses // +optional + // +listType=map + // +listMapKey=type Conditions []metav1.Condition `json:"conditions,omitempty"` } diff --git a/vendor/github.com/openshift/api/machine/v1/types_alibabaprovider.go b/vendor/github.com/openshift/api/machine/v1/types_alibabaprovider.go index d1396fbfb2..12a8196726 100644 --- a/vendor/github.com/openshift/api/machine/v1/types_alibabaprovider.go +++ b/vendor/github.com/openshift/api/machine/v1/types_alibabaprovider.go @@ -224,6 +224,8 @@ type AlibabaCloudMachineProviderStatus struct { // conditions is a set of conditions associated with the Machine to indicate // errors or other status // +optional + // +listType=map + // +listMapKey=type Conditions []metav1.Condition `json:"conditions,omitempty"` } diff --git a/vendor/github.com/openshift/api/machine/v1/types_controlplanemachineset.go b/vendor/github.com/openshift/api/machine/v1/types_controlplanemachineset.go index cc9c04ca27..ead8b20771 100644 --- a/vendor/github.com/openshift/api/machine/v1/types_controlplanemachineset.go +++ b/vendor/github.com/openshift/api/machine/v1/types_controlplanemachineset.go @@ -428,12 +428,10 @@ type RootVolume struct { type ControlPlaneMachineSetStatus struct { // conditions represents the observations of the ControlPlaneMachineSet's current state. // Known .status.conditions.type are: Available, Degraded and Progressing. - // +patchMergeKey=type - // +patchStrategy=merge // +listType=map // +listMapKey=type // +optional - Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + Conditions []metav1.Condition `json:"conditions,omitempty"` // observedGeneration is the most recent generation observed for this // ControlPlaneMachineSet. It corresponds to the ControlPlaneMachineSets's generation, diff --git a/vendor/github.com/openshift/api/machine/v1/types_nutanixprovider.go b/vendor/github.com/openshift/api/machine/v1/types_nutanixprovider.go index cc1a355b53..e2ddde2ad7 100644 --- a/vendor/github.com/openshift/api/machine/v1/types_nutanixprovider.go +++ b/vendor/github.com/openshift/api/machine/v1/types_nutanixprovider.go @@ -331,6 +331,8 @@ type NutanixMachineProviderStatus struct { // conditions is a set of conditions associated with the Machine to indicate // errors or other status // +optional + // +listType=map + // +listMapKey=type Conditions []metav1.Condition `json:"conditions,omitempty"` // vmUUID is the Machine associated VM's UUID diff --git a/vendor/github.com/openshift/api/machine/v1/types_powervsprovider.go b/vendor/github.com/openshift/api/machine/v1/types_powervsprovider.go index b676a8d5f7..d3a4c6ec82 100644 --- a/vendor/github.com/openshift/api/machine/v1/types_powervsprovider.go +++ b/vendor/github.com/openshift/api/machine/v1/types_powervsprovider.go @@ -170,12 +170,10 @@ type PowerVSMachineProviderStatus struct { // conditions is a set of conditions associated with the Machine to indicate // errors or other status - // +patchMergeKey=type - // +patchStrategy=merge // +listType=map // +listMapKey=type // +optional - Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + Conditions []metav1.Condition `json:"conditions,omitempty"` // instanceId is the instance ID of the machine created in PowerVS // instanceId uniquely identifies a Power VS server instance(VM) under a Power VS service. diff --git a/vendor/github.com/openshift/api/machine/v1beta1/types_awsprovider.go b/vendor/github.com/openshift/api/machine/v1beta1/types_awsprovider.go index 4291b9e802..d69bcd0233 100644 --- a/vendor/github.com/openshift/api/machine/v1beta1/types_awsprovider.go +++ b/vendor/github.com/openshift/api/machine/v1beta1/types_awsprovider.go @@ -330,6 +330,8 @@ type AWSMachineProviderStatus struct { // conditions is a set of conditions associated with the Machine to indicate // errors or other status // +optional + // +listType=map + // +listMapKey=type Conditions []metav1.Condition `json:"conditions,omitempty"` } diff --git a/vendor/github.com/openshift/api/machine/v1beta1/types_azureprovider.go b/vendor/github.com/openshift/api/machine/v1beta1/types_azureprovider.go index db84fa2c9f..760360bd57 100644 --- a/vendor/github.com/openshift/api/machine/v1beta1/types_azureprovider.go +++ b/vendor/github.com/openshift/api/machine/v1beta1/types_azureprovider.go @@ -234,6 +234,8 @@ type AzureMachineProviderStatus struct { // conditions is a set of conditions associated with the Machine to indicate // errors or other status. // +optional + // +listType=map + // +listMapKey=type Conditions []metav1.Condition `json:"conditions,omitempty"` } diff --git a/vendor/github.com/openshift/api/machine/v1beta1/types_gcpprovider.go b/vendor/github.com/openshift/api/machine/v1beta1/types_gcpprovider.go index e554f466de..72a31b5bdd 100644 --- a/vendor/github.com/openshift/api/machine/v1beta1/types_gcpprovider.go +++ b/vendor/github.com/openshift/api/machine/v1beta1/types_gcpprovider.go @@ -313,6 +313,8 @@ type GCPMachineProviderStatus struct { // conditions is a set of conditions associated with the Machine to indicate // errors or other status // +optional + // +listType=map + // +listMapKey=type Conditions []metav1.Condition `json:"conditions,omitempty"` } diff --git a/vendor/github.com/openshift/api/machine/v1beta1/types_vsphereprovider.go b/vendor/github.com/openshift/api/machine/v1beta1/types_vsphereprovider.go index 43934c85da..fe6626f729 100644 --- a/vendor/github.com/openshift/api/machine/v1beta1/types_vsphereprovider.go +++ b/vendor/github.com/openshift/api/machine/v1beta1/types_vsphereprovider.go @@ -265,6 +265,9 @@ type VSphereMachineProviderStatus struct { InstanceState *string `json:"instanceState,omitempty"` // conditions is a set of conditions associated with the Machine to indicate // errors or other status + // +listType=map + // +listMapKey=type + // +optional Conditions []metav1.Condition `json:"conditions,omitempty"` // taskRef is a managed object reference to a Task related to the machine. // This value is set automatically at runtime and should not be set or diff --git a/vendor/github.com/openshift/api/monitoring/v1/types.go b/vendor/github.com/openshift/api/monitoring/v1/types.go index fc650d9616..faa250ed32 100644 --- a/vendor/github.com/openshift/api/monitoring/v1/types.go +++ b/vendor/github.com/openshift/api/monitoring/v1/types.go @@ -264,6 +264,8 @@ type AlertRelabelConfigStatus struct { // empty. // // +optional + // +listType=map + // +listMapKey=type Conditions []metav1.Condition `json:"conditions,omitempty"` } diff --git a/vendor/github.com/openshift/api/networkoperator/v1/generated.proto b/vendor/github.com/openshift/api/networkoperator/v1/generated.proto index ebf09e7e9a..1999f71e8d 100644 --- a/vendor/github.com/openshift/api/networkoperator/v1/generated.proto +++ b/vendor/github.com/openshift/api/networkoperator/v1/generated.proto @@ -110,6 +110,8 @@ message EgressRouterSpec { message EgressRouterStatus { // Observed status of the egress router // +required + // +listType=map + // +listMapKey=type repeated EgressRouterStatusCondition conditions = 1; } diff --git a/vendor/github.com/openshift/api/networkoperator/v1/types_egressrouter.go b/vendor/github.com/openshift/api/networkoperator/v1/types_egressrouter.go index 87e279eda8..541c3b5597 100644 --- a/vendor/github.com/openshift/api/networkoperator/v1/types_egressrouter.go +++ b/vendor/github.com/openshift/api/networkoperator/v1/types_egressrouter.go @@ -247,6 +247,8 @@ type EgressRouterStatusCondition struct { type EgressRouterStatus struct { // Observed status of the egress router // +required + // +listType=map + // +listMapKey=type Conditions []EgressRouterStatusCondition `json:"conditions,omitempty" protobuf:"bytes,1,rep,name=conditions"` } diff --git a/vendor/github.com/openshift/api/operator/v1/types_console.go b/vendor/github.com/openshift/api/operator/v1/types_console.go index 68d9daa450..c2f25e4e64 100644 --- a/vendor/github.com/openshift/api/operator/v1/types_console.go +++ b/vendor/github.com/openshift/api/operator/v1/types_console.go @@ -143,8 +143,141 @@ type Capability struct { Visibility CapabilityVisibility `json:"visibility"` } +// ThemeMode is the value of the logo theme mode that determines the theme mode in the console UI. +// +kubebuilder:validation:Enum="Dark";"Light" +// +enum +type ThemeMode string + +// ThemeMode values +const ( + // ThemeModeDark represents the dark mode for a console theme. + ThemeModeDark ThemeMode = "Dark" + + // ThemeModeLight represents the light mode for a console theme. + ThemeModeLight ThemeMode = "Light" +) + +// LogoType is the value of the logo type that determines if the logo is for the masthead or the favicon in the console UI. +// The masthead logo is displayed in the masthead and about modal of the console UI. +// +kubebuilder:validation:Enum="Masthead";"Favicon" +// +enum +type LogoType string + +const ( + // Masthead represents the logo in the masthead. + LogoTypeMasthead LogoType = "Masthead" + + // Favicon represents the favicon logo. + LogoTypeFavicon LogoType = "Favicon" +) + +// SourceType defines the source type of the file reference. +// +kubebuilder:validation:Enum="ConfigMap" +// +enum +type SourceType string + +const ( + // SourceTypeConfigMap represents a ConfigMap source. + SourceTypeConfigMap SourceType = "ConfigMap" +) + +// ConfigMapFileReference references a specific file within a ConfigMap. +type ConfigMapFileReference struct { + // name is the name of the ConfigMap. + // name is a required field. + // Must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character. + // Must be at most 253 characters in length. + // +kubebuilder:validation:MaxLength=253 + // +kubebuilder:validation:XValidation:rule="!format.dns1123Subdomain().validate(self).hasValue()",message="a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character." + // +required + Name string `json:"name"` + + // key is the logo key inside the referenced ConfigMap. + // Must consist only of alphanumeric characters, dashes (-), underscores (_), and periods (.). + // Must be at most 253 characters in length. + // Must end in a valid file extension. + // A valid file extension must consist of a period followed by 2 to 5 alpha characters. + // +kubebuilder:validation:MaxLength=253 + // +kubebuilder:validation:XValidation:rule="self.matches('^[a-zA-Z0-9._-]+$')",message="The ConfigMap key must consist only of alphanumeric characters, dashes (-), underscores (_), and periods (.)." + // +kubebuilder:validation:XValidation:rule="self.matches('.*\\\\.[a-zA-Z]{2,5}$')",message="The ConfigMap key must end with a valid file extension (2 to 5 letters)." + // +required + Key string `json:"key"` +} +// FileReferenceSource is used by the console to locate the specified file containing a custom logo. +// +kubebuilder:validation:XValidation:rule="has(self.from) && self.from == 'ConfigMap' ? has(self.configMap) : !has(self.configMap)",message="configMap is required when from is 'ConfigMap', and forbidden otherwise." +type FileReferenceSource struct { + // from is a required field to specify the source type of the file reference. + // Allowed values are ConfigMap. + // When set to ConfigMap, the file will be sourced from a ConfigMap in the openshift-config namespace. The configMap field must be set when from is set to ConfigMap. + // +required + From SourceType `json:"from"` + + // configMap specifies the ConfigMap sourcing details such as the name of the ConfigMap and the key for the file. + // The ConfigMap must exist in the openshift-config namespace. + // Required when from is "ConfigMap", and forbidden otherwise. + // +optional + ConfigMap *ConfigMapFileReference `json:"configMap"` +} + +// Theme defines a theme mode for the console UI. +type Theme struct { + // mode is used to specify what theme mode a logo will apply to in the console UI. + // mode is a required field that allows values of Dark and Light. + // When set to Dark, the logo file referenced in the 'file' field will be used when an end-user of the console UI enables the Dark mode. + // When set to Light, the logo file referenced in the 'file' field will be used when an end-user of the console UI enables the Light mode. + // +required + Mode ThemeMode `json:"mode"` + + // source is used by the console to locate the specified file containing a custom logo. + // source is a required field that references a ConfigMap name and key that contains the custom logo file in the openshift-config namespace. + // You can create it with a command like: + // - 'oc create configmap custom-logos-config --namespace=openshift-config --from-file=/path/to/file' + // The ConfigMap key must include the file extension so that the console serves the file with the correct MIME type. + // The recommended file format for the Masthead and Favicon logos is SVG, but other file formats are allowed if supported by the browser. + // The logo image size must be less than 1 MB due to constraints on the ConfigMap size. + // For more information, see the documentation: https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/web_console/customizing-web-console#customizing-web-console + // +required + Source FileReferenceSource `json:"source"` +} + +// Logo defines a configuration based on theme modes for the console UI logo. +type Logo struct { + // type specifies the type of the logo for the console UI. It determines whether the logo is for the masthead or favicon. + // type is a required field that allows values of Masthead and Favicon. + // When set to "Masthead", the logo will be used in the masthead and about modal of the console UI. + // When set to "Favicon", the logo will be used as the favicon of the console UI. + // +required + Type LogoType `json:"type"` + + // themes specifies the themes for the console UI logo. + // themes is a required field that allows a list of themes. Each item in the themes list must have a unique mode and a source field. + // Each mode determines whether the logo is for the dark or light mode of the console UI. + // If a theme is not specified, the default OpenShift logo will be displayed for that theme. + // There must be at least one entry and no more than 2 entries. + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=2 + // +listType=map + // +listMapKey=mode + // +required + Themes []Theme `json:"themes"` +} + // ConsoleCustomization defines a list of optional configuration for the console UI. +// Ensure that Logos and CustomLogoFile cannot be set at the same time. +// +kubebuilder:validation:XValidation:rule="!(has(self.logos) && has(self.customLogoFile))",message="Only one of logos or customLogoFile can be set." type ConsoleCustomization struct { + // logos is used to replace the OpenShift Masthead and Favicon logos in the console UI with custom logos. + // logos is an optional field that allows a list of logos. + // Only one of logos or customLogoFile can be set at a time. + // If logos is set, customLogoFile must be unset. + // When specified, there must be at least one entry and no more than 2 entries. + // Each type must appear only once in the list. + // +kubebuilder:validation:MaxItems=2 + // +listType=map + // +listMapKey=type + // +optional + Logos []Logo `json:"logos"` + // capabilities defines an array of capabilities that can be interacted with in the console UI. // Each capability defines a visual state that can be interacted with the console to render in the UI. // Available capabilities are LightspeedButton and GettingStartedBanner. @@ -172,14 +305,14 @@ type ConsoleCustomization struct { // +optional CustomProductName string `json:"customProductName,omitempty"` // customLogoFile replaces the default OpenShift logo in the masthead and about dialog. It is a reference to a + // Only one of customLogoFile or logos can be set at a time. // ConfigMap in the openshift-config namespace. This can be created with a command like // 'oc create configmap custom-logo --from-file=/path/to/file -n openshift-config'. // Image size must be less than 1 MB due to constraints on the ConfigMap size. // The ConfigMap key should include a file extension so that the console serves the file // with the correct MIME type. - // Recommended logo specifications: - // Dimensions: Max height of 68px and max width of 200px - // SVG format preferred + // The recommended file format for the logo is SVG, but other file formats are allowed if supported by the browser. + // Deprecated: Use logos instead. // +optional CustomLogoFile configv1.ConfigMapFileReference `json:"customLogoFile,omitempty"` // developerCatalog allows to configure the shown developer catalog categories (filters) and types (sub-catalogs). diff --git a/vendor/github.com/openshift/api/operator/v1/types_csi_cluster_driver.go b/vendor/github.com/openshift/api/operator/v1/types_csi_cluster_driver.go index 731323750a..b25133a42f 100644 --- a/vendor/github.com/openshift/api/operator/v1/types_csi_cluster_driver.go +++ b/vendor/github.com/openshift/api/operator/v1/types_csi_cluster_driver.go @@ -369,6 +369,21 @@ type VSphereCSIDriverConfigSpec struct { // +openshift:enable:FeatureGate=VSphereDriverConfiguration // +optional GranularMaxSnapshotsPerBlockVolumeInVVOL *uint32 `json:"granularMaxSnapshotsPerBlockVolumeInVVOL,omitempty"` + + // maxAllowedBlockVolumesPerNode is an optional configuration parameter that allows setting a custom value for the + // limit of the number of PersistentVolumes attached to a node. In vSphere version 7 this limit was set to 59 by + // default, however in vSphere version 8 this limit was increased to 255. + // Before increasing this value above 59 the cluster administrator needs to ensure that every node forming the + // cluster is updated to ESXi version 8 or higher and that all nodes are running the same version. + // The limit must be between 1 and 255, which matches the vSphere version 8 maximum. + // When omitted, this means no opinion and the platform is left to choose a reasonable default, which is subject to + // change over time. + // The current default is 59, which matches the limit for vSphere version 7. + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:Maximum=255 + // +openshift:enable:FeatureGate=VSphereConfigurableMaxAllowedBlockVolumesPerNode + // +optional + MaxAllowedBlockVolumesPerNode int32 `json:"maxAllowedBlockVolumesPerNode,omitempty"` } // ClusterCSIDriverStatus is the observed status of CSI driver operator diff --git a/vendor/github.com/openshift/api/operator/v1/types_machineconfiguration.go b/vendor/github.com/openshift/api/operator/v1/types_machineconfiguration.go index 88b89f8188..4c53734d86 100644 --- a/vendor/github.com/openshift/api/operator/v1/types_machineconfiguration.go +++ b/vendor/github.com/openshift/api/operator/v1/types_machineconfiguration.go @@ -41,8 +41,10 @@ type MachineConfigurationSpec struct { // managedBootImages allows configuration for the management of boot images for machine // resources within the cluster. This configuration allows users to select resources that should // be updated to the latest boot images during cluster upgrades, ensuring that new machines - // always boot with the current cluster version's boot image. When omitted, no boot images - // will be updated. + // always boot with the current cluster version's boot image. When omitted, this means no opinion + // and the platform is left to choose a reasonable default, which is subject to change over time. + // The default for each machine manager mode is All for GCP and AWS platforms, and None for all + // other platforms. // +openshift:enable:FeatureGate=ManagedBootImages // +optional ManagedBootImages ManagedBootImages `json:"managedBootImages"` @@ -62,11 +64,10 @@ type MachineConfigurationStatus struct { ObservedGeneration int64 `json:"observedGeneration,omitempty"` // conditions is a list of conditions and their status - // +patchMergeKey=type - // +patchStrategy=merge // +listType=map // +listMapKey=type - Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` // Previously there was a StaticPodOperatorStatus here for legacy reasons. Many of the fields within // it are no longer relevant for the MachineConfiguration CRD's functions. The following remainder @@ -96,6 +97,12 @@ type MachineConfigurationStatus struct { // +openshift:enable:FeatureGate=NodeDisruptionPolicy // +optional NodeDisruptionPolicyStatus NodeDisruptionPolicyStatus `json:"nodeDisruptionPolicyStatus"` + + // managedBootImagesStatus reflects what the latest cluster-validated boot image configuration is + // and will be used by Machine Config Controller while performing boot image updates. + // +openshift:enable:FeatureGate=ManagedBootImages + // +optional + ManagedBootImagesStatus ManagedBootImages `json:"managedBootImagesStatus"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -122,6 +129,7 @@ type ManagedBootImages struct { // +listType=map // +listMapKey=resource // +listMapKey=apiGroup + // +kubebuilder:validation:MaxItems=5 MachineManagers []MachineManager `json:"machineManagers"` } @@ -152,6 +160,7 @@ type MachineManagerSelector struct { // Valid values are All and Partial. // All means that every resource matched by the machine manager will be updated. // Partial requires specified selector(s) and allows customisation of which resources matched by the machine manager will be updated. + // None means that every resource matched by the machine manager will not be updated. // +unionDiscriminator // +required Mode MachineManagerSelectorMode `json:"mode"` @@ -170,7 +179,7 @@ type PartialSelector struct { } // MachineManagerSelectorMode is a string enum used in the MachineManagerSelector union discriminator. -// +kubebuilder:validation:Enum:="All";"Partial" +// +kubebuilder:validation:Enum:="All";"Partial";"None" type MachineManagerSelectorMode string const ( @@ -180,6 +189,9 @@ const ( // Partial represents a configuration mode that will register resources specified by the parent MachineManager only // if they match with the label selector. Partial MachineManagerSelectorMode = "Partial" + + // None represents a configuration mode that excludes all resources specified by the parent MachineManager from boot image updates. + None MachineManagerSelectorMode = "None" ) // MachineManagerManagedResourceType is a string enum used in the MachineManager type to describe the resource diff --git a/vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_50_console_01_consoles.crd.yaml b/vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_50_console_01_consoles.crd.yaml index 6578035ed1..dfd9e3ddb4 100644 --- a/vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_50_console_01_consoles.crd.yaml +++ b/vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_50_console_01_consoles.crd.yaml @@ -131,14 +131,14 @@ spec: customLogoFile: description: |- customLogoFile replaces the default OpenShift logo in the masthead and about dialog. It is a reference to a + Only one of customLogoFile or logos can be set at a time. ConfigMap in the openshift-config namespace. This can be created with a command like 'oc create configmap custom-logo --from-file=/path/to/file -n openshift-config'. Image size must be less than 1 MB due to constraints on the ConfigMap size. The ConfigMap key should include a file extension so that the console serves the file with the correct MIME type. - Recommended logo specifications: - Dimensions: Max height of 68px and max width of 200px - SVG format preferred + The recommended file format for the logo is SVG, but other file formats are allowed if supported by the browser. + Deprecated: Use logos instead. properties: key: description: key allows pointing to a specific key/value inside @@ -273,6 +273,136 @@ spec: Invalid value will prevent a console rollout. pattern: ^$|^((https):\/\/?)[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|\/?))\/$ type: string + logos: + description: |- + logos is used to replace the OpenShift Masthead and Favicon logos in the console UI with custom logos. + logos is an optional field that allows a list of logos. + Only one of logos or customLogoFile can be set at a time. + If logos is set, customLogoFile must be unset. + When specified, there must be at least one entry and no more than 2 entries. + Each type must appear only once in the list. + items: + description: Logo defines a configuration based on theme modes + for the console UI logo. + properties: + themes: + description: |- + themes specifies the themes for the console UI logo. + themes is a required field that allows a list of themes. Each item in the themes list must have a unique mode and a source field. + Each mode determines whether the logo is for the dark or light mode of the console UI. + If a theme is not specified, the default OpenShift logo will be displayed for that theme. + There must be at least one entry and no more than 2 entries. + items: + description: Theme defines a theme mode for the console + UI. + properties: + mode: + description: |- + mode is used to specify what theme mode a logo will apply to in the console UI. + mode is a required field that allows values of Dark and Light. + When set to Dark, the logo file referenced in the 'file' field will be used when an end-user of the console UI enables the Dark mode. + When set to Light, the logo file referenced in the 'file' field will be used when an end-user of the console UI enables the Light mode. + enum: + - Dark + - Light + type: string + source: + description: |- + source is used by the console to locate the specified file containing a custom logo. + source is a required field that references a ConfigMap name and key that contains the custom logo file in the openshift-config namespace. + You can create it with a command like: + - 'oc create configmap custom-logos-config --namespace=openshift-config --from-file=/path/to/file' + The ConfigMap key must include the file extension so that the console serves the file with the correct MIME type. + The recommended file format for the Masthead and Favicon logos is SVG, but other file formats are allowed if supported by the browser. + The logo image size must be less than 1 MB due to constraints on the ConfigMap size. + For more information, see the documentation: https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/web_console/customizing-web-console#customizing-web-console + properties: + configMap: + description: |- + configMap specifies the ConfigMap sourcing details such as the name of the ConfigMap and the key for the file. + The ConfigMap must exist in the openshift-config namespace. + Required when from is "ConfigMap", and forbidden otherwise. + properties: + key: + description: |- + key is the logo key inside the referenced ConfigMap. + Must consist only of alphanumeric characters, dashes (-), underscores (_), and periods (.). + Must be at most 253 characters in length. + Must end in a valid file extension. + A valid file extension must consist of a period followed by 2 to 5 alpha characters. + maxLength: 253 + type: string + x-kubernetes-validations: + - message: The ConfigMap key must consist + only of alphanumeric characters, dashes + (-), underscores (_), and periods (.). + rule: self.matches('^[a-zA-Z0-9._-]+$') + - message: The ConfigMap key must end with + a valid file extension (2 to 5 letters). + rule: self.matches('.*\\.[a-zA-Z]{2,5}$') + name: + description: |- + name is the name of the ConfigMap. + name is a required field. + Must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character. + Must be at most 253 characters in length. + maxLength: 253 + type: string + x-kubernetes-validations: + - message: a lowercase RFC 1123 subdomain + must consist of lower case alphanumeric + characters, '-' or '.', and must start + and end with an alphanumeric character. + rule: '!format.dns1123Subdomain().validate(self).hasValue()' + required: + - key + - name + type: object + from: + description: |- + from is a required field to specify the source type of the file reference. + Allowed values are ConfigMap. + When set to ConfigMap, the file will be sourced from a ConfigMap in the openshift-config namespace. The configMap field must be set when from is set to ConfigMap. + enum: + - ConfigMap + type: string + required: + - from + type: object + x-kubernetes-validations: + - message: configMap is required when from is 'ConfigMap', + and forbidden otherwise. + rule: 'has(self.from) && self.from == ''ConfigMap'' + ? has(self.configMap) : !has(self.configMap)' + required: + - mode + - source + type: object + maxItems: 2 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - mode + x-kubernetes-list-type: map + type: + description: |- + type specifies the type of the logo for the console UI. It determines whether the logo is for the masthead or favicon. + type is a required field that allows values of Masthead and Favicon. + When set to "Masthead", the logo will be used in the masthead and about modal of the console UI. + When set to "Favicon", the logo will be used as the favicon of the console UI. + enum: + - Masthead + - Favicon + type: string + required: + - themes + - type + type: object + maxItems: 2 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map perspectives: description: perspectives allows enabling/disabling of perspective(s) that user can see in the Perspective switcher dropdown. @@ -694,6 +824,9 @@ spec: type: array type: object type: object + x-kubernetes-validations: + - message: Only one of logos or customLogoFile can be set. + rule: '!(has(self.logos) && has(self.customLogoFile))' ingress: description: |- ingress allows to configure the alternative ingress for the console. diff --git a/vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_50_csi-driver_01_clustercsidrivers-CustomNoUpgrade.crd.yaml b/vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_50_csi-driver_01_clustercsidrivers-CustomNoUpgrade.crd.yaml new file mode 100644 index 0000000000..8e2ab77f10 --- /dev/null +++ b/vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_50_csi-driver_01_clustercsidrivers-CustomNoUpgrade.crd.yaml @@ -0,0 +1,504 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.openshift.io: https://github.com/openshift/api/pull/701 + api.openshift.io/merged-by-featuregates: "true" + include.release.openshift.io/ibm-cloud-managed: "true" + include.release.openshift.io/self-managed-high-availability: "true" + release.openshift.io/feature-set: CustomNoUpgrade + name: clustercsidrivers.operator.openshift.io +spec: + group: operator.openshift.io + names: + kind: ClusterCSIDriver + listKind: ClusterCSIDriverList + plural: clustercsidrivers + singular: clustercsidriver + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + description: |- + ClusterCSIDriver object allows management and configuration of a CSI driver operator + installed by default in OpenShift. Name of the object must be name of the CSI driver + it operates. See CSIDriverName type for list of allowed values. + + Compatibility level 1: Stable within a major release for a minimum of 12 months or 3 minor releases (whichever is longer). + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + properties: + name: + enum: + - ebs.csi.aws.com + - efs.csi.aws.com + - disk.csi.azure.com + - file.csi.azure.com + - filestore.csi.storage.gke.io + - pd.csi.storage.gke.io + - cinder.csi.openstack.org + - csi.vsphere.vmware.com + - manila.csi.openstack.org + - csi.ovirt.org + - csi.kubevirt.io + - csi.sharedresource.openshift.io + - diskplugin.csi.alibabacloud.com + - vpc.block.csi.ibm.io + - powervs.csi.ibm.com + - secrets-store.csi.k8s.io + - smb.csi.k8s.io + type: string + type: object + spec: + description: spec holds user settable values for configuration + properties: + driverConfig: + description: |- + driverConfig can be used to specify platform specific driver configuration. + When omitted, this means no opinion and the platform is left to choose reasonable + defaults. These defaults are subject to change over time. + properties: + aws: + description: aws is used to configure the AWS CSI driver. + properties: + efsVolumeMetrics: + description: efsVolumeMetrics sets the configuration for collecting + metrics from EFS volumes used by the EFS CSI Driver. + properties: + recursiveWalk: + description: |- + recursiveWalk provides additional configuration for collecting volume metrics in the AWS EFS CSI Driver + when the state is set to RecursiveWalk. + properties: + fsRateLimit: + description: |- + fsRateLimit defines the rate limit, in goroutines per file system, for processing volume metrics. + When omitted, this means no opinion and the platform is left to choose a reasonable + default, which is subject to change over time. The current default is 5. + The valid range is from 1 to 100 goroutines. + format: int32 + maximum: 100 + minimum: 1 + type: integer + refreshPeriodMinutes: + description: |- + refreshPeriodMinutes specifies the frequency, in minutes, at which volume metrics are refreshed. + When omitted, this means no opinion and the platform is left to choose a reasonable + default, which is subject to change over time. The current default is 240. + The valid range is from 1 to 43200 minutes (30 days). + format: int32 + maximum: 43200 + minimum: 1 + type: integer + type: object + state: + description: |- + state defines the state of metric collection in the AWS EFS CSI Driver. + This field is required and must be set to one of the following values: Disabled or RecursiveWalk. + Disabled means no metrics collection will be performed. This is the default value. + RecursiveWalk means the AWS EFS CSI Driver will recursively scan volumes to collect metrics. + This process may result in high CPU and memory usage, depending on the volume size. + enum: + - RecursiveWalk + - Disabled + type: string + required: + - state + type: object + kmsKeyARN: + description: |- + kmsKeyARN sets the cluster default storage class to encrypt volumes with a user-defined KMS key, + rather than the default KMS key used by AWS. + The value may be either the ARN or Alias ARN of a KMS key. + pattern: ^arn:(aws|aws-cn|aws-us-gov|aws-iso|aws-iso-b|aws-iso-e|aws-iso-f):kms:[a-z0-9-]+:[0-9]{12}:(key|alias)\/.*$ + type: string + type: object + azure: + description: azure is used to configure the Azure CSI driver. + properties: + diskEncryptionSet: + description: |- + diskEncryptionSet sets the cluster default storage class to encrypt volumes with a + customer-managed encryption set, rather than the default platform-managed keys. + properties: + name: + description: |- + name is the name of the disk encryption set that will be set on the default storage class. + The value should consist of only alphanumberic characters, + underscores (_), hyphens, and be at most 80 characters in length. + maxLength: 80 + pattern: ^[a-zA-Z0-9\_-]+$ + type: string + resourceGroup: + description: |- + resourceGroup defines the Azure resource group that contains the disk encryption set. + The value should consist of only alphanumberic characters, + underscores (_), parentheses, hyphens and periods. + The value should not end in a period and be at most 90 characters in + length. + maxLength: 90 + pattern: ^[\w\.\-\(\)]*[\w\-\(\)]$ + type: string + subscriptionID: + description: |- + subscriptionID defines the Azure subscription that contains the disk encryption set. + The value should meet the following conditions: + 1. It should be a 128-bit number. + 2. It should be 36 characters (32 hexadecimal characters and 4 hyphens) long. + 3. It should be displayed in five groups separated by hyphens (-). + 4. The first group should be 8 characters long. + 5. The second, third, and fourth groups should be 4 characters long. + 6. The fifth group should be 12 characters long. + An Example SubscrionID: f2007bbf-f802-4a47-9336-cf7c6b89b378 + maxLength: 36 + pattern: ^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$ + type: string + required: + - name + - resourceGroup + - subscriptionID + type: object + type: object + driverType: + description: |- + driverType indicates type of CSI driver for which the + driverConfig is being applied to. + Valid values are: AWS, Azure, GCP, IBMCloud, vSphere and omitted. + Consumers should treat unknown values as a NO-OP. + enum: + - "" + - AWS + - Azure + - GCP + - IBMCloud + - vSphere + type: string + gcp: + description: gcp is used to configure the GCP CSI driver. + properties: + kmsKey: + description: |- + kmsKey sets the cluster default storage class to encrypt volumes with customer-supplied + encryption keys, rather than the default keys managed by GCP. + properties: + keyRing: + description: |- + keyRing is the name of the KMS Key Ring which the KMS Key belongs to. + The value should correspond to an existing KMS key ring and should + consist of only alphanumeric characters, hyphens (-) and underscores (_), + and be at most 63 characters in length. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z0-9\_-]+$ + type: string + location: + description: |- + location is the GCP location in which the Key Ring exists. + The value must match an existing GCP location, or "global". + Defaults to global, if not set. + pattern: ^[a-zA-Z0-9\_-]+$ + type: string + name: + description: |- + name is the name of the customer-managed encryption key to be used for disk encryption. + The value should correspond to an existing KMS key and should + consist of only alphanumeric characters, hyphens (-) and underscores (_), + and be at most 63 characters in length. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z0-9\_-]+$ + type: string + projectID: + description: |- + projectID is the ID of the Project in which the KMS Key Ring exists. + It must be 6 to 30 lowercase letters, digits, or hyphens. + It must start with a letter. Trailing hyphens are prohibited. + maxLength: 30 + minLength: 6 + pattern: ^[a-z][a-z0-9-]+[a-z0-9]$ + type: string + required: + - keyRing + - name + - projectID + type: object + type: object + ibmcloud: + description: ibmcloud is used to configure the IBM Cloud CSI driver. + properties: + encryptionKeyCRN: + description: |- + encryptionKeyCRN is the IBM Cloud CRN of the customer-managed root key to use + for disk encryption of volumes for the default storage classes. + maxLength: 154 + minLength: 144 + pattern: ^crn:v[0-9]+:bluemix:(public|private):(kms|hs-crypto):[a-z-]+:a/[0-9a-f]+:[0-9a-f-]{36}:key:[0-9a-f-]{36}$ + type: string + required: + - encryptionKeyCRN + type: object + vSphere: + description: vSphere is used to configure the vsphere CSI driver. + properties: + globalMaxSnapshotsPerBlockVolume: + description: |- + globalMaxSnapshotsPerBlockVolume is a global configuration parameter that applies to volumes on all kinds of + datastores. If omitted, the platform chooses a default, which is subject to change over time, currently that default is 3. + Snapshots can not be disabled using this parameter. + Increasing number of snapshots above 3 can have negative impact on performance, for more details see: https://kb.vmware.com/s/article/1025279 + Volume snapshot documentation: https://docs.vmware.com/en/VMware-vSphere-Container-Storage-Plug-in/3.0/vmware-vsphere-csp-getting-started/GUID-E0B41C69-7EEB-450F-A73D-5FD2FF39E891.html + format: int32 + maximum: 32 + minimum: 1 + type: integer + granularMaxSnapshotsPerBlockVolumeInVSAN: + description: |- + granularMaxSnapshotsPerBlockVolumeInVSAN is a granular configuration parameter on vSAN datastore only. It + overrides GlobalMaxSnapshotsPerBlockVolume if set, while it falls back to the global constraint if unset. + Snapshots for VSAN can not be disabled using this parameter. + format: int32 + maximum: 32 + minimum: 1 + type: integer + granularMaxSnapshotsPerBlockVolumeInVVOL: + description: |- + granularMaxSnapshotsPerBlockVolumeInVVOL is a granular configuration parameter on Virtual Volumes datastore only. + It overrides GlobalMaxSnapshotsPerBlockVolume if set, while it falls back to the global constraint if unset. + Snapshots for VVOL can not be disabled using this parameter. + format: int32 + maximum: 32 + minimum: 1 + type: integer + maxAllowedBlockVolumesPerNode: + description: |- + maxAllowedBlockVolumesPerNode is an optional configuration parameter that allows setting a custom value for the + limit of the number of PersistentVolumes attached to a node. In vSphere version 7 this limit was set to 59 by + default, however in vSphere version 8 this limit was increased to 255. + Before increasing this value above 59 the cluster administrator needs to ensure that every node forming the + cluster is updated to ESXi version 8 or higher and that all nodes are running the same version. + The limit must be between 1 and 255, which matches the vSphere version 8 maximum. + When omitted, this means no opinion and the platform is left to choose a reasonable default, which is subject to + change over time. + The current default is 59, which matches the limit for vSphere version 7. + format: int32 + maximum: 255 + minimum: 1 + type: integer + topologyCategories: + description: |- + topologyCategories indicates tag categories with which + vcenter resources such as hostcluster or datacenter were tagged with. + If cluster Infrastructure object has a topology, values specified in + Infrastructure object will be used and modifications to topologyCategories + will be rejected. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + required: + - driverType + type: object + x-kubernetes-validations: + - message: ibmcloud must be set if driverType is 'IBMCloud', but remain + unset otherwise + rule: 'has(self.driverType) && self.driverType == ''IBMCloud'' ? + has(self.ibmcloud) : !has(self.ibmcloud)' + logLevel: + default: Normal + description: |- + logLevel is an intent based logging for an overall component. It does not give fine grained control, but it is a + simple way to manage coarse grained logging choices that operators have to interpret for their operands. + + Valid values are: "Normal", "Debug", "Trace", "TraceAll". + Defaults to "Normal". + enum: + - "" + - Normal + - Debug + - Trace + - TraceAll + type: string + managementState: + description: managementState indicates whether and how the operator + should manage the component + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + observedConfig: + description: |- + observedConfig holds a sparse config that controller has observed from the cluster state. It exists in spec because + it is an input to the level for the operator + nullable: true + type: object + x-kubernetes-preserve-unknown-fields: true + operatorLogLevel: + default: Normal + description: |- + operatorLogLevel is an intent based logging for the operator itself. It does not give fine grained control, but it is a + simple way to manage coarse grained logging choices that operators have to interpret for themselves. + + Valid values are: "Normal", "Debug", "Trace", "TraceAll". + Defaults to "Normal". + enum: + - "" + - Normal + - Debug + - Trace + - TraceAll + type: string + storageClassState: + description: |- + storageClassState determines if CSI operator should create and manage storage classes. + If this field value is empty or Managed - CSI operator will continuously reconcile + storage class and create if necessary. + If this field value is Unmanaged - CSI operator will not reconcile any previously created + storage class. + If this field value is Removed - CSI operator will delete the storage class it created previously. + When omitted, this means the user has no opinion and the platform chooses a reasonable default, + which is subject to change over time. + The current default behaviour is Managed. + enum: + - "" + - Managed + - Unmanaged + - Removed + type: string + unsupportedConfigOverrides: + description: |- + unsupportedConfigOverrides overrides the final configuration that was computed by the operator. + Red Hat does not support the use of this field. + Misuse of this field could lead to unexpected behavior or conflict with other configuration options. + Seek guidance from the Red Hat support before using this field. + Use of this property blocks cluster upgrades, it must be removed before upgrading your cluster. + nullable: true + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + status: + description: status holds observed values from the cluster. They may not + be overridden. + properties: + conditions: + description: conditions is a list of conditions and their status + items: + description: OperatorCondition is just the standard condition fields. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + type: string + reason: + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + generations: + description: generations are used to determine when an item needs + to be reconciled or has changed in a way that needs a reaction. + items: + description: GenerationStatus keeps track of the generation for + a given resource so that decisions about forced updates can be + made. + properties: + group: + description: group is the group of the thing you're tracking + type: string + hash: + description: hash is an optional field set for resources without + generation that are content sensitive like secrets and configmaps + type: string + lastGeneration: + description: lastGeneration is the last generation of the workload + controller involved + format: int64 + type: integer + name: + description: name is the name of the thing you're tracking + type: string + namespace: + description: namespace is where the thing you're tracking is + type: string + resource: + description: resource is the resource type of the thing you're + tracking + type: string + required: + - group + - name + - namespace + - resource + type: object + type: array + x-kubernetes-list-map-keys: + - group + - resource + - namespace + - name + x-kubernetes-list-type: map + latestAvailableRevision: + description: latestAvailableRevision is the deploymentID of the most + recent deployment + format: int32 + type: integer + x-kubernetes-validations: + - message: must only increase + rule: self >= oldSelf + observedGeneration: + description: observedGeneration is the last generation change you've + dealt with + format: int64 + type: integer + readyReplicas: + description: readyReplicas indicates how many replicas are ready and + at the desired state + format: int32 + type: integer + version: + description: version is the level this availability applies to + type: string + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_50_csi-driver_01_clustercsidrivers.crd.yaml b/vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_50_csi-driver_01_clustercsidrivers-Default.crd.yaml similarity index 99% rename from vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_50_csi-driver_01_clustercsidrivers.crd.yaml rename to vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_50_csi-driver_01_clustercsidrivers-Default.crd.yaml index 1db446757e..daf1f8abdb 100644 --- a/vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_50_csi-driver_01_clustercsidrivers.crd.yaml +++ b/vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_50_csi-driver_01_clustercsidrivers-Default.crd.yaml @@ -6,6 +6,7 @@ metadata: api.openshift.io/merged-by-featuregates: "true" include.release.openshift.io/ibm-cloud-managed: "true" include.release.openshift.io/self-managed-high-availability: "true" + release.openshift.io/feature-set: Default name: clustercsidrivers.operator.openshift.io spec: group: operator.openshift.io diff --git a/vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_50_csi-driver_01_clustercsidrivers-DevPreviewNoUpgrade.crd.yaml b/vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_50_csi-driver_01_clustercsidrivers-DevPreviewNoUpgrade.crd.yaml new file mode 100644 index 0000000000..e8766002d4 --- /dev/null +++ b/vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_50_csi-driver_01_clustercsidrivers-DevPreviewNoUpgrade.crd.yaml @@ -0,0 +1,504 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.openshift.io: https://github.com/openshift/api/pull/701 + api.openshift.io/merged-by-featuregates: "true" + include.release.openshift.io/ibm-cloud-managed: "true" + include.release.openshift.io/self-managed-high-availability: "true" + release.openshift.io/feature-set: DevPreviewNoUpgrade + name: clustercsidrivers.operator.openshift.io +spec: + group: operator.openshift.io + names: + kind: ClusterCSIDriver + listKind: ClusterCSIDriverList + plural: clustercsidrivers + singular: clustercsidriver + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + description: |- + ClusterCSIDriver object allows management and configuration of a CSI driver operator + installed by default in OpenShift. Name of the object must be name of the CSI driver + it operates. See CSIDriverName type for list of allowed values. + + Compatibility level 1: Stable within a major release for a minimum of 12 months or 3 minor releases (whichever is longer). + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + properties: + name: + enum: + - ebs.csi.aws.com + - efs.csi.aws.com + - disk.csi.azure.com + - file.csi.azure.com + - filestore.csi.storage.gke.io + - pd.csi.storage.gke.io + - cinder.csi.openstack.org + - csi.vsphere.vmware.com + - manila.csi.openstack.org + - csi.ovirt.org + - csi.kubevirt.io + - csi.sharedresource.openshift.io + - diskplugin.csi.alibabacloud.com + - vpc.block.csi.ibm.io + - powervs.csi.ibm.com + - secrets-store.csi.k8s.io + - smb.csi.k8s.io + type: string + type: object + spec: + description: spec holds user settable values for configuration + properties: + driverConfig: + description: |- + driverConfig can be used to specify platform specific driver configuration. + When omitted, this means no opinion and the platform is left to choose reasonable + defaults. These defaults are subject to change over time. + properties: + aws: + description: aws is used to configure the AWS CSI driver. + properties: + efsVolumeMetrics: + description: efsVolumeMetrics sets the configuration for collecting + metrics from EFS volumes used by the EFS CSI Driver. + properties: + recursiveWalk: + description: |- + recursiveWalk provides additional configuration for collecting volume metrics in the AWS EFS CSI Driver + when the state is set to RecursiveWalk. + properties: + fsRateLimit: + description: |- + fsRateLimit defines the rate limit, in goroutines per file system, for processing volume metrics. + When omitted, this means no opinion and the platform is left to choose a reasonable + default, which is subject to change over time. The current default is 5. + The valid range is from 1 to 100 goroutines. + format: int32 + maximum: 100 + minimum: 1 + type: integer + refreshPeriodMinutes: + description: |- + refreshPeriodMinutes specifies the frequency, in minutes, at which volume metrics are refreshed. + When omitted, this means no opinion and the platform is left to choose a reasonable + default, which is subject to change over time. The current default is 240. + The valid range is from 1 to 43200 minutes (30 days). + format: int32 + maximum: 43200 + minimum: 1 + type: integer + type: object + state: + description: |- + state defines the state of metric collection in the AWS EFS CSI Driver. + This field is required and must be set to one of the following values: Disabled or RecursiveWalk. + Disabled means no metrics collection will be performed. This is the default value. + RecursiveWalk means the AWS EFS CSI Driver will recursively scan volumes to collect metrics. + This process may result in high CPU and memory usage, depending on the volume size. + enum: + - RecursiveWalk + - Disabled + type: string + required: + - state + type: object + kmsKeyARN: + description: |- + kmsKeyARN sets the cluster default storage class to encrypt volumes with a user-defined KMS key, + rather than the default KMS key used by AWS. + The value may be either the ARN or Alias ARN of a KMS key. + pattern: ^arn:(aws|aws-cn|aws-us-gov|aws-iso|aws-iso-b|aws-iso-e|aws-iso-f):kms:[a-z0-9-]+:[0-9]{12}:(key|alias)\/.*$ + type: string + type: object + azure: + description: azure is used to configure the Azure CSI driver. + properties: + diskEncryptionSet: + description: |- + diskEncryptionSet sets the cluster default storage class to encrypt volumes with a + customer-managed encryption set, rather than the default platform-managed keys. + properties: + name: + description: |- + name is the name of the disk encryption set that will be set on the default storage class. + The value should consist of only alphanumberic characters, + underscores (_), hyphens, and be at most 80 characters in length. + maxLength: 80 + pattern: ^[a-zA-Z0-9\_-]+$ + type: string + resourceGroup: + description: |- + resourceGroup defines the Azure resource group that contains the disk encryption set. + The value should consist of only alphanumberic characters, + underscores (_), parentheses, hyphens and periods. + The value should not end in a period and be at most 90 characters in + length. + maxLength: 90 + pattern: ^[\w\.\-\(\)]*[\w\-\(\)]$ + type: string + subscriptionID: + description: |- + subscriptionID defines the Azure subscription that contains the disk encryption set. + The value should meet the following conditions: + 1. It should be a 128-bit number. + 2. It should be 36 characters (32 hexadecimal characters and 4 hyphens) long. + 3. It should be displayed in five groups separated by hyphens (-). + 4. The first group should be 8 characters long. + 5. The second, third, and fourth groups should be 4 characters long. + 6. The fifth group should be 12 characters long. + An Example SubscrionID: f2007bbf-f802-4a47-9336-cf7c6b89b378 + maxLength: 36 + pattern: ^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$ + type: string + required: + - name + - resourceGroup + - subscriptionID + type: object + type: object + driverType: + description: |- + driverType indicates type of CSI driver for which the + driverConfig is being applied to. + Valid values are: AWS, Azure, GCP, IBMCloud, vSphere and omitted. + Consumers should treat unknown values as a NO-OP. + enum: + - "" + - AWS + - Azure + - GCP + - IBMCloud + - vSphere + type: string + gcp: + description: gcp is used to configure the GCP CSI driver. + properties: + kmsKey: + description: |- + kmsKey sets the cluster default storage class to encrypt volumes with customer-supplied + encryption keys, rather than the default keys managed by GCP. + properties: + keyRing: + description: |- + keyRing is the name of the KMS Key Ring which the KMS Key belongs to. + The value should correspond to an existing KMS key ring and should + consist of only alphanumeric characters, hyphens (-) and underscores (_), + and be at most 63 characters in length. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z0-9\_-]+$ + type: string + location: + description: |- + location is the GCP location in which the Key Ring exists. + The value must match an existing GCP location, or "global". + Defaults to global, if not set. + pattern: ^[a-zA-Z0-9\_-]+$ + type: string + name: + description: |- + name is the name of the customer-managed encryption key to be used for disk encryption. + The value should correspond to an existing KMS key and should + consist of only alphanumeric characters, hyphens (-) and underscores (_), + and be at most 63 characters in length. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z0-9\_-]+$ + type: string + projectID: + description: |- + projectID is the ID of the Project in which the KMS Key Ring exists. + It must be 6 to 30 lowercase letters, digits, or hyphens. + It must start with a letter. Trailing hyphens are prohibited. + maxLength: 30 + minLength: 6 + pattern: ^[a-z][a-z0-9-]+[a-z0-9]$ + type: string + required: + - keyRing + - name + - projectID + type: object + type: object + ibmcloud: + description: ibmcloud is used to configure the IBM Cloud CSI driver. + properties: + encryptionKeyCRN: + description: |- + encryptionKeyCRN is the IBM Cloud CRN of the customer-managed root key to use + for disk encryption of volumes for the default storage classes. + maxLength: 154 + minLength: 144 + pattern: ^crn:v[0-9]+:bluemix:(public|private):(kms|hs-crypto):[a-z-]+:a/[0-9a-f]+:[0-9a-f-]{36}:key:[0-9a-f-]{36}$ + type: string + required: + - encryptionKeyCRN + type: object + vSphere: + description: vSphere is used to configure the vsphere CSI driver. + properties: + globalMaxSnapshotsPerBlockVolume: + description: |- + globalMaxSnapshotsPerBlockVolume is a global configuration parameter that applies to volumes on all kinds of + datastores. If omitted, the platform chooses a default, which is subject to change over time, currently that default is 3. + Snapshots can not be disabled using this parameter. + Increasing number of snapshots above 3 can have negative impact on performance, for more details see: https://kb.vmware.com/s/article/1025279 + Volume snapshot documentation: https://docs.vmware.com/en/VMware-vSphere-Container-Storage-Plug-in/3.0/vmware-vsphere-csp-getting-started/GUID-E0B41C69-7EEB-450F-A73D-5FD2FF39E891.html + format: int32 + maximum: 32 + minimum: 1 + type: integer + granularMaxSnapshotsPerBlockVolumeInVSAN: + description: |- + granularMaxSnapshotsPerBlockVolumeInVSAN is a granular configuration parameter on vSAN datastore only. It + overrides GlobalMaxSnapshotsPerBlockVolume if set, while it falls back to the global constraint if unset. + Snapshots for VSAN can not be disabled using this parameter. + format: int32 + maximum: 32 + minimum: 1 + type: integer + granularMaxSnapshotsPerBlockVolumeInVVOL: + description: |- + granularMaxSnapshotsPerBlockVolumeInVVOL is a granular configuration parameter on Virtual Volumes datastore only. + It overrides GlobalMaxSnapshotsPerBlockVolume if set, while it falls back to the global constraint if unset. + Snapshots for VVOL can not be disabled using this parameter. + format: int32 + maximum: 32 + minimum: 1 + type: integer + maxAllowedBlockVolumesPerNode: + description: |- + maxAllowedBlockVolumesPerNode is an optional configuration parameter that allows setting a custom value for the + limit of the number of PersistentVolumes attached to a node. In vSphere version 7 this limit was set to 59 by + default, however in vSphere version 8 this limit was increased to 255. + Before increasing this value above 59 the cluster administrator needs to ensure that every node forming the + cluster is updated to ESXi version 8 or higher and that all nodes are running the same version. + The limit must be between 1 and 255, which matches the vSphere version 8 maximum. + When omitted, this means no opinion and the platform is left to choose a reasonable default, which is subject to + change over time. + The current default is 59, which matches the limit for vSphere version 7. + format: int32 + maximum: 255 + minimum: 1 + type: integer + topologyCategories: + description: |- + topologyCategories indicates tag categories with which + vcenter resources such as hostcluster or datacenter were tagged with. + If cluster Infrastructure object has a topology, values specified in + Infrastructure object will be used and modifications to topologyCategories + will be rejected. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + required: + - driverType + type: object + x-kubernetes-validations: + - message: ibmcloud must be set if driverType is 'IBMCloud', but remain + unset otherwise + rule: 'has(self.driverType) && self.driverType == ''IBMCloud'' ? + has(self.ibmcloud) : !has(self.ibmcloud)' + logLevel: + default: Normal + description: |- + logLevel is an intent based logging for an overall component. It does not give fine grained control, but it is a + simple way to manage coarse grained logging choices that operators have to interpret for their operands. + + Valid values are: "Normal", "Debug", "Trace", "TraceAll". + Defaults to "Normal". + enum: + - "" + - Normal + - Debug + - Trace + - TraceAll + type: string + managementState: + description: managementState indicates whether and how the operator + should manage the component + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + observedConfig: + description: |- + observedConfig holds a sparse config that controller has observed from the cluster state. It exists in spec because + it is an input to the level for the operator + nullable: true + type: object + x-kubernetes-preserve-unknown-fields: true + operatorLogLevel: + default: Normal + description: |- + operatorLogLevel is an intent based logging for the operator itself. It does not give fine grained control, but it is a + simple way to manage coarse grained logging choices that operators have to interpret for themselves. + + Valid values are: "Normal", "Debug", "Trace", "TraceAll". + Defaults to "Normal". + enum: + - "" + - Normal + - Debug + - Trace + - TraceAll + type: string + storageClassState: + description: |- + storageClassState determines if CSI operator should create and manage storage classes. + If this field value is empty or Managed - CSI operator will continuously reconcile + storage class and create if necessary. + If this field value is Unmanaged - CSI operator will not reconcile any previously created + storage class. + If this field value is Removed - CSI operator will delete the storage class it created previously. + When omitted, this means the user has no opinion and the platform chooses a reasonable default, + which is subject to change over time. + The current default behaviour is Managed. + enum: + - "" + - Managed + - Unmanaged + - Removed + type: string + unsupportedConfigOverrides: + description: |- + unsupportedConfigOverrides overrides the final configuration that was computed by the operator. + Red Hat does not support the use of this field. + Misuse of this field could lead to unexpected behavior or conflict with other configuration options. + Seek guidance from the Red Hat support before using this field. + Use of this property blocks cluster upgrades, it must be removed before upgrading your cluster. + nullable: true + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + status: + description: status holds observed values from the cluster. They may not + be overridden. + properties: + conditions: + description: conditions is a list of conditions and their status + items: + description: OperatorCondition is just the standard condition fields. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + type: string + reason: + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + generations: + description: generations are used to determine when an item needs + to be reconciled or has changed in a way that needs a reaction. + items: + description: GenerationStatus keeps track of the generation for + a given resource so that decisions about forced updates can be + made. + properties: + group: + description: group is the group of the thing you're tracking + type: string + hash: + description: hash is an optional field set for resources without + generation that are content sensitive like secrets and configmaps + type: string + lastGeneration: + description: lastGeneration is the last generation of the workload + controller involved + format: int64 + type: integer + name: + description: name is the name of the thing you're tracking + type: string + namespace: + description: namespace is where the thing you're tracking is + type: string + resource: + description: resource is the resource type of the thing you're + tracking + type: string + required: + - group + - name + - namespace + - resource + type: object + type: array + x-kubernetes-list-map-keys: + - group + - resource + - namespace + - name + x-kubernetes-list-type: map + latestAvailableRevision: + description: latestAvailableRevision is the deploymentID of the most + recent deployment + format: int32 + type: integer + x-kubernetes-validations: + - message: must only increase + rule: self >= oldSelf + observedGeneration: + description: observedGeneration is the last generation change you've + dealt with + format: int64 + type: integer + readyReplicas: + description: readyReplicas indicates how many replicas are ready and + at the desired state + format: int32 + type: integer + version: + description: version is the level this availability applies to + type: string + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_50_csi-driver_01_clustercsidrivers-TechPreviewNoUpgrade.crd.yaml b/vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_50_csi-driver_01_clustercsidrivers-TechPreviewNoUpgrade.crd.yaml new file mode 100644 index 0000000000..98f87a3563 --- /dev/null +++ b/vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_50_csi-driver_01_clustercsidrivers-TechPreviewNoUpgrade.crd.yaml @@ -0,0 +1,504 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.openshift.io: https://github.com/openshift/api/pull/701 + api.openshift.io/merged-by-featuregates: "true" + include.release.openshift.io/ibm-cloud-managed: "true" + include.release.openshift.io/self-managed-high-availability: "true" + release.openshift.io/feature-set: TechPreviewNoUpgrade + name: clustercsidrivers.operator.openshift.io +spec: + group: operator.openshift.io + names: + kind: ClusterCSIDriver + listKind: ClusterCSIDriverList + plural: clustercsidrivers + singular: clustercsidriver + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + description: |- + ClusterCSIDriver object allows management and configuration of a CSI driver operator + installed by default in OpenShift. Name of the object must be name of the CSI driver + it operates. See CSIDriverName type for list of allowed values. + + Compatibility level 1: Stable within a major release for a minimum of 12 months or 3 minor releases (whichever is longer). + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + properties: + name: + enum: + - ebs.csi.aws.com + - efs.csi.aws.com + - disk.csi.azure.com + - file.csi.azure.com + - filestore.csi.storage.gke.io + - pd.csi.storage.gke.io + - cinder.csi.openstack.org + - csi.vsphere.vmware.com + - manila.csi.openstack.org + - csi.ovirt.org + - csi.kubevirt.io + - csi.sharedresource.openshift.io + - diskplugin.csi.alibabacloud.com + - vpc.block.csi.ibm.io + - powervs.csi.ibm.com + - secrets-store.csi.k8s.io + - smb.csi.k8s.io + type: string + type: object + spec: + description: spec holds user settable values for configuration + properties: + driverConfig: + description: |- + driverConfig can be used to specify platform specific driver configuration. + When omitted, this means no opinion and the platform is left to choose reasonable + defaults. These defaults are subject to change over time. + properties: + aws: + description: aws is used to configure the AWS CSI driver. + properties: + efsVolumeMetrics: + description: efsVolumeMetrics sets the configuration for collecting + metrics from EFS volumes used by the EFS CSI Driver. + properties: + recursiveWalk: + description: |- + recursiveWalk provides additional configuration for collecting volume metrics in the AWS EFS CSI Driver + when the state is set to RecursiveWalk. + properties: + fsRateLimit: + description: |- + fsRateLimit defines the rate limit, in goroutines per file system, for processing volume metrics. + When omitted, this means no opinion and the platform is left to choose a reasonable + default, which is subject to change over time. The current default is 5. + The valid range is from 1 to 100 goroutines. + format: int32 + maximum: 100 + minimum: 1 + type: integer + refreshPeriodMinutes: + description: |- + refreshPeriodMinutes specifies the frequency, in minutes, at which volume metrics are refreshed. + When omitted, this means no opinion and the platform is left to choose a reasonable + default, which is subject to change over time. The current default is 240. + The valid range is from 1 to 43200 minutes (30 days). + format: int32 + maximum: 43200 + minimum: 1 + type: integer + type: object + state: + description: |- + state defines the state of metric collection in the AWS EFS CSI Driver. + This field is required and must be set to one of the following values: Disabled or RecursiveWalk. + Disabled means no metrics collection will be performed. This is the default value. + RecursiveWalk means the AWS EFS CSI Driver will recursively scan volumes to collect metrics. + This process may result in high CPU and memory usage, depending on the volume size. + enum: + - RecursiveWalk + - Disabled + type: string + required: + - state + type: object + kmsKeyARN: + description: |- + kmsKeyARN sets the cluster default storage class to encrypt volumes with a user-defined KMS key, + rather than the default KMS key used by AWS. + The value may be either the ARN or Alias ARN of a KMS key. + pattern: ^arn:(aws|aws-cn|aws-us-gov|aws-iso|aws-iso-b|aws-iso-e|aws-iso-f):kms:[a-z0-9-]+:[0-9]{12}:(key|alias)\/.*$ + type: string + type: object + azure: + description: azure is used to configure the Azure CSI driver. + properties: + diskEncryptionSet: + description: |- + diskEncryptionSet sets the cluster default storage class to encrypt volumes with a + customer-managed encryption set, rather than the default platform-managed keys. + properties: + name: + description: |- + name is the name of the disk encryption set that will be set on the default storage class. + The value should consist of only alphanumberic characters, + underscores (_), hyphens, and be at most 80 characters in length. + maxLength: 80 + pattern: ^[a-zA-Z0-9\_-]+$ + type: string + resourceGroup: + description: |- + resourceGroup defines the Azure resource group that contains the disk encryption set. + The value should consist of only alphanumberic characters, + underscores (_), parentheses, hyphens and periods. + The value should not end in a period and be at most 90 characters in + length. + maxLength: 90 + pattern: ^[\w\.\-\(\)]*[\w\-\(\)]$ + type: string + subscriptionID: + description: |- + subscriptionID defines the Azure subscription that contains the disk encryption set. + The value should meet the following conditions: + 1. It should be a 128-bit number. + 2. It should be 36 characters (32 hexadecimal characters and 4 hyphens) long. + 3. It should be displayed in five groups separated by hyphens (-). + 4. The first group should be 8 characters long. + 5. The second, third, and fourth groups should be 4 characters long. + 6. The fifth group should be 12 characters long. + An Example SubscrionID: f2007bbf-f802-4a47-9336-cf7c6b89b378 + maxLength: 36 + pattern: ^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$ + type: string + required: + - name + - resourceGroup + - subscriptionID + type: object + type: object + driverType: + description: |- + driverType indicates type of CSI driver for which the + driverConfig is being applied to. + Valid values are: AWS, Azure, GCP, IBMCloud, vSphere and omitted. + Consumers should treat unknown values as a NO-OP. + enum: + - "" + - AWS + - Azure + - GCP + - IBMCloud + - vSphere + type: string + gcp: + description: gcp is used to configure the GCP CSI driver. + properties: + kmsKey: + description: |- + kmsKey sets the cluster default storage class to encrypt volumes with customer-supplied + encryption keys, rather than the default keys managed by GCP. + properties: + keyRing: + description: |- + keyRing is the name of the KMS Key Ring which the KMS Key belongs to. + The value should correspond to an existing KMS key ring and should + consist of only alphanumeric characters, hyphens (-) and underscores (_), + and be at most 63 characters in length. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z0-9\_-]+$ + type: string + location: + description: |- + location is the GCP location in which the Key Ring exists. + The value must match an existing GCP location, or "global". + Defaults to global, if not set. + pattern: ^[a-zA-Z0-9\_-]+$ + type: string + name: + description: |- + name is the name of the customer-managed encryption key to be used for disk encryption. + The value should correspond to an existing KMS key and should + consist of only alphanumeric characters, hyphens (-) and underscores (_), + and be at most 63 characters in length. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z0-9\_-]+$ + type: string + projectID: + description: |- + projectID is the ID of the Project in which the KMS Key Ring exists. + It must be 6 to 30 lowercase letters, digits, or hyphens. + It must start with a letter. Trailing hyphens are prohibited. + maxLength: 30 + minLength: 6 + pattern: ^[a-z][a-z0-9-]+[a-z0-9]$ + type: string + required: + - keyRing + - name + - projectID + type: object + type: object + ibmcloud: + description: ibmcloud is used to configure the IBM Cloud CSI driver. + properties: + encryptionKeyCRN: + description: |- + encryptionKeyCRN is the IBM Cloud CRN of the customer-managed root key to use + for disk encryption of volumes for the default storage classes. + maxLength: 154 + minLength: 144 + pattern: ^crn:v[0-9]+:bluemix:(public|private):(kms|hs-crypto):[a-z-]+:a/[0-9a-f]+:[0-9a-f-]{36}:key:[0-9a-f-]{36}$ + type: string + required: + - encryptionKeyCRN + type: object + vSphere: + description: vSphere is used to configure the vsphere CSI driver. + properties: + globalMaxSnapshotsPerBlockVolume: + description: |- + globalMaxSnapshotsPerBlockVolume is a global configuration parameter that applies to volumes on all kinds of + datastores. If omitted, the platform chooses a default, which is subject to change over time, currently that default is 3. + Snapshots can not be disabled using this parameter. + Increasing number of snapshots above 3 can have negative impact on performance, for more details see: https://kb.vmware.com/s/article/1025279 + Volume snapshot documentation: https://docs.vmware.com/en/VMware-vSphere-Container-Storage-Plug-in/3.0/vmware-vsphere-csp-getting-started/GUID-E0B41C69-7EEB-450F-A73D-5FD2FF39E891.html + format: int32 + maximum: 32 + minimum: 1 + type: integer + granularMaxSnapshotsPerBlockVolumeInVSAN: + description: |- + granularMaxSnapshotsPerBlockVolumeInVSAN is a granular configuration parameter on vSAN datastore only. It + overrides GlobalMaxSnapshotsPerBlockVolume if set, while it falls back to the global constraint if unset. + Snapshots for VSAN can not be disabled using this parameter. + format: int32 + maximum: 32 + minimum: 1 + type: integer + granularMaxSnapshotsPerBlockVolumeInVVOL: + description: |- + granularMaxSnapshotsPerBlockVolumeInVVOL is a granular configuration parameter on Virtual Volumes datastore only. + It overrides GlobalMaxSnapshotsPerBlockVolume if set, while it falls back to the global constraint if unset. + Snapshots for VVOL can not be disabled using this parameter. + format: int32 + maximum: 32 + minimum: 1 + type: integer + maxAllowedBlockVolumesPerNode: + description: |- + maxAllowedBlockVolumesPerNode is an optional configuration parameter that allows setting a custom value for the + limit of the number of PersistentVolumes attached to a node. In vSphere version 7 this limit was set to 59 by + default, however in vSphere version 8 this limit was increased to 255. + Before increasing this value above 59 the cluster administrator needs to ensure that every node forming the + cluster is updated to ESXi version 8 or higher and that all nodes are running the same version. + The limit must be between 1 and 255, which matches the vSphere version 8 maximum. + When omitted, this means no opinion and the platform is left to choose a reasonable default, which is subject to + change over time. + The current default is 59, which matches the limit for vSphere version 7. + format: int32 + maximum: 255 + minimum: 1 + type: integer + topologyCategories: + description: |- + topologyCategories indicates tag categories with which + vcenter resources such as hostcluster or datacenter were tagged with. + If cluster Infrastructure object has a topology, values specified in + Infrastructure object will be used and modifications to topologyCategories + will be rejected. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + required: + - driverType + type: object + x-kubernetes-validations: + - message: ibmcloud must be set if driverType is 'IBMCloud', but remain + unset otherwise + rule: 'has(self.driverType) && self.driverType == ''IBMCloud'' ? + has(self.ibmcloud) : !has(self.ibmcloud)' + logLevel: + default: Normal + description: |- + logLevel is an intent based logging for an overall component. It does not give fine grained control, but it is a + simple way to manage coarse grained logging choices that operators have to interpret for their operands. + + Valid values are: "Normal", "Debug", "Trace", "TraceAll". + Defaults to "Normal". + enum: + - "" + - Normal + - Debug + - Trace + - TraceAll + type: string + managementState: + description: managementState indicates whether and how the operator + should manage the component + pattern: ^(Managed|Unmanaged|Force|Removed)$ + type: string + observedConfig: + description: |- + observedConfig holds a sparse config that controller has observed from the cluster state. It exists in spec because + it is an input to the level for the operator + nullable: true + type: object + x-kubernetes-preserve-unknown-fields: true + operatorLogLevel: + default: Normal + description: |- + operatorLogLevel is an intent based logging for the operator itself. It does not give fine grained control, but it is a + simple way to manage coarse grained logging choices that operators have to interpret for themselves. + + Valid values are: "Normal", "Debug", "Trace", "TraceAll". + Defaults to "Normal". + enum: + - "" + - Normal + - Debug + - Trace + - TraceAll + type: string + storageClassState: + description: |- + storageClassState determines if CSI operator should create and manage storage classes. + If this field value is empty or Managed - CSI operator will continuously reconcile + storage class and create if necessary. + If this field value is Unmanaged - CSI operator will not reconcile any previously created + storage class. + If this field value is Removed - CSI operator will delete the storage class it created previously. + When omitted, this means the user has no opinion and the platform chooses a reasonable default, + which is subject to change over time. + The current default behaviour is Managed. + enum: + - "" + - Managed + - Unmanaged + - Removed + type: string + unsupportedConfigOverrides: + description: |- + unsupportedConfigOverrides overrides the final configuration that was computed by the operator. + Red Hat does not support the use of this field. + Misuse of this field could lead to unexpected behavior or conflict with other configuration options. + Seek guidance from the Red Hat support before using this field. + Use of this property blocks cluster upgrades, it must be removed before upgrading your cluster. + nullable: true + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + status: + description: status holds observed values from the cluster. They may not + be overridden. + properties: + conditions: + description: conditions is a list of conditions and their status + items: + description: OperatorCondition is just the standard condition fields. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + type: string + reason: + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + generations: + description: generations are used to determine when an item needs + to be reconciled or has changed in a way that needs a reaction. + items: + description: GenerationStatus keeps track of the generation for + a given resource so that decisions about forced updates can be + made. + properties: + group: + description: group is the group of the thing you're tracking + type: string + hash: + description: hash is an optional field set for resources without + generation that are content sensitive like secrets and configmaps + type: string + lastGeneration: + description: lastGeneration is the last generation of the workload + controller involved + format: int64 + type: integer + name: + description: name is the name of the thing you're tracking + type: string + namespace: + description: namespace is where the thing you're tracking is + type: string + resource: + description: resource is the resource type of the thing you're + tracking + type: string + required: + - group + - name + - namespace + - resource + type: object + type: array + x-kubernetes-list-map-keys: + - group + - resource + - namespace + - name + x-kubernetes-list-type: map + latestAvailableRevision: + description: latestAvailableRevision is the deploymentID of the most + recent deployment + format: int32 + type: integer + x-kubernetes-validations: + - message: must only increase + rule: self >= oldSelf + observedGeneration: + description: observedGeneration is the last generation change you've + dealt with + format: int64 + type: integer + readyReplicas: + description: readyReplicas indicates how many replicas are ready and + at the desired state + format: int32 + type: integer + version: + description: version is the level this availability applies to + type: string + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_80_machine-config_01_machineconfigurations.crd.yaml b/vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_80_machine-config_01_machineconfigurations.crd.yaml index 073cb45f3d..7976d1dab4 100644 --- a/vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_80_machine-config_01_machineconfigurations.crd.yaml +++ b/vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_80_machine-config_01_machineconfigurations.crd.yaml @@ -77,8 +77,10 @@ spec: managedBootImages allows configuration for the management of boot images for machine resources within the cluster. This configuration allows users to select resources that should be updated to the latest boot images during cluster upgrades, ensuring that new machines - always boot with the current cluster version's boot image. When omitted, no boot images - will be updated. + always boot with the current cluster version's boot image. When omitted, this means no opinion + and the platform is left to choose a reasonable default, which is subject to change over time. + The default for each machine manager mode is All for GCP and AWS platforms, and None for all + other platforms. properties: machineManagers: description: |- @@ -116,9 +118,11 @@ spec: Valid values are All and Partial. All means that every resource matched by the machine manager will be updated. Partial requires specified selector(s) and allows customisation of which resources matched by the machine manager will be updated. + None means that every resource matched by the machine manager will not be updated. enum: - All - Partial + - None type: string partial: description: |- @@ -190,6 +194,7 @@ spec: - resource - selection type: object + maxItems: 5 type: array x-kubernetes-list-map-keys: - resource @@ -703,6 +708,130 @@ spec: x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map + managedBootImagesStatus: + description: |- + managedBootImagesStatus reflects what the latest cluster-validated boot image configuration is + and will be used by Machine Config Controller while performing boot image updates. + properties: + machineManagers: + description: |- + machineManagers can be used to register machine management resources for boot image updates. The Machine Config Operator + will watch for changes to this list. Only one entry is permitted per type of machine management resource. + items: + description: |- + MachineManager describes a target machine resource that is registered for boot image updates. It stores identifying information + such as the resource type and the API Group of the resource. It also provides granular control via the selection field. + properties: + apiGroup: + description: |- + apiGroup is name of the APIGroup that the machine management resource belongs to. + The only current valid value is machine.openshift.io. + machine.openshift.io means that the machine manager will only register resources that belong to OpenShift machine API group. + enum: + - machine.openshift.io + type: string + resource: + description: |- + resource is the machine management resource's type. + The only current valid value is machinesets. + machinesets means that the machine manager will only register resources of the kind MachineSet. + enum: + - machinesets + type: string + selection: + description: selection allows granular control of the machine + management resources that will be registered for boot + image updates. + properties: + mode: + description: |- + mode determines how machine managers will be selected for updates. + Valid values are All and Partial. + All means that every resource matched by the machine manager will be updated. + Partial requires specified selector(s) and allows customisation of which resources matched by the machine manager will be updated. + None means that every resource matched by the machine manager will not be updated. + enum: + - All + - Partial + - None + type: string + partial: + description: |- + partial provides label selector(s) that can be used to match machine management resources. + Only permitted when mode is set to "Partial". + properties: + machineResourceSelector: + description: machineResourceSelector is a label + selector that can be used to select machine resources + like MachineSets. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - machineResourceSelector + type: object + required: + - mode + type: object + x-kubernetes-validations: + - message: Partial is required when type is partial, and + forbidden otherwise + rule: 'has(self.mode) && self.mode == ''Partial'' ? has(self.partial) + : !has(self.partial)' + required: + - apiGroup + - resource + - selection + type: object + maxItems: 5 + type: array + x-kubernetes-list-map-keys: + - resource + - apiGroup + x-kubernetes-list-type: map + type: object nodeDisruptionPolicyStatus: description: |- nodeDisruptionPolicyStatus status reflects what the latest cluster-validated policies are, diff --git a/vendor/github.com/openshift/api/operator/v1/zz_generated.deepcopy.go b/vendor/github.com/openshift/api/operator/v1/zz_generated.deepcopy.go index 700ae5e695..1257a66e71 100644 --- a/vendor/github.com/openshift/api/operator/v1/zz_generated.deepcopy.go +++ b/vendor/github.com/openshift/api/operator/v1/zz_generated.deepcopy.go @@ -849,6 +849,22 @@ func (in *ConfigList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigMapFileReference) DeepCopyInto(out *ConfigMapFileReference) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapFileReference. +func (in *ConfigMapFileReference) DeepCopy() *ConfigMapFileReference { + if in == nil { + return nil + } + out := new(ConfigMapFileReference) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) { *out = *in @@ -931,6 +947,13 @@ func (in *ConsoleConfigRoute) DeepCopy() *ConsoleConfigRoute { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ConsoleCustomization) DeepCopyInto(out *ConsoleCustomization) { *out = *in + if in.Logos != nil { + in, out := &in.Logos, &out.Logos + *out = make([]Logo, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } if in.Capabilities != nil { in, out := &in.Capabilities, &out.Capabilities *out = make([]Capability, len(*in)) @@ -1598,6 +1621,27 @@ func (in *FeaturesMigration) DeepCopy() *FeaturesMigration { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FileReferenceSource) DeepCopyInto(out *FileReferenceSource) { + *out = *in + if in.ConfigMap != nil { + in, out := &in.ConfigMap, &out.ConfigMap + *out = new(ConfigMapFileReference) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FileReferenceSource. +func (in *FileReferenceSource) DeepCopy() *FileReferenceSource { + if in == nil { + return nil + } + out := new(FileReferenceSource) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ForwardPlugin) DeepCopyInto(out *ForwardPlugin) { *out = *in @@ -3035,6 +3079,29 @@ func (in *LoggingDestination) DeepCopy() *LoggingDestination { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Logo) DeepCopyInto(out *Logo) { + *out = *in + if in.Themes != nil { + in, out := &in.Themes, &out.Themes + *out = make([]Theme, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Logo. +func (in *Logo) DeepCopy() *Logo { + if in == nil { + return nil + } + out := new(Logo) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MTUMigration) DeepCopyInto(out *MTUMigration) { *out = *in @@ -3178,6 +3245,7 @@ func (in *MachineConfigurationStatus) DeepCopyInto(out *MachineConfigurationStat } } in.NodeDisruptionPolicyStatus.DeepCopyInto(&out.NodeDisruptionPolicyStatus) + in.ManagedBootImagesStatus.DeepCopyInto(&out.ManagedBootImagesStatus) return } @@ -5276,6 +5344,23 @@ func (in *SyslogLoggingDestinationParameters) DeepCopy() *SyslogLoggingDestinati return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Theme) DeepCopyInto(out *Theme) { + *out = *in + in.Source.DeepCopyInto(&out.Source) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Theme. +func (in *Theme) DeepCopy() *Theme { + if in == nil { + return nil + } + out := new(Theme) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Upstream) DeepCopyInto(out *Upstream) { *out = *in diff --git a/vendor/github.com/openshift/api/operator/v1/zz_generated.featuregated-crd-manifests.yaml b/vendor/github.com/openshift/api/operator/v1/zz_generated.featuregated-crd-manifests.yaml index 6d4e3cf232..81f2a87a99 100644 --- a/vendor/github.com/openshift/api/operator/v1/zz_generated.featuregated-crd-manifests.yaml +++ b/vendor/github.com/openshift/api/operator/v1/zz_generated.featuregated-crd-manifests.yaml @@ -70,6 +70,7 @@ clustercsidrivers.operator.openshift.io: Category: "" FeatureGates: - AWSEFSDriverVolumeMetrics + - VSphereConfigurableMaxAllowedBlockVolumesPerNode - VSphereDriverConfiguration FilenameOperatorName: csi-driver FilenameOperatorOrdering: "01" diff --git a/vendor/github.com/openshift/api/operator/v1/zz_generated.swagger_doc_generated.go b/vendor/github.com/openshift/api/operator/v1/zz_generated.swagger_doc_generated.go index 63833dd4d9..a0fa4fe475 100644 --- a/vendor/github.com/openshift/api/operator/v1/zz_generated.swagger_doc_generated.go +++ b/vendor/github.com/openshift/api/operator/v1/zz_generated.swagger_doc_generated.go @@ -227,6 +227,16 @@ func (CapabilityVisibility) SwaggerDoc() map[string]string { return map_CapabilityVisibility } +var map_ConfigMapFileReference = map[string]string{ + "": "ConfigMapFileReference references a specific file within a ConfigMap.", + "name": "name is the name of the ConfigMap. name is a required field. Must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character. Must be at most 253 characters in length.", + "key": "key is the logo key inside the referenced ConfigMap. Must consist only of alphanumeric characters, dashes (-), underscores (_), and periods (.). Must be at most 253 characters in length. Must end in a valid file extension. A valid file extension must consist of a period followed by 2 to 5 alpha characters.", +} + +func (ConfigMapFileReference) SwaggerDoc() map[string]string { + return map_ConfigMapFileReference +} + var map_Console = map[string]string{ "": "Console provides a means to configure an operator to manage the console.\n\nCompatibility level 1: Stable within a major release for a minimum of 12 months or 3 minor releases (whichever is longer).", "metadata": "metadata is the standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", @@ -247,12 +257,13 @@ func (ConsoleConfigRoute) SwaggerDoc() map[string]string { } var map_ConsoleCustomization = map[string]string{ - "": "ConsoleCustomization defines a list of optional configuration for the console UI.", + "": "ConsoleCustomization defines a list of optional configuration for the console UI. Ensure that Logos and CustomLogoFile cannot be set at the same time.", + "logos": "logos is used to replace the OpenShift Masthead and Favicon logos in the console UI with custom logos. logos is an optional field that allows a list of logos. Only one of logos or customLogoFile can be set at a time. If logos is set, customLogoFile must be unset. When specified, there must be at least one entry and no more than 2 entries. Each type must appear only once in the list.", "capabilities": "capabilities defines an array of capabilities that can be interacted with in the console UI. Each capability defines a visual state that can be interacted with the console to render in the UI. Available capabilities are LightspeedButton and GettingStartedBanner. Each of the available capabilities may appear only once in the list.", "brand": "brand is the default branding of the web console which can be overridden by providing the brand field. There is a limited set of specific brand options. This field controls elements of the console such as the logo. Invalid value will prevent a console rollout.", "documentationBaseURL": "documentationBaseURL links to external documentation are shown in various sections of the web console. Providing documentationBaseURL will override the default documentation URL. Invalid value will prevent a console rollout.", "customProductName": "customProductName is the name that will be displayed in page titles, logo alt text, and the about dialog instead of the normal OpenShift product name.", - "customLogoFile": "customLogoFile replaces the default OpenShift logo in the masthead and about dialog. It is a reference to a ConfigMap in the openshift-config namespace. This can be created with a command like 'oc create configmap custom-logo --from-file=/path/to/file -n openshift-config'. Image size must be less than 1 MB due to constraints on the ConfigMap size. The ConfigMap key should include a file extension so that the console serves the file with the correct MIME type. Recommended logo specifications: Dimensions: Max height of 68px and max width of 200px SVG format preferred", + "customLogoFile": "customLogoFile replaces the default OpenShift logo in the masthead and about dialog. It is a reference to a Only one of customLogoFile or logos can be set at a time. ConfigMap in the openshift-config namespace. This can be created with a command like 'oc create configmap custom-logo --from-file=/path/to/file -n openshift-config'. Image size must be less than 1 MB due to constraints on the ConfigMap size. The ConfigMap key should include a file extension so that the console serves the file with the correct MIME type. The recommended file format for the logo is SVG, but other file formats are allowed if supported by the browser. Deprecated: Use logos instead.", "developerCatalog": "developerCatalog allows to configure the shown developer catalog categories (filters) and types (sub-catalogs).", "projectAccess": "projectAccess allows customizing the available list of ClusterRoles in the Developer perspective Project access page which can be used by a project admin to specify roles to other users and restrict access within the project. If set, the list will replace the default ClusterRole options.", "quickStarts": "quickStarts allows customization of available ConsoleQuickStart resources in console.", @@ -344,6 +355,16 @@ func (DeveloperConsoleCatalogTypes) SwaggerDoc() map[string]string { return map_DeveloperConsoleCatalogTypes } +var map_FileReferenceSource = map[string]string{ + "": "FileReferenceSource is used by the console to locate the specified file containing a custom logo.", + "from": "from is a required field to specify the source type of the file reference. Allowed values are ConfigMap. When set to ConfigMap, the file will be sourced from a ConfigMap in the openshift-config namespace. The configMap field must be set when from is set to ConfigMap.", + "configMap": "configMap specifies the ConfigMap sourcing details such as the name of the ConfigMap and the key for the file. The ConfigMap must exist in the openshift-config namespace. Required when from is \"ConfigMap\", and forbidden otherwise.", +} + +func (FileReferenceSource) SwaggerDoc() map[string]string { + return map_FileReferenceSource +} + var map_Ingress = map[string]string{ "": "Ingress allows cluster admin to configure alternative ingress for the console.", "consoleURL": "consoleURL is a URL to be used as the base console address. If not specified, the console route hostname will be used. This field is required for clusters without ingress capability, where access to routes is not possible. Make sure that appropriate ingress is set up at this URL. The console operator will monitor the URL and may go degraded if it's unreachable for an extended period. Must use the HTTPS scheme.", @@ -354,6 +375,16 @@ func (Ingress) SwaggerDoc() map[string]string { return map_Ingress } +var map_Logo = map[string]string{ + "": "Logo defines a configuration based on theme modes for the console UI logo.", + "type": "type specifies the type of the logo for the console UI. It determines whether the logo is for the masthead or favicon. type is a required field that allows values of Masthead and Favicon. When set to \"Masthead\", the logo will be used in the masthead and about modal of the console UI. When set to \"Favicon\", the logo will be used as the favicon of the console UI.", + "themes": "themes specifies the themes for the console UI logo. themes is a required field that allows a list of themes. Each item in the themes list must have a unique mode and a source field. Each mode determines whether the logo is for the dark or light mode of the console UI. If a theme is not specified, the default OpenShift logo will be displayed for that theme. There must be at least one entry and no more than 2 entries.", +} + +func (Logo) SwaggerDoc() map[string]string { + return map_Logo +} + var map_Perspective = map[string]string{ "": "Perspective defines a perspective that cluster admins want to show/hide in the perspective switcher dropdown", "id": "id defines the id of the perspective. Example: \"dev\", \"admin\". The available perspective ids can be found in the code snippet section next to the yaml editor. Incorrect or unknown ids will be ignored.", @@ -423,6 +454,16 @@ func (StatuspageProvider) SwaggerDoc() map[string]string { return map_StatuspageProvider } +var map_Theme = map[string]string{ + "": "Theme defines a theme mode for the console UI.", + "mode": "mode is used to specify what theme mode a logo will apply to in the console UI. mode is a required field that allows values of Dark and Light. When set to Dark, the logo file referenced in the 'file' field will be used when an end-user of the console UI enables the Dark mode. When set to Light, the logo file referenced in the 'file' field will be used when an end-user of the console UI enables the Light mode.", + "source": "source is used by the console to locate the specified file containing a custom logo. source is a required field that references a ConfigMap name and key that contains the custom logo file in the openshift-config namespace. You can create it with a command like: - 'oc create configmap custom-logos-config --namespace=openshift-config --from-file=/path/to/file' The ConfigMap key must include the file extension so that the console serves the file with the correct MIME type. The recommended file format for the Masthead and Favicon logos is SVG, but other file formats are allowed if supported by the browser. The logo image size must be less than 1 MB due to constraints on the ConfigMap size. For more information, see the documentation: https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/web_console/customizing-web-console#customizing-web-console", +} + +func (Theme) SwaggerDoc() map[string]string { + return map_Theme +} + var map_AWSCSIDriverConfigSpec = map[string]string{ "": "AWSCSIDriverConfigSpec defines properties that can be configured for the AWS CSI driver.", "kmsKeyARN": "kmsKeyARN sets the cluster default storage class to encrypt volumes with a user-defined KMS key, rather than the default KMS key used by AWS. The value may be either the ARN or Alias ARN of a KMS key.", @@ -561,6 +602,7 @@ var map_VSphereCSIDriverConfigSpec = map[string]string{ "globalMaxSnapshotsPerBlockVolume": "globalMaxSnapshotsPerBlockVolume is a global configuration parameter that applies to volumes on all kinds of datastores. If omitted, the platform chooses a default, which is subject to change over time, currently that default is 3. Snapshots can not be disabled using this parameter. Increasing number of snapshots above 3 can have negative impact on performance, for more details see: https://kb.vmware.com/s/article/1025279 Volume snapshot documentation: https://docs.vmware.com/en/VMware-vSphere-Container-Storage-Plug-in/3.0/vmware-vsphere-csp-getting-started/GUID-E0B41C69-7EEB-450F-A73D-5FD2FF39E891.html", "granularMaxSnapshotsPerBlockVolumeInVSAN": "granularMaxSnapshotsPerBlockVolumeInVSAN is a granular configuration parameter on vSAN datastore only. It overrides GlobalMaxSnapshotsPerBlockVolume if set, while it falls back to the global constraint if unset. Snapshots for VSAN can not be disabled using this parameter.", "granularMaxSnapshotsPerBlockVolumeInVVOL": "granularMaxSnapshotsPerBlockVolumeInVVOL is a granular configuration parameter on Virtual Volumes datastore only. It overrides GlobalMaxSnapshotsPerBlockVolume if set, while it falls back to the global constraint if unset. Snapshots for VVOL can not be disabled using this parameter.", + "maxAllowedBlockVolumesPerNode": "maxAllowedBlockVolumesPerNode is an optional configuration parameter that allows setting a custom value for the limit of the number of PersistentVolumes attached to a node. In vSphere version 7 this limit was set to 59 by default, however in vSphere version 8 this limit was increased to 255. Before increasing this value above 59 the cluster administrator needs to ensure that every node forming the cluster is updated to ESXi version 8 or higher and that all nodes are running the same version. The limit must be between 1 and 255, which matches the vSphere version 8 maximum. When omitted, this means no opinion and the platform is left to choose a reasonable default, which is subject to change over time. The current default is 59, which matches the limit for vSphere version 7.", } func (VSphereCSIDriverConfigSpec) SwaggerDoc() map[string]string { @@ -1359,7 +1401,7 @@ func (MachineConfigurationList) SwaggerDoc() map[string]string { } var map_MachineConfigurationSpec = map[string]string{ - "managedBootImages": "managedBootImages allows configuration for the management of boot images for machine resources within the cluster. This configuration allows users to select resources that should be updated to the latest boot images during cluster upgrades, ensuring that new machines always boot with the current cluster version's boot image. When omitted, no boot images will be updated.", + "managedBootImages": "managedBootImages allows configuration for the management of boot images for machine resources within the cluster. This configuration allows users to select resources that should be updated to the latest boot images during cluster upgrades, ensuring that new machines always boot with the current cluster version's boot image. When omitted, this means no opinion and the platform is left to choose a reasonable default, which is subject to change over time. The default for each machine manager mode is All for GCP and AWS platforms, and None for all other platforms.", "nodeDisruptionPolicy": "nodeDisruptionPolicy allows an admin to set granular node disruption actions for MachineConfig-based updates, such as drains, service reloads, etc. Specifying this will allow for less downtime when doing small configuration updates to the cluster. This configuration has no effect on cluster upgrades which will still incur node disruption where required.", } @@ -1371,6 +1413,7 @@ var map_MachineConfigurationStatus = map[string]string{ "observedGeneration": "observedGeneration is the last generation change you've dealt with", "conditions": "conditions is a list of conditions and their status", "nodeDisruptionPolicyStatus": "nodeDisruptionPolicyStatus status reflects what the latest cluster-validated policies are, and will be used by the Machine Config Daemon during future node updates.", + "managedBootImagesStatus": "managedBootImagesStatus reflects what the latest cluster-validated boot image configuration is and will be used by Machine Config Controller while performing boot image updates.", } func (MachineConfigurationStatus) SwaggerDoc() map[string]string { @@ -1389,7 +1432,7 @@ func (MachineManager) SwaggerDoc() map[string]string { } var map_MachineManagerSelector = map[string]string{ - "mode": "mode determines how machine managers will be selected for updates. Valid values are All and Partial. All means that every resource matched by the machine manager will be updated. Partial requires specified selector(s) and allows customisation of which resources matched by the machine manager will be updated.", + "mode": "mode determines how machine managers will be selected for updates. Valid values are All and Partial. All means that every resource matched by the machine manager will be updated. Partial requires specified selector(s) and allows customisation of which resources matched by the machine manager will be updated. None means that every resource matched by the machine manager will not be updated.", "partial": "partial provides label selector(s) that can be used to match machine management resources. Only permitted when mode is set to \"Partial\".", } diff --git a/vendor/github.com/openshift/api/operator/v1alpha1/types_etcdbackup.go b/vendor/github.com/openshift/api/operator/v1alpha1/types_etcdbackup.go index 3c6f344b1e..fe56b0eab2 100644 --- a/vendor/github.com/openshift/api/operator/v1alpha1/types_etcdbackup.go +++ b/vendor/github.com/openshift/api/operator/v1alpha1/types_etcdbackup.go @@ -44,12 +44,10 @@ type EtcdBackupSpec struct { // +kubebuilder:validation:Optional type EtcdBackupStatus struct { // conditions provide details on the status of the etcd backup job. - // +patchMergeKey=type - // +patchStrategy=merge // +listType=map // +listMapKey=type // +optional - Conditions []metav1.Condition `json:"conditions" patchStrategy:"merge" patchMergeKey:"type"` + Conditions []metav1.Condition `json:"conditions,omitempty"` // backupJob is the reference to the Job that executes the backup. // Optional diff --git a/vendor/github.com/openshift/api/sharedresource/v1alpha1/types_shared_configmap.go b/vendor/github.com/openshift/api/sharedresource/v1alpha1/types_shared_configmap.go index 3b6e6be374..2a4a0d1b6c 100644 --- a/vendor/github.com/openshift/api/sharedresource/v1alpha1/types_shared_configmap.go +++ b/vendor/github.com/openshift/api/sharedresource/v1alpha1/types_shared_configmap.go @@ -93,7 +93,8 @@ type SharedConfigMapSpec struct { // SharedSecretStatus contains the observed status of the shared resource type SharedConfigMapStatus struct { // conditions represents any observations made on this particular shared resource by the underlying CSI driver or Share controller. - // +patchMergeKey=type - // +patchStrategy=merge - Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + // +listType=map + // +listMapKey=type + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` } diff --git a/vendor/github.com/openshift/api/sharedresource/v1alpha1/types_shared_secret.go b/vendor/github.com/openshift/api/sharedresource/v1alpha1/types_shared_secret.go index 3ea9260f0c..be06f97749 100644 --- a/vendor/github.com/openshift/api/sharedresource/v1alpha1/types_shared_secret.go +++ b/vendor/github.com/openshift/api/sharedresource/v1alpha1/types_shared_secret.go @@ -92,7 +92,8 @@ type SharedSecretSpec struct { // SharedSecretStatus contains the observed status of the shared resource type SharedSecretStatus struct { // conditions represents any observations made on this particular shared resource by the underlying CSI driver or Share controller. - // +patchMergeKey=type - // +patchStrategy=merge - Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + // +listType=map + // +listMapKey=type + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` } diff --git a/vendor/github.com/openshift/library-go/pkg/crypto/crypto.go b/vendor/github.com/openshift/library-go/pkg/crypto/crypto.go index 69bac6637c..80f5efc2c0 100644 --- a/vendor/github.com/openshift/library-go/pkg/crypto/crypto.go +++ b/vendor/github.com/openshift/library-go/pkg/crypto/crypto.go @@ -110,15 +110,6 @@ func DefaultTLSVersion() uint16 { return tls.VersionTLS12 } -// ciphersTLS13 copies golang 1.13 implementation, where TLS1.3 suites are not -// configurable (cipherSuites field is ignored for TLS1.3 flows and all of the -// below three - and none other - are used) -var ciphersTLS13 = map[string]uint16{ - "TLS_AES_128_GCM_SHA256": tls.TLS_AES_128_GCM_SHA256, - "TLS_AES_256_GCM_SHA384": tls.TLS_AES_256_GCM_SHA384, - "TLS_CHACHA20_POLY1305_SHA256": tls.TLS_CHACHA20_POLY1305_SHA256, -} - var ciphers = map[string]uint16{ "TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, @@ -144,6 +135,9 @@ var ciphers = map[string]uint16{ "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + "TLS_AES_128_GCM_SHA256": tls.TLS_AES_128_GCM_SHA256, + "TLS_AES_256_GCM_SHA384": tls.TLS_AES_256_GCM_SHA384, + "TLS_CHACHA20_POLY1305_SHA256": tls.TLS_CHACHA20_POLY1305_SHA256, } // openSSLToIANACiphersMap maps OpenSSL cipher suite names to IANA names @@ -223,10 +217,6 @@ func CipherSuite(cipherName string) (uint16, error) { return cipher, nil } - if _, ok := ciphersTLS13[cipherName]; ok { - return 0, fmt.Errorf("all golang TLSv1.3 ciphers are always used for TLSv1.3 flows") - } - return 0, fmt.Errorf("unknown cipher name %q", cipherName) } @@ -281,6 +271,9 @@ func DefaultCiphers() []uint16 { // tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, // forbidden by http/2, disabled to mitigate SWEET32 attack tls.TLS_RSA_WITH_AES_128_CBC_SHA, // forbidden by http/2 tls.TLS_RSA_WITH_AES_256_CBC_SHA, // forbidden by http/2 + tls.TLS_AES_128_GCM_SHA256, + tls.TLS_AES_256_GCM_SHA384, + tls.TLS_CHACHA20_POLY1305_SHA256, } } diff --git a/vendor/github.com/openshift/library-go/pkg/operator/apiserver/audit/manifests/allrequestbodies-rules.yaml b/vendor/github.com/openshift/library-go/pkg/operator/apiserver/audit/manifests/allrequestbodies-rules.yaml index bcb0a30de2..4b6844d2e5 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/apiserver/audit/manifests/allrequestbodies-rules.yaml +++ b/vendor/github.com/openshift/library-go/pkg/operator/apiserver/audit/manifests/allrequestbodies-rules.yaml @@ -8,5 +8,7 @@ resources: ["tokenreviews", "tokenrequests"] - group: "oauth.openshift.io" resources: ["oauthclients", "tokenreviews"] + - group: "machineconfiguration.openshift.io" + resource: ["machineconfig", "controllerconfig"] # catch-all rule to log all other requests with request and response payloads - level: RequestResponse diff --git a/vendor/github.com/openshift/library-go/pkg/operator/apiserver/audit/manifests/writerequestbodies-rules.yaml b/vendor/github.com/openshift/library-go/pkg/operator/apiserver/audit/manifests/writerequestbodies-rules.yaml index 68389fe30f..5302fad3db 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/apiserver/audit/manifests/writerequestbodies-rules.yaml +++ b/vendor/github.com/openshift/library-go/pkg/operator/apiserver/audit/manifests/writerequestbodies-rules.yaml @@ -8,6 +8,8 @@ resources: ["tokenreviews", "tokenrequests"] - group: "oauth.openshift.io" resources: ["oauthclients", "tokenreviews"] + - group: "machineconfiguration.openshift.io" + resource: ["machineconfig", "controllerconfig"] # log request and response payloads for all write requests - level: RequestResponse verbs: diff --git a/vendor/github.com/openshift/library-go/pkg/operator/apiserver/controller/workload/workload.go b/vendor/github.com/openshift/library-go/pkg/operator/apiserver/controller/workload/workload.go index ce81f6be6a..7eb495571a 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/apiserver/controller/workload/workload.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/apiserver/controller/workload/workload.go @@ -4,6 +4,8 @@ import ( "context" "errors" "fmt" + "strings" + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -14,11 +16,11 @@ import ( corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/tools/cache" "k8s.io/client-go/util/workqueue" - "strings" operatorv1 "github.com/openshift/api/operator/v1" openshiftconfigclientv1 "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1" applyoperatorv1 "github.com/openshift/client-go/operator/applyconfigurations/operator/v1" + "github.com/openshift/library-go/pkg/apiserver/jsonpatch" "github.com/openshift/library-go/pkg/apps/deployment" "github.com/openshift/library-go/pkg/controller/factory" "github.com/openshift/library-go/pkg/operator/events" @@ -33,7 +35,13 @@ const ( // Delegate captures a set of methods that hold a custom logic type Delegate interface { // Sync a method that will be used for delegation. It should bring the desired workload into operation. - Sync(ctx context.Context, controllerContext factory.SyncContext) (*appsv1.Deployment, bool, []error) + // + // It returns a reference to the workload, a bool indicating whether the operator config is at the highest + // generation, a bool indicating whether the operator status conditions must be removed (e.g. in case the + // delegate intentionally deletes the workload), two strings indicating the name & namespace of the workload + // that must be cleaned up from the operator status (respective to the conditions to be removed) and a list + // errors. + Sync(ctx context.Context, controllerContext factory.SyncContext) (*appsv1.Deployment, bool, bool, string, string, []error) // PreconditionFulfilled a method that indicates whether all prerequisites are met and we can Sync. // @@ -81,7 +89,7 @@ func NewController(instanceName, operatorNamespace, targetNamespace, targetOpera kubeClient kubernetes.Interface, podLister corev1listers.PodLister, informers []factory.Informer, - tagetNamespaceInformers []factory.Informer, + targetNamespaceInformers []factory.Informer, delegate Delegate, openshiftClusterConfigClient openshiftconfigclientv1.ClusterOperatorInterface, eventRecorder events.Recorder, @@ -100,11 +108,11 @@ func NewController(instanceName, operatorNamespace, targetNamespace, targetOpera delegate: delegate, openshiftClusterConfigClient: openshiftClusterConfigClient, versionRecorder: versionRecorder, - queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), instanceName), + queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultTypedControllerRateLimiter[any](), instanceName), } c := factory.New() - for _, nsi := range tagetNamespaceInformers { + for _, nsi := range targetNamespaceInformers { c.WithNamespaceInformer(nsi, targetNamespace) } @@ -128,14 +136,14 @@ func (c *Controller) sync(ctx context.Context, controllerContext factory.SyncCon } if fulfilled, err := c.delegate.PreconditionFulfilled(ctx); err != nil { - return c.updateOperatorStatus(ctx, operatorStatus, nil, false, false, []error{err}) + return c.updateOperatorStatus(ctx, operatorStatus, nil, false, false, false, "", "", []error{err}) } else if !fulfilled { - return c.updateOperatorStatus(ctx, operatorStatus, nil, false, false, nil) + return c.updateOperatorStatus(ctx, operatorStatus, nil, false, false, false, "", "", nil) } - workload, operatorConfigAtHighestGeneration, errs := c.delegate.Sync(ctx, controllerContext) + workload, operatorConfigAtHighestGeneration, removeConditions, removedWorkloadName, removedWorkloadNamespace, errs := c.delegate.Sync(ctx, controllerContext) - return c.updateOperatorStatus(ctx, operatorStatus, workload, operatorConfigAtHighestGeneration, true, errs) + return c.updateOperatorStatus(ctx, operatorStatus, workload, operatorConfigAtHighestGeneration, true, removeConditions, removedWorkloadName, removedWorkloadNamespace, errs) } // shouldSync checks ManagementState to determine if we can run this operator, probably set by a cluster administrator. @@ -156,48 +164,84 @@ func (c *Controller) shouldSync(ctx context.Context, operatorSpec *operatorv1.Op } } -// updateOperatorStatus updates the status based on the actual workload and errors that might have occurred during synchronization. -func (c *Controller) updateOperatorStatus(ctx context.Context, previousStatus *operatorv1.OperatorStatus, workload *appsv1.Deployment, operatorConfigAtHighestGeneration bool, preconditionsReady bool, errs []error) (err error) { +func (c *Controller) updateOperatorStatus(ctx context.Context, + previousStatus *operatorv1.OperatorStatus, + workload *appsv1.Deployment, + operatorConfigAtHighestGeneration, preconditionsReady, removeConditions bool, + removedWorkloadName, removedWorkloadNamespace string, + errs []error, +) (err error) { + if errs == nil { errs = []error{} } - deploymentAvailableCondition := applyoperatorv1.OperatorCondition(). - WithType(fmt.Sprintf("%sDeployment%s", c.conditionsPrefix, operatorv1.OperatorStatusTypeAvailable)) + typeAvailable := fmt.Sprintf("%sDeployment%s", c.conditionsPrefix, operatorv1.OperatorStatusTypeAvailable) + typeDegraded := fmt.Sprintf("%sDeployment%s", c.conditionsPrefix, operatorv1.OperatorStatusTypeDegraded) + typeProgressing := fmt.Sprintf("%sDeployment%s", c.conditionsPrefix, operatorv1.OperatorStatusTypeProgressing) + typeWorkloadDegraded := fmt.Sprintf("%sWorkload%s", c.conditionsPrefix, operatorv1.OperatorStatusTypeDegraded) - workloadDegradedCondition := applyoperatorv1.OperatorCondition(). - WithType(fmt.Sprintf("%sWorkloadDegraded", c.conditionsPrefix)) + deploymentAvailableCondition := applyoperatorv1.OperatorCondition().WithType(typeAvailable) + deploymentDegradedCondition := applyoperatorv1.OperatorCondition().WithType(typeDegraded) + deploymentProgressingCondition := applyoperatorv1.OperatorCondition().WithType(typeProgressing) + workloadDegradedCondition := applyoperatorv1.OperatorCondition().WithType(typeWorkloadDegraded) - deploymentDegradedCondition := applyoperatorv1.OperatorCondition(). - WithType(fmt.Sprintf("%sDeploymentDegraded", c.conditionsPrefix)) + status := applyoperatorv1.OperatorStatus() + mustApplyStatus := false + statusCleanupPatch := jsonpatch.New() - deploymentProgressingCondition := applyoperatorv1.OperatorCondition(). - WithType(fmt.Sprintf("%sDeployment%s", c.conditionsPrefix, operatorv1.OperatorStatusTypeProgressing)) + // remove operator status conditions if requested by the delegate; otherwise update them + // if necessary + if removeConditions { + statusCleanupPatch = v1helpers.RemoveConditionsJSONPatch(previousStatus, []string{typeAvailable, typeDegraded, typeProgressing, typeWorkloadDegraded}, statusCleanupPatch) - status := applyoperatorv1.OperatorStatus() - if workload != nil { - // The Hash field is not required since the LastGeneration field is enough to uniquely identify a Deployment's desired state - status = status.WithGenerations(applyoperatorv1.GenerationStatus(). - WithGroup("apps"). - WithResource("deployments"). - WithNamespace(workload.Namespace). - WithName(workload.Name). - WithLastGeneration(workload.Generation), - ) + } else { + status = c.updateOperatorStatusConditions(previousStatus, status, workload, preconditionsReady, deploymentAvailableCondition, deploymentDegradedCondition, deploymentProgressingCondition, workloadDegradedCondition, errs) + mustApplyStatus = true } - defer func() { - status = status.WithConditions( - deploymentAvailableCondition, - deploymentDegradedCondition, - deploymentProgressingCondition, - workloadDegradedCondition, - ) + // remove operator status generations and version if requested by the delegate; otherwise update them + // if necessary + if len(removedWorkloadName) > 0 && len(removedWorkloadNamespace) > 0 { + statusCleanupPatch = v1helpers.RemoveWorkloadGenerationsJSONPatch(previousStatus, removedWorkloadName, removedWorkloadNamespace, statusCleanupPatch) + + c.setVersion(removedWorkloadName, "") - if applyError := c.operatorClient.ApplyOperatorStatus(ctx, c.controllerInstanceName, status); applyError != nil { - err = applyError + } else { + status = c.updateOperatorStatusGenerationsVersion(status, workload, operatorConfigAtHighestGeneration, preconditionsReady) + mustApplyStatus = true + } + + if mustApplyStatus { + if applyErr := c.operatorClient.ApplyOperatorStatus(ctx, c.controllerInstanceName, status); applyErr != nil { + return applyErr } - }() + } + + if !statusCleanupPatch.IsEmpty() { + if patchErr := c.operatorClient.PatchOperatorStatus(ctx, statusCleanupPatch); patchErr != nil { + return patchErr + } + } + + return kerrors.NewAggregate(errs) +} + +func (c *Controller) updateOperatorStatusConditions( + previousStatus *operatorv1.OperatorStatus, + statusApplyConfig *applyoperatorv1.OperatorStatusApplyConfiguration, + workload *appsv1.Deployment, + preconditionsReady bool, + deploymentAvailableCondition, deploymentDegradedCondition, deploymentProgressingCondition, workloadDegradedCondition *applyoperatorv1.OperatorConditionApplyConfiguration, + errs []error, +) *applyoperatorv1.OperatorStatusApplyConfiguration { + + defer statusApplyConfig.WithConditions( + deploymentAvailableCondition, + deploymentDegradedCondition, + deploymentProgressingCondition, + workloadDegradedCondition, + ) if !preconditionsReady { var message string @@ -228,7 +272,7 @@ func (c *Controller) updateOperatorStatus(ctx context.Context, previousStatus *o WithReason("PreconditionNotFulfilled"). WithMessage(message) - return kerrors.NewAggregate(errs) + return statusApplyConfig } if len(errs) > 0 { @@ -267,7 +311,7 @@ func (c *Controller) updateOperatorStatus(ctx context.Context, previousStatus *o WithReason("NoDeployment"). WithMessage(message) - return kerrors.NewAggregate(errs) + return statusApplyConfig } if workload.Status.AvailableReplicas == 0 { @@ -336,22 +380,55 @@ func (c *Controller) updateOperatorStatus(ctx context.Context, previousStatus *o WithReason("AsExpected") } + return statusApplyConfig +} + +func (c *Controller) updateOperatorStatusGenerationsVersion( + statusApplyConfig *applyoperatorv1.OperatorStatusApplyConfiguration, + workload *appsv1.Deployment, + operatorConfigAtHighestGeneration, preconditionsReady bool, +) *applyoperatorv1.OperatorStatusApplyConfiguration { + + if workload == nil { + return statusApplyConfig + } + + statusApplyConfig = statusApplyConfig.WithGenerations(applyoperatorv1.GenerationStatus(). + WithGroup("apps"). + WithResource("deployments"). + WithNamespace(workload.Namespace). + WithName(workload.Name). + WithLastGeneration(workload.Generation), + ) + + if !preconditionsReady { + return statusApplyConfig + } + + desiredReplicas := int32(1) + if workload.Spec.Replicas != nil { + desiredReplicas = *(workload.Spec.Replicas) + } + + workloadAtHighestGeneration := workload.ObjectMeta.Generation == workload.Status.ObservedGeneration + workloadHasAllPodsAvailable := workload.Status.AvailableReplicas >= desiredReplicas + // if the deployment is all available and at the expected generation, then update the version to the latest // when we update, the image pull spec should immediately be different, which should immediately cause a deployment rollout // which should immediately result in a deployment generation diff, which should cause this block to be skipped until it is ready. workloadHasAllPodsUpdated := workload.Status.UpdatedReplicas == desiredReplicas if workloadAtHighestGeneration && workloadHasAllPodsAvailable && workloadHasAllPodsUpdated && operatorConfigAtHighestGeneration { - operandName := workload.Name - if len(c.operandNamePrefix) > 0 { - operandName = fmt.Sprintf("%s-%s", c.operandNamePrefix, workload.Name) - } - c.versionRecorder.SetVersion(operandName, c.targetOperandVersion) + c.setVersion(workload.Name, c.targetOperandVersion) } - if len(errs) > 0 { - return kerrors.NewAggregate(errs) + return statusApplyConfig +} + +func (c *Controller) setVersion(operandName, version string) { + if len(c.operandNamePrefix) > 0 { + operandName = fmt.Sprintf("%s-%s", c.operandNamePrefix, operandName) } - return nil + c.versionRecorder.SetVersion(operandName, version) } // hasDeploymentProgressed returns true if the deployment reports NewReplicaSetAvailable diff --git a/vendor/github.com/openshift/library-go/pkg/operator/genericoperatorclient/dynamic_operator_client.go b/vendor/github.com/openshift/library-go/pkg/operator/genericoperatorclient/dynamic_operator_client.go index 1c09bea361..b761347619 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/genericoperatorclient/dynamic_operator_client.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/genericoperatorclient/dynamic_operator_client.go @@ -347,15 +347,6 @@ func (c dynamicOperatorClient) applyOperatorStatus(ctx context.Context, fieldMan } } - for _, curr := range desiredConfiguration.Conditions { - if len(ptr.Deref(curr.Reason, "")) == 0 { - klog.Warningf(".status.conditions[%q].reason is missing; this will eventually be fatal", *curr.Type) - } - if len(ptr.Deref(curr.Message, "")) == 0 { - klog.Warningf(".status.conditions[%q].message is missing; this will eventually be fatal", *curr.Type) - } - } - desiredStatus, err := runtime.DefaultUnstructuredConverter.ToUnstructured(desiredConfiguration) if err != nil { return fmt.Errorf("failed to convert to unstructured: %w", err) diff --git a/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceapply/generic.go b/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceapply/generic.go index 357efad619..9105464bd0 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceapply/generic.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceapply/generic.go @@ -9,6 +9,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" policyv1 "k8s.io/api/policy/v1" rbacv1 "k8s.io/api/rbac/v1" storagev1 "k8s.io/api/storage/v1" @@ -142,6 +143,12 @@ func ApplyDirectly(ctx context.Context, clients *ClientHolder, recorder events.R } else { result.Result, result.Changed, result.Error = ApplySecretImproved(ctx, client, recorder, t, cache) } + case *networkingv1.NetworkPolicy: + if clients.kubeClient == nil { + result.Error = fmt.Errorf("missing kubeClient") + } else { + result.Result, result.Changed, result.Error = ApplyNetworkPolicy(ctx, clients.kubeClient.NetworkingV1(), recorder, t) + } case *rbacv1.ClusterRole: if clients.kubeClient == nil { result.Error = fmt.Errorf("missing kubeClient") @@ -208,6 +215,18 @@ func ApplyDirectly(ctx context.Context, clients *ClientHolder, recorder events.R } else { result.Result, result.Changed, result.Error = ApplyValidatingAdmissionPolicyBindingV1beta1(ctx, clients.kubeClient.AdmissionregistrationV1beta1(), recorder, t, cache) } + case *admissionregistrationv1.ValidatingAdmissionPolicy: + if clients.kubeClient == nil { + result.Error = fmt.Errorf("missing kubeClient") + } else { + result.Result, result.Changed, result.Error = ApplyValidatingAdmissionPolicyV1(ctx, clients.kubeClient.AdmissionregistrationV1(), recorder, t, cache) + } + case *admissionregistrationv1.ValidatingAdmissionPolicyBinding: + if clients.kubeClient == nil { + result.Error = fmt.Errorf("missing kubeClient") + } else { + result.Result, result.Changed, result.Error = ApplyValidatingAdmissionPolicyBindingV1(ctx, clients.kubeClient.AdmissionregistrationV1(), recorder, t, cache) + } case *storagev1.CSIDriver: if clients.kubeClient == nil { result.Error = fmt.Errorf("missing kubeClient") @@ -295,6 +314,12 @@ func DeleteAll(ctx context.Context, clients *ClientHolder, recorder events.Recor } else { _, result.Changed, result.Error = DeleteSecret(ctx, client, recorder, t) } + case *networkingv1.NetworkPolicy: + if clients.kubeClient == nil { + result.Error = fmt.Errorf("missing kubeClient") + } else { + _, result.Changed, result.Error = DeleteNetworkPolicy(ctx, clients.kubeClient.NetworkingV1(), recorder, t) + } case *rbacv1.ClusterRole: if clients.kubeClient == nil { result.Error = fmt.Errorf("missing kubeClient") diff --git a/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceapply/networking.go b/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceapply/networking.go new file mode 100644 index 0000000000..0a3df326e4 --- /dev/null +++ b/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceapply/networking.go @@ -0,0 +1,59 @@ +package resourceapply + +import ( + "context" + + networkingv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/api/equality" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + networkingclientv1 "k8s.io/client-go/kubernetes/typed/networking/v1" + "k8s.io/klog/v2" + + "github.com/openshift/library-go/pkg/operator/events" + "github.com/openshift/library-go/pkg/operator/resource/resourcehelper" + "github.com/openshift/library-go/pkg/operator/resource/resourcemerge" +) + +// ApplyClusterRole merges objectmeta, does not worry about anything else +func ApplyNetworkPolicy(ctx context.Context, client networkingclientv1.NetworkPoliciesGetter, recorder events.Recorder, required *networkingv1.NetworkPolicy) (*networkingv1.NetworkPolicy, bool, error) { + existing, err := client.NetworkPolicies(required.Namespace).Get(ctx, required.Name, metav1.GetOptions{}) + if apierrors.IsNotFound(err) { + requiredCopy := required.DeepCopy() + actual, err := client.NetworkPolicies(required.Namespace).Create( + ctx, resourcemerge.WithCleanLabelsAndAnnotations(requiredCopy).(*networkingv1.NetworkPolicy), metav1.CreateOptions{}) + resourcehelper.ReportCreateEvent(recorder, required, err) + return actual, true, err + } + if err != nil { + return nil, false, err + } + + modified := false + existingCopy := existing.DeepCopy() + + resourcemerge.EnsureObjectMeta(&modified, &existingCopy.ObjectMeta, required.ObjectMeta) + if equality.Semantic.DeepEqual(existingCopy.Spec, required.Spec) && !modified { + return existingCopy, false, nil + } + + if klog.V(2).Enabled() { + klog.Infof("NetworkPolicy %q changes: %v", required.Name, JSONPatchNoError(existing, existingCopy)) + } + + actual, err := client.NetworkPolicies(existingCopy.Namespace).Update(ctx, existingCopy, metav1.UpdateOptions{}) + resourcehelper.ReportUpdateEvent(recorder, required, err) + return actual, true, err +} + +func DeleteNetworkPolicy(ctx context.Context, client networkingclientv1.NetworkPoliciesGetter, recorder events.Recorder, required *networkingv1.NetworkPolicy) (*networkingv1.NetworkPolicy, bool, error) { + err := client.NetworkPolicies(required.Namespace).Delete(ctx, required.Name, metav1.DeleteOptions{}) + if err != nil && apierrors.IsNotFound(err) { + return nil, false, nil + } + if err != nil { + return nil, false, err + } + resourcehelper.ReportDeleteEvent(recorder, required, err) + return nil, true, nil +} diff --git a/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceread/networking.go b/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceread/networking.go new file mode 100644 index 0000000000..9832ede719 --- /dev/null +++ b/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceread/networking.go @@ -0,0 +1,26 @@ +package resourceread + +import ( + networkingv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/serializer" +) + +var ( + netScheme = runtime.NewScheme() + netCodecs = serializer.NewCodecFactory(netScheme) +) + +func init() { + if err := networkingv1.AddToScheme(netScheme); err != nil { + panic(err) + } +} + +func ReadNetworkPolicyV1OrDie(objBytes []byte) *networkingv1.NetworkPolicy { + requiredObj, err := runtime.Decode(coreCodecs.UniversalDecoder(networkingv1.SchemeGroupVersion), objBytes) + if err != nil { + panic(err) + } + return requiredObj.(*networkingv1.NetworkPolicy) +} diff --git a/vendor/github.com/openshift/library-go/pkg/operator/staleconditions/remove_stale_conditions.go b/vendor/github.com/openshift/library-go/pkg/operator/staleconditions/remove_stale_conditions.go index 3e2e270d50..3cac3e3c73 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/staleconditions/remove_stale_conditions.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/staleconditions/remove_stale_conditions.go @@ -2,10 +2,8 @@ package staleconditions import ( "context" - "fmt" "time" - "github.com/openshift/library-go/pkg/apiserver/jsonpatch" "github.com/openshift/library-go/pkg/controller/factory" "github.com/openshift/library-go/pkg/operator/events" "github.com/openshift/library-go/pkg/operator/v1helpers" @@ -45,25 +43,7 @@ func (c RemoveStaleConditionsController) sync(ctx context.Context, syncContext f return err } - var removedCount int - jsonPatch := jsonpatch.New() - for i, existingCondition := range operatorStatus.Conditions { - for _, conditionTypeToRemove := range c.conditionTypesToRemove { - if existingCondition.Type != conditionTypeToRemove { - continue - } - removeAtIndex := i - if !jsonPatch.IsEmpty() { - removeAtIndex = removeAtIndex - removedCount - } - jsonPatch.WithRemove( - fmt.Sprintf("/status/conditions/%d", removeAtIndex), - jsonpatch.NewTestCondition(fmt.Sprintf("/status/conditions/%d/type", removeAtIndex), conditionTypeToRemove), - ) - removedCount++ - } - } - + jsonPatch := v1helpers.RemoveConditionsJSONPatch(operatorStatus, c.conditionTypesToRemove, nil) if jsonPatch.IsEmpty() { return nil } diff --git a/vendor/github.com/openshift/library-go/pkg/operator/staticresourcecontroller/static_resource_controller.go b/vendor/github.com/openshift/library-go/pkg/operator/staticresourcecontroller/static_resource_controller.go index 234cc08eba..22a377a7c4 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/staticresourcecontroller/static_resource_controller.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/staticresourcecontroller/static_resource_controller.go @@ -17,6 +17,7 @@ import ( "k8s.io/client-go/restmapper" corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" policyv1 "k8s.io/api/policy/v1" rbacv1 "k8s.io/api/rbac/v1" storagev1 "k8s.io/api/storage/v1" @@ -236,6 +237,8 @@ func (c *StaticResourceController) AddKubeInformers(kubeInformersByNamespace v1h ret = ret.AddInformer(informer.Core().V1().ConfigMaps().Informer()) case *corev1.Secret: ret = ret.AddInformer(informer.Core().V1().Secrets().Informer()) + case *networkingv1.NetworkPolicy: + ret = ret.AddInformer(informer.Networking().V1().NetworkPolicies().Informer()) case *rbacv1.ClusterRole: ret = ret.AddInformer(informer.Rbac().V1().ClusterRoles().Informer()) case *rbacv1.ClusterRoleBinding: diff --git a/vendor/github.com/openshift/library-go/pkg/operator/status/status_controller.go b/vendor/github.com/openshift/library-go/pkg/operator/status/status_controller.go index c270551525..3d5c98cf7b 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/status/status_controller.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/status/status_controller.go @@ -2,11 +2,11 @@ package status import ( "context" - "k8s.io/utils/clock" "strings" "time" "k8s.io/klog/v2" + "k8s.io/utils/clock" configv1 "github.com/openshift/api/config/v1" operatorv1 "github.com/openshift/api/operator/v1" @@ -53,6 +53,7 @@ type StatusSyncer struct { degradedInertia Inertia removeUnusedVersions bool + removeEmptyVersions bool } var _ factory.Controller = &StatusSyncer{} @@ -130,6 +131,14 @@ func (c *StatusSyncer) WithVersionRemoval() *StatusSyncer { return &output } +// WithEmptyVersionRemoval returns a copy of the StatusSyncer that will +// remove versions that are an empty string in VersionGetter from the status. +func (c *StatusSyncer) WithEmptyVersionRemoval() *StatusSyncer { + output := *c + output.removeEmptyVersions = true + return &output +} + // sync reacts to a change in prereqs by finding information that is required to match another value in the cluster. This // must be information that is logically "owned" by another component. func (c StatusSyncer) Sync(ctx context.Context, syncCtx factory.SyncContext) error { @@ -259,6 +268,17 @@ func (c *StatusSyncer) syncStatusVersions(clusterOperatorObj *configv1.ClusterOp } } + if c.removeEmptyVersions { + filteredVersions := make([]configv1.OperandVersion, 0, len(clusterOperatorObj.Status.Versions)) + for _, version := range clusterOperatorObj.Status.Versions { + if len(version.Version) > 0 { + filteredVersions = append(filteredVersions, version) + } + } + + clusterOperatorObj.Status.Versions = filteredVersions + } + if !c.removeUnusedVersions { return } diff --git a/vendor/github.com/openshift/library-go/pkg/operator/v1helpers/helpers.go b/vendor/github.com/openshift/library-go/pkg/operator/v1helpers/helpers.go index fd34ec6201..1e6adaa549 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/v1helpers/helpers.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/v1helpers/helpers.go @@ -12,6 +12,7 @@ import ( "github.com/google/go-cmp/cmp" configv1 "github.com/openshift/api/config/v1" operatorv1 "github.com/openshift/api/operator/v1" + "github.com/openshift/library-go/pkg/apiserver/jsonpatch" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -565,3 +566,63 @@ func IsUpdatingTooLong(operatorStatus *operatorv1.OperatorStatus, progressingCon progressing := FindOperatorCondition(operatorStatus.Conditions, progressingConditionType) return progressing != nil && progressing.Status == operatorv1.ConditionTrue && time.Now().After(progressing.LastTransitionTime.Add(progressingConditionTimeout)) } + +func RemoveConditionsJSONPatch(operatorStatus *operatorv1.OperatorStatus, conditionTypesToRemove []string, jsonPatch *jsonpatch.PatchSet) *jsonpatch.PatchSet { + if operatorStatus == nil { + return jsonPatch + } + + if jsonPatch == nil { + jsonPatch = jsonpatch.New() + } + + var removedCount int + for i, cond := range operatorStatus.Conditions { + for _, conditionTypeToRemove := range conditionTypesToRemove { + if cond.Type != conditionTypeToRemove { + continue + } + removeAtIndex := i + if !jsonPatch.IsEmpty() { + removeAtIndex = removeAtIndex - removedCount + } + jsonPatch.WithRemove( + fmt.Sprintf("/status/conditions/%d", removeAtIndex), + jsonpatch.NewTestCondition(fmt.Sprintf("/status/conditions/%d/type", removeAtIndex), conditionTypeToRemove), + ) + removedCount++ + } + } + + return jsonPatch +} + +func RemoveWorkloadGenerationsJSONPatch(operatorStatus *operatorv1.OperatorStatus, name, namespace string, jsonPatch *jsonpatch.PatchSet) *jsonpatch.PatchSet { + if operatorStatus == nil { + return jsonPatch + } + + if jsonPatch == nil { + jsonPatch = jsonpatch.New() + } + + var removedCount int + for i, gen := range operatorStatus.Generations { + if gen.Name != name || gen.Namespace != namespace || gen.Group != "apps" || gen.Resource != "deployments" { + continue + } + removeAtIndex := i + if !jsonPatch.IsEmpty() { + removeAtIndex = removeAtIndex - removedCount + } + + path := fmt.Sprintf("/status/generations/%d", removeAtIndex) + jsonPatch.WithTest(path+"/namespace", namespace) + jsonPatch.WithTest(path+"/group", "apps") + jsonPatch.WithTest(path+"/resource", "deployments") + jsonPatch.WithRemove(path, jsonpatch.NewTestCondition(path+"/name", name)) + removedCount++ + } + + return jsonPatch +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 87960e0614..a20990c306 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -180,7 +180,7 @@ github.com/modern-go/reflect2 # github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 ## explicit github.com/munnerz/goautoneg -# github.com/openshift/api v0.0.0-20250305225826-b8da3bfeaf77 +# github.com/openshift/api v0.0.0-20250320170726-75d64d71980b ## explicit; go 1.23.0 github.com/openshift/api github.com/openshift/api/annotations @@ -321,7 +321,7 @@ github.com/openshift/client-go/user/applyconfigurations/internal github.com/openshift/client-go/user/applyconfigurations/user/v1 github.com/openshift/client-go/user/clientset/versioned/scheme github.com/openshift/client-go/user/clientset/versioned/typed/user/v1 -# github.com/openshift/library-go v0.0.0-20250319141325-07c53d93ad06 +# github.com/openshift/library-go v0.0.0-20250319141325-07c53d93ad06 => github.com/liouk/library-go v0.0.0-20250605114318-fc8d3885f9ce ## explicit; go 1.23.0 github.com/openshift/library-go/pkg/apiserver/jsonpatch github.com/openshift/library-go/pkg/apps/deployment @@ -1501,3 +1501,4 @@ sigs.k8s.io/structured-merge-diff/v4/value ## explicit; go 1.12 sigs.k8s.io/yaml sigs.k8s.io/yaml/goyaml.v2 +# github.com/openshift/library-go => github.com/liouk/library-go v0.0.0-20250605114318-fc8d3885f9ce