From a8dcb76f6fe7c1680c151ed44afc4e0eb227a736 Mon Sep 17 00:00:00 2001 From: Ram Lavi Date: Mon, 27 May 2024 10:46:25 +0300 Subject: [PATCH] virt-operator: Deploy node-restriction policy virt-operator will deploy the validatingAdmission policy and binding objects implementing the node restriction for virt-handler. Signed-off-by: Ram Lavi --- pkg/controller/virtinformers.go | 50 ++++++ pkg/virt-operator/application.go | 30 ++++ pkg/virt-operator/kubevirt.go | 76 +++++--- pkg/virt-operator/kubevirt_test.go | 105 ++++++++--- .../resource/apply/admissionregistration.go | 167 ++++++++++++++++++ pkg/virt-operator/resource/apply/delete.go | 36 ++++ pkg/virt-operator/resource/apply/reconcile.go | 79 ++++++++- .../resource/generate/install/strategy.go | 67 +++++-- pkg/virt-operator/util/BUILD.bazel | 1 + pkg/virt-operator/util/client.go | 43 +++++ pkg/virt-operator/util/types.go | 160 +++++++++-------- 11 files changed, 683 insertions(+), 131 deletions(-) diff --git a/pkg/controller/virtinformers.go b/pkg/controller/virtinformers.go index 49c3ab3e93ed..1441b0788318 100644 --- a/pkg/controller/virtinformers.go +++ b/pkg/controller/virtinformers.go @@ -288,6 +288,18 @@ type KubeInformerFactory interface { // Fake ServiceMonitor informer used when Prometheus is not installed DummyOperatorServiceMonitor() cache.SharedIndexInformer + // ValidatingAdmissionPolicyBinding created/managed by virt operator + OperatorValidatingAdmissionPolicyBinding() cache.SharedIndexInformer + + // Fake OperatorValidatingAdmissionPolicyBinding informer used when ValidatingAdmissionPolicyBinding is not installed + DummyOperatorValidatingAdmissionPolicyBinding() cache.SharedIndexInformer + + // ValidatingAdmissionPolicies created/managed by virt operator + OperatorValidatingAdmissionPolicy() cache.SharedIndexInformer + + // Fake OperatorValidatingAdmissionPolicy informer used when ValidatingAdmissionPolicy is not installed + DummyOperatorValidatingAdmissionPolicy() cache.SharedIndexInformer + // The namespace where kubevirt is deployed in Namespace() cache.SharedIndexInformer @@ -1283,6 +1295,44 @@ func (f *kubeInformerFactory) DummyOperatorServiceMonitor() cache.SharedIndexInf }) } +func (f *kubeInformerFactory) OperatorValidatingAdmissionPolicyBinding() cache.SharedIndexInformer { + return f.getInformer("operatorValidatingAdmissionPolicyBindingInformer", func() cache.SharedIndexInformer { + labelSelector, err := labels.Parse(OperatorLabel) + if err != nil { + panic(err) + } + + lw := NewListWatchFromClient(f.clientSet.AdmissionregistrationV1().RESTClient(), "validatingadmissionpolicybindings", k8sv1.NamespaceAll, fields.Everything(), labelSelector) + return cache.NewSharedIndexInformer(lw, &admissionregistrationv1.ValidatingAdmissionPolicyBinding{}, f.defaultResync, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) + }) +} + +func (f *kubeInformerFactory) DummyOperatorValidatingAdmissionPolicyBinding() cache.SharedIndexInformer { + return f.getInformer("FakeOperatorValidatingAdmissionPolicyBindingInformer", func() cache.SharedIndexInformer { + informer, _ := testutils.NewFakeInformerFor(&admissionregistrationv1.ValidatingAdmissionPolicyBinding{}) + return informer + }) +} + +func (f *kubeInformerFactory) OperatorValidatingAdmissionPolicy() cache.SharedIndexInformer { + return f.getInformer("operatorValidatingAdmissionPolicyInformer", func() cache.SharedIndexInformer { + labelSelector, err := labels.Parse(OperatorLabel) + if err != nil { + panic(err) + } + + lw := NewListWatchFromClient(f.clientSet.AdmissionregistrationV1().RESTClient(), "validatingadmissionpolicies", k8sv1.NamespaceAll, fields.Everything(), labelSelector) + return cache.NewSharedIndexInformer(lw, &admissionregistrationv1.ValidatingAdmissionPolicy{}, f.defaultResync, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) + }) +} + +func (f *kubeInformerFactory) DummyOperatorValidatingAdmissionPolicy() cache.SharedIndexInformer { + return f.getInformer("FakeOperatorValidatingAdmissionPolicyInformer", func() cache.SharedIndexInformer { + informer, _ := testutils.NewFakeInformerFor(&admissionregistrationv1.ValidatingAdmissionPolicy{}) + return informer + }) +} + func (f *kubeInformerFactory) K8SInformerFactory() informers.SharedInformerFactory { return f.k8sInformers } diff --git a/pkg/virt-operator/application.go b/pkg/virt-operator/application.go index af994c4c797e..0a7830e6f766 100644 --- a/pkg/virt-operator/application.go +++ b/pkg/virt-operator/application.go @@ -270,6 +270,36 @@ func Execute() { app.stores.PrometheusRuleCache = app.informerFactory.DummyOperatorPrometheusRule().GetStore() } + validatingAdmissionPolicyBindingEnabled, err := util.IsValidatingAdmissionPolicyBindingEnabled(app.clientSet) + if err != nil { + golog.Fatalf("Error checking for ValidatingAdmissionPolicyBinding: %v", err) + } + if validatingAdmissionPolicyBindingEnabled { + log.Log.Info("validatingAdmissionPolicyBindingEnabled is defined") + app.informers.ValidatingAdmissionPolicyBinding = app.informerFactory.OperatorValidatingAdmissionPolicyBinding() + app.stores.ValidatingAdmissionPolicyBindingCache = app.informerFactory.OperatorValidatingAdmissionPolicyBinding().GetStore() + app.stores.ValidatingAdmissionPolicyBindingEnabled = true + } else { + log.Log.Info("validatingAdmissionPolicyBindingEnabled is not defined") + app.informers.ValidatingAdmissionPolicyBinding = app.informerFactory.DummyOperatorValidatingAdmissionPolicyBinding() + app.stores.ValidatingAdmissionPolicyBindingCache = app.informerFactory.DummyOperatorValidatingAdmissionPolicyBinding().GetStore() + } + + validatingAdmissionPolicyEnabled, err := util.IsValidatingAdmissionPolicyEnabled(app.clientSet) + if err != nil { + golog.Fatalf("Error checking for ValidatingAdmissionPolicy: %v", err) + } + if validatingAdmissionPolicyEnabled { + log.Log.Info("validatingAdmissionPolicyEnabled is defined") + app.informers.ValidatingAdmissionPolicy = app.informerFactory.OperatorValidatingAdmissionPolicy() + app.stores.ValidatingAdmissionPolicyCache = app.informerFactory.OperatorValidatingAdmissionPolicy().GetStore() + app.stores.ValidatingAdmissionPolicyEnabled = true + } else { + log.Log.Info("validatingAdmissionPolicyEnabled is not defined") + app.informers.ValidatingAdmissionPolicy = app.informerFactory.DummyOperatorValidatingAdmissionPolicy() + app.stores.ValidatingAdmissionPolicyCache = app.informerFactory.DummyOperatorValidatingAdmissionPolicy().GetStore() + } + app.prepareCertManagers() app.kubeVirtRecorder = app.getNewRecorder(k8sv1.NamespaceAll, VirtOperator) diff --git a/pkg/virt-operator/kubevirt.go b/pkg/virt-operator/kubevirt.go index 054499ee3cca..fdd7095a6463 100644 --- a/pkg/virt-operator/kubevirt.go +++ b/pkg/virt-operator/kubevirt.go @@ -99,27 +99,29 @@ func NewKubeVirtController( stores: stores, informers: informers, kubeVirtExpectations: util.Expectations{ - ServiceAccount: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("ServiceAccount")), - ClusterRole: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("ClusterRole")), - ClusterRoleBinding: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("ClusterRoleBinding")), - Role: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("Role")), - RoleBinding: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("RoleBinding")), - Crd: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("Crd")), - Service: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("Service")), - Deployment: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("Deployment")), - DaemonSet: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("DaemonSet")), - ValidationWebhook: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("ValidationWebhook")), - MutatingWebhook: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("MutatingWebhook")), - APIService: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("APIService")), - SCC: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("SCC")), - Route: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("Route")), - InstallStrategyConfigMap: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("InstallStrategyConfigMap")), - InstallStrategyJob: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("Jobs")), - PodDisruptionBudget: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("PodDisruptionBudgets")), - ServiceMonitor: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("ServiceMonitor")), - PrometheusRule: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("PrometheusRule")), - Secrets: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("Secret")), - ConfigMap: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("ConfigMap")), + ServiceAccount: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("ServiceAccount")), + ClusterRole: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("ClusterRole")), + ClusterRoleBinding: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("ClusterRoleBinding")), + Role: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("Role")), + RoleBinding: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("RoleBinding")), + Crd: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("Crd")), + Service: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("Service")), + Deployment: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("Deployment")), + DaemonSet: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("DaemonSet")), + ValidationWebhook: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("ValidationWebhook")), + MutatingWebhook: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("MutatingWebhook")), + APIService: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("APIService")), + SCC: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("SCC")), + Route: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("Route")), + InstallStrategyConfigMap: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("InstallStrategyConfigMap")), + InstallStrategyJob: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("Jobs")), + PodDisruptionBudget: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("PodDisruptionBudgets")), + ServiceMonitor: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("ServiceMonitor")), + PrometheusRule: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("PrometheusRule")), + Secrets: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("Secret")), + ConfigMap: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("ConfigMap")), + ValidatingAdmissionPolicyBinding: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("ValidatingAdmissionPolicyBinding")), + ValidatingAdmissionPolicy: controller.NewUIDTrackingControllerExpectations(controller.NewControllerExpectationsWithName("ValidatingAdmissionPolicy")), }, operatorNamespace: operatorNamespace, @@ -479,6 +481,36 @@ func NewKubeVirtController( return nil, err } + _, err = c.informers.ValidatingAdmissionPolicyBinding.AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + c.genericAddHandler(obj, c.kubeVirtExpectations.ValidatingAdmissionPolicyBinding) + }, + DeleteFunc: func(obj interface{}) { + c.genericDeleteHandler(obj, c.kubeVirtExpectations.ValidatingAdmissionPolicyBinding) + }, + UpdateFunc: func(oldObj, newObj interface{}) { + c.genericUpdateHandler(oldObj, newObj, c.kubeVirtExpectations.ValidatingAdmissionPolicyBinding) + }, + }) + if err != nil { + return nil, err + } + + _, err = c.informers.ValidatingAdmissionPolicy.AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + c.genericAddHandler(obj, c.kubeVirtExpectations.ValidatingAdmissionPolicy) + }, + DeleteFunc: func(obj interface{}) { + c.genericDeleteHandler(obj, c.kubeVirtExpectations.ValidatingAdmissionPolicy) + }, + UpdateFunc: func(oldObj, newObj interface{}) { + c.genericUpdateHandler(oldObj, newObj, c.kubeVirtExpectations.ValidatingAdmissionPolicy) + }, + }) + if err != nil { + return nil, err + } + return &c, nil } @@ -652,6 +684,8 @@ func (c *KubeVirtController) Run(threadiness int, stopCh <-chan struct{}) { cache.WaitForCacheSync(stopCh, c.informers.PrometheusRule.HasSynced) cache.WaitForCacheSync(stopCh, c.informers.Secrets.HasSynced) cache.WaitForCacheSync(stopCh, c.informers.ConfigMap.HasSynced) + cache.WaitForCacheSync(stopCh, c.informers.ValidatingAdmissionPolicyBinding.HasSynced) + cache.WaitForCacheSync(stopCh, c.informers.ValidatingAdmissionPolicy.HasSynced) // Start the actual work for i := 0; i < threadiness; i++ { diff --git a/pkg/virt-operator/kubevirt_test.go b/pkg/virt-operator/kubevirt_test.go index f65a241c5d47..af87e2fe66ba 100644 --- a/pkg/virt-operator/kubevirt_test.go +++ b/pkg/virt-operator/kubevirt_test.go @@ -100,29 +100,31 @@ type KubeVirtTestData struct { kvInformer cache.SharedIndexInformer apiServiceClient *install.MockAPIServiceInterface - serviceAccountSource *framework.FakeControllerSource - clusterRoleSource *framework.FakeControllerSource - clusterRoleBindingSource *framework.FakeControllerSource - roleSource *framework.FakeControllerSource - roleBindingSource *framework.FakeControllerSource - crdSource *framework.FakeControllerSource - serviceSource *framework.FakeControllerSource - deploymentSource *framework.FakeControllerSource - daemonSetSource *framework.FakeControllerSource - validatingWebhookSource *framework.FakeControllerSource - mutatingWebhookSource *framework.FakeControllerSource - apiserviceSource *framework.FakeControllerSource - sccSource *framework.FakeControllerSource - routeSource *framework.FakeControllerSource - installStrategyConfigMapSource *framework.FakeControllerSource - installStrategyJobSource *framework.FakeControllerSource - infrastructurePodSource *framework.FakeControllerSource - podDisruptionBudgetSource *framework.FakeControllerSource - serviceMonitorSource *framework.FakeControllerSource - namespaceSource *framework.FakeControllerSource - prometheusRuleSource *framework.FakeControllerSource - secretsSource *framework.FakeControllerSource - configMapSource *framework.FakeControllerSource + serviceAccountSource *framework.FakeControllerSource + clusterRoleSource *framework.FakeControllerSource + clusterRoleBindingSource *framework.FakeControllerSource + roleSource *framework.FakeControllerSource + roleBindingSource *framework.FakeControllerSource + crdSource *framework.FakeControllerSource + serviceSource *framework.FakeControllerSource + deploymentSource *framework.FakeControllerSource + daemonSetSource *framework.FakeControllerSource + validatingWebhookSource *framework.FakeControllerSource + mutatingWebhookSource *framework.FakeControllerSource + apiserviceSource *framework.FakeControllerSource + sccSource *framework.FakeControllerSource + routeSource *framework.FakeControllerSource + installStrategyConfigMapSource *framework.FakeControllerSource + installStrategyJobSource *framework.FakeControllerSource + infrastructurePodSource *framework.FakeControllerSource + podDisruptionBudgetSource *framework.FakeControllerSource + serviceMonitorSource *framework.FakeControllerSource + namespaceSource *framework.FakeControllerSource + prometheusRuleSource *framework.FakeControllerSource + secretsSource *framework.FakeControllerSource + configMapSource *framework.FakeControllerSource + ValidatingAdmissionPolicyBindingSource *framework.FakeControllerSource + ValidatingAdmissionPolicySource *framework.FakeControllerSource stop chan struct{} controller *KubeVirtController @@ -259,6 +261,13 @@ func (k *KubeVirtTestData) BeforeTest() { k.informers.ConfigMap, k.configMapSource = testutils.NewFakeInformerFor(&k8sv1.ConfigMap{}) k.stores.ConfigMapCache = k.informers.ConfigMap.GetStore() + k.informers.ValidatingAdmissionPolicyBinding, k.ValidatingAdmissionPolicyBindingSource = testutils.NewFakeInformerFor(&admissionregistrationv1.ValidatingAdmissionPolicyBinding{}) + k.stores.ValidatingAdmissionPolicyBindingCache = k.informers.ValidatingAdmissionPolicyBinding.GetStore() + k.stores.ValidatingAdmissionPolicyBindingEnabled = true + k.informers.ValidatingAdmissionPolicy, k.ValidatingAdmissionPolicySource = testutils.NewFakeInformerFor(&admissionregistrationv1.ValidatingAdmissionPolicy{}) + k.stores.ValidatingAdmissionPolicyCache = k.informers.ValidatingAdmissionPolicy.GetStore() + k.stores.ValidatingAdmissionPolicyEnabled = true + k.controller, _ = NewKubeVirtController(k.virtClient, k.apiServiceClient, k.kvInformer, k.recorder, k.stores, k.informers, NAMESPACE) k.controller.delayedQueueAdder = func(key interface{}, queue workqueue.RateLimitingInterface) { // no delay to speed up tests @@ -309,6 +318,14 @@ func (k *KubeVirtTestData) BeforeTest() { if action.GetVerb() == "get" && action.GetResource().Resource == "mutatingwebhookconfigurations" { return true, nil, errors.NewNotFound(schema.GroupResource{Group: "", Resource: "mutatingwebhookconfigurations"}, "whatever") } + if action.GetVerb() == "create" && action.GetResource().Resource == "validatingadmissionpolicybindings" { + dummyValidatingAdmissionPolicyBinding := &admissionregistrationv1.ValidatingAdmissionPolicyBinding{} + return true, dummyValidatingAdmissionPolicyBinding, nil + } + if action.GetVerb() == "create" && action.GetResource().Resource == "validatingadmissionpolicies" { + dummyValidatingAdmissionPolicy := &admissionregistrationv1.ValidatingAdmissionPolicy{} + return true, dummyValidatingAdmissionPolicy, nil + } if action.GetVerb() == "get" && action.GetResource().Resource == "serviceaccounts" { return true, nil, errors.NewNotFound(schema.GroupResource{Group: "", Resource: "serviceaccounts"}, "whatever") } @@ -533,6 +550,10 @@ func (k *KubeVirtTestData) deleteResource(resource string, key string) { k.deleteInstallStrategyJob(key) case "configmaps": k.deleteConfigMap(key) + case "validatingadmissionpolicybindings": + k.deleteValidatingAdmissionPolicyBinding(key) + case "validatingadmissionpolicies": + k.deleteValidatingAdmissionPolicy(key) case "poddisruptionbudgets": k.deletePodDisruptionBudget(key) case "secrets": @@ -686,6 +707,24 @@ func (k *KubeVirtTestData) deleteConfigMap(key string) { k.mockQueue.Wait() } +func (k *KubeVirtTestData) deleteValidatingAdmissionPolicyBinding(key string) { + k.mockQueue.ExpectAdds(1) + if obj, exists, _ := k.informers.ValidatingAdmissionPolicyBinding.GetStore().GetByKey(key); exists { + validatingAdmissionPolicyBinding := obj.(*admissionregistrationv1.ValidatingAdmissionPolicyBinding) + k.ValidatingAdmissionPolicyBindingSource.Delete(validatingAdmissionPolicyBinding) + } + k.mockQueue.Wait() +} + +func (k *KubeVirtTestData) deleteValidatingAdmissionPolicy(key string) { + k.mockQueue.ExpectAdds(1) + if obj, exists, _ := k.informers.ValidatingAdmissionPolicy.GetStore().GetByKey(key); exists { + validatingAdmissionPolicy := obj.(*admissionregistrationv1.ValidatingAdmissionPolicy) + k.ValidatingAdmissionPolicySource.Delete(validatingAdmissionPolicy) + } + k.mockQueue.Wait() +} + func (k *KubeVirtTestData) deleteSCC(key string) { k.mockQueue.ExpectAdds(1) if obj, exists, _ := k.informers.SCC.GetStore().GetByKey(key); exists { @@ -863,6 +902,8 @@ func (k *KubeVirtTestData) shouldExpectCreations() { k.secClient.Fake.PrependReactor("create", "securitycontextconstraints", genericCreateFunc) k.promClient.Fake.PrependReactor("create", "servicemonitors", genericCreateFunc) k.promClient.Fake.PrependReactor("create", "prometheusrules", genericCreateFunc) + k.promClient.Fake.PrependReactor("create", "validatingadmissionpolicybindings", genericCreateFunc) + k.promClient.Fake.PrependReactor("create", "validatingadmissionpolicies", genericCreateFunc) k.apiServiceClient.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Do(func(ctx context.Context, obj runtime.Object, opts metav1.CreateOptions) { genericCreateFunc(&testing.CreateActionImpl{Object: obj}) }) @@ -945,6 +986,12 @@ func (k *KubeVirtTestData) addResource(obj runtime.Object, config *util.KubeVirt case *routev1.Route: injectMetadata(&obj.(*routev1.Route).ObjectMeta, config) k.addRoute(resource) + case *admissionregistrationv1.ValidatingAdmissionPolicyBinding: + injectMetadata(&obj.(*admissionregistrationv1.ValidatingAdmissionPolicyBinding).ObjectMeta, config) + k.addValidatingAdmissionPolicyBinding(resource) + case *admissionregistrationv1.ValidatingAdmissionPolicy: + injectMetadata(&obj.(*admissionregistrationv1.ValidatingAdmissionPolicy).ObjectMeta, config) + k.addValidatingAdmissionPolicy(resource) default: Fail("unknown resource type") } @@ -1065,6 +1112,18 @@ func (k *KubeVirtTestData) addSecret(secret *k8sv1.Secret) { k.mockQueue.Wait() } +func (k *KubeVirtTestData) addValidatingAdmissionPolicyBinding(validatingAdmissionPolicyBinding *admissionregistrationv1.ValidatingAdmissionPolicyBinding) { + k.mockQueue.ExpectAdds(1) + k.ValidatingAdmissionPolicyBindingSource.Add(validatingAdmissionPolicyBinding) + k.mockQueue.Wait() +} + +func (k *KubeVirtTestData) addValidatingAdmissionPolicy(validatingAdmissionPolicy *admissionregistrationv1.ValidatingAdmissionPolicy) { + k.mockQueue.ExpectAdds(1) + k.ValidatingAdmissionPolicySource.Add(validatingAdmissionPolicy) + k.mockQueue.Wait() +} + func (k *KubeVirtTestData) addConfigMap(configMap *k8sv1.ConfigMap) { k.mockQueue.ExpectAdds(1) if _, ok := configMap.Labels[v1.InstallStrategyLabel]; ok { diff --git a/pkg/virt-operator/resource/apply/admissionregistration.go b/pkg/virt-operator/resource/apply/admissionregistration.go index b8b02c8fdd41..91b425846890 100644 --- a/pkg/virt-operator/resource/apply/admissionregistration.go +++ b/pkg/virt-operator/resource/apply/admissionregistration.go @@ -9,6 +9,7 @@ import ( "github.com/openshift/library-go/pkg/operator/resource/resourcemerge" admissionregistrationv1 "k8s.io/api/admissionregistration/v1" admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1" + "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -353,3 +354,169 @@ func (r *Reconciler) createOrUpdateMutatingWebhookConfiguration(webhook *admissi return nil } + +func generateValidationAdmissionPolicyBindingPatch( + cachedValidatingAdmissionPolicyBinding *admissionregistrationv1.ValidatingAdmissionPolicyBinding, + validatingAdmissionPolicyBinding *admissionregistrationv1.ValidatingAdmissionPolicyBinding) ([]string, error) { + + patchOps, err := getObjectMetaPatch(validatingAdmissionPolicyBinding.ObjectMeta, cachedValidatingAdmissionPolicyBinding.ObjectMeta) + if err != nil { + return nil, err + } + + // If the Specs don't equal each other, replace it + if !equality.Semantic.DeepEqual(cachedValidatingAdmissionPolicyBinding.Spec, validatingAdmissionPolicyBinding.Spec) { + newSpec, err := json.Marshal(validatingAdmissionPolicyBinding.Spec) + if err != nil { + return patchOps, err + } + + patchOps = append(patchOps, fmt.Sprintf(`{ "op": "replace", "path": "/spec", "value": %s }`, string(newSpec))) + } + + return patchOps, nil +} + +// +//func (r *Reconciler) isValidationAdmissionPolicyBindingResourceAvailable() (bool, error) { +// return r.isResourceAvailable(admissionregistrationv1.GroupName, "v1", "ValidatingAdmissionPolicyBinding") +//} +// +//func (r *Reconciler) isValidationAdmissionPolicyResourceAvailable() (bool, error) { +// return r.isResourceAvailable(admissionregistrationv1.GroupName, "v1", "ValidatingAdmissionPolicy") +//} + +func (r *Reconciler) createOrUpdateValidationAdmissionPolicyBindings() error { + if !r.stores.ValidatingAdmissionPolicyBindingEnabled { + return nil + } + + for _, validatingAdmissionPolicyBinding := range r.targetStrategy.ValidatingAdmissionPolicyBindings() { + err := r.createOrUpdateValidationAdmissionPolicyBinding(validatingAdmissionPolicyBinding.DeepCopy()) + if err != nil { + return err + } + } + return nil +} + +func (r *Reconciler) createOrUpdateValidationAdmissionPolicyBinding(validatingAdmissionPolicyBinding *admissionregistrationv1.ValidatingAdmissionPolicyBinding) error { + admissionRegistrationV1 := r.clientset.AdmissionregistrationV1() + version, imageRegistry, id := getTargetVersionRegistryID(r.kv) + + injectOperatorMetadata(r.kv, &validatingAdmissionPolicyBinding.ObjectMeta, version, imageRegistry, id, true) + + obj, exists, _ := r.stores.ValidatingAdmissionPolicyBindingCache.Get(validatingAdmissionPolicyBinding) + + if !exists { + r.expectations.ValidatingAdmissionPolicyBinding.RaiseExpectations(r.kvKey, 1, 0) + _, err := admissionRegistrationV1.ValidatingAdmissionPolicyBindings().Create(context.Background(), validatingAdmissionPolicyBinding, metav1.CreateOptions{}) + if err != nil { + r.expectations.ValidatingAdmissionPolicyBinding.LowerExpectations(r.kvKey, 1, 0) + return fmt.Errorf("unable to create validatingAdmissionPolicyBinding %+v: %v", validatingAdmissionPolicyBinding, err) + } + + return nil + } + + cachedValidatingAdmissionPolicyBinding := obj.(*admissionregistrationv1.ValidatingAdmissionPolicyBinding) + + patchOps, err := generateValidationAdmissionPolicyBindingPatch(cachedValidatingAdmissionPolicyBinding, validatingAdmissionPolicyBinding) + if err != nil { + return fmt.Errorf("unable to generate validatingAdmissionPolicyBinding patch operations for %+v: %v", validatingAdmissionPolicyBinding, err) + } + + if len(patchOps) == 0 { + log.Log.V(4).Infof("validatingAdmissionPolicyBinding %v is up-to-date", validatingAdmissionPolicyBinding.GetName()) + return nil + } + + _, err = admissionRegistrationV1.ValidatingAdmissionPolicyBindings().Patch(context.Background(), + validatingAdmissionPolicyBinding.Name, + types.JSONPatchType, + generatePatchBytes(patchOps), + metav1.PatchOptions{}) + if err != nil { + return fmt.Errorf("unable to patch validatingAdmissionPolicyBinding %+v: %v", validatingAdmissionPolicyBinding, err) + } + + log.Log.V(2).Infof("validatingAdmissionPolicyBinding %v patched", validatingAdmissionPolicyBinding.GetName()) + return nil +} + +func generateValidatingAdmissionPolicyPatch( + cachedValidatingAdmissionPolicy *admissionregistrationv1.ValidatingAdmissionPolicy, + validatingAdmissionPolicy *admissionregistrationv1.ValidatingAdmissionPolicy) ([]string, error) { + + patchOps, err := getObjectMetaPatch(validatingAdmissionPolicy.ObjectMeta, cachedValidatingAdmissionPolicy.ObjectMeta) + if err != nil { + return nil, err + } + + // If the Specs don't equal each other, replace it + if !equality.Semantic.DeepEqual(cachedValidatingAdmissionPolicy.Spec, validatingAdmissionPolicy.Spec) { + newSpec, err := json.Marshal(validatingAdmissionPolicy.Spec) + if err != nil { + return patchOps, err + } + + patchOps = append(patchOps, fmt.Sprintf(`{ "op": "replace", "path": "/spec", "value": %s }`, string(newSpec))) + } + + return patchOps, nil +} + +func (r *Reconciler) createOrUpdateValidationAdmissionPolicies() error { + if !r.stores.ValidatingAdmissionPolicyEnabled { + return nil + } + + for _, validatingAdmissionPolicy := range r.targetStrategy.ValidatingAdmissionPolicies() { + err := r.createOrUpdateValidationAdmissionPolicy(validatingAdmissionPolicy.DeepCopy()) + if err != nil { + return err + } + } + + return nil +} + +func (r *Reconciler) createOrUpdateValidationAdmissionPolicy(validatingAdmissionPolicy *admissionregistrationv1.ValidatingAdmissionPolicy) error { + admissionRegistrationV1 := r.clientset.AdmissionregistrationV1() + version, imageRegistry, id := getTargetVersionRegistryID(r.kv) + + injectOperatorMetadata(r.kv, &validatingAdmissionPolicy.ObjectMeta, version, imageRegistry, id, true) + + obj, exists, _ := r.stores.ValidatingAdmissionPolicyCache.Get(validatingAdmissionPolicy) + + if !exists { + r.expectations.ValidatingAdmissionPolicy.RaiseExpectations(r.kvKey, 1, 0) + _, err := admissionRegistrationV1.ValidatingAdmissionPolicies().Create(context.Background(), validatingAdmissionPolicy, metav1.CreateOptions{}) + if err != nil { + r.expectations.ValidatingAdmissionPolicy.LowerExpectations(r.kvKey, 1, 0) + return fmt.Errorf("unable to create validatingAdmissionPolicy %+v: %v", validatingAdmissionPolicy, err) + } + + return nil + } + + cachedValidatingAdmissionPolicy := obj.(*admissionregistrationv1.ValidatingAdmissionPolicy) + + patchOps, err := generateValidatingAdmissionPolicyPatch(cachedValidatingAdmissionPolicy, validatingAdmissionPolicy) + if err != nil { + return fmt.Errorf("unable to generate validatingAdmissionPolicy patch operations for %+v: %v", validatingAdmissionPolicy, err) + } + + if len(patchOps) == 0 { + log.Log.V(4).Infof("validatingAdmissionPolicy %v is up-to-date", validatingAdmissionPolicy.GetName()) + return nil + } + + _, err = admissionRegistrationV1.ValidatingAdmissionPolicies().Patch(context.Background(), validatingAdmissionPolicy.Name, types.JSONPatchType, generatePatchBytes(patchOps), metav1.PatchOptions{}) + if err != nil { + return fmt.Errorf("unable to patch validatingAdmissionPolicy %+v: %v", validatingAdmissionPolicy, err) + } + + log.Log.V(2).Infof("validatingAdmissionPolicy %v patched", validatingAdmissionPolicy.GetName()) + return nil +} diff --git a/pkg/virt-operator/resource/apply/delete.go b/pkg/virt-operator/resource/apply/delete.go index ce76166fef26..c94190923db6 100644 --- a/pkg/virt-operator/resource/apply/delete.go +++ b/pkg/virt-operator/resource/apply/delete.go @@ -464,6 +464,42 @@ func DeleteAll(kv *v1.KubeVirt, } } + objects = stores.ValidatingAdmissionPolicyBindingCache.List() + for _, obj := range objects { + if validatingAdmissionPolicyBinding, ok := obj.(*admissionregistrationv1.ValidatingAdmissionPolicyBinding); ok && validatingAdmissionPolicyBinding.DeletionTimestamp == nil { + if key, err := controller.KeyFunc(validatingAdmissionPolicyBinding); err == nil { + expectations.ConfigMap.AddExpectedDeletion(kvkey, key) + err := clientset.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Delete(context.Background(), validatingAdmissionPolicyBinding.Name, deleteOptions) + if err != nil { + expectations.ValidatingAdmissionPolicyBinding.DeletionObserved(kvkey, key) + log.Log.Errorf("Failed to delete validatingAdmissionPolicyBinding %+v: %v", validatingAdmissionPolicyBinding, err) + return err + } + } + } else if !ok { + log.Log.Errorf(castFailedFmt, obj) + return nil + } + } + + objects = stores.ValidatingAdmissionPolicyCache.List() + for _, obj := range objects { + if validatingAdmissionPolicy, ok := obj.(*admissionregistrationv1.ValidatingAdmissionPolicy); ok && validatingAdmissionPolicy.DeletionTimestamp == nil { + if key, err := controller.KeyFunc(validatingAdmissionPolicy); err == nil { + expectations.ConfigMap.AddExpectedDeletion(kvkey, key) + err := clientset.AdmissionregistrationV1().ValidatingAdmissionPolicies().Delete(context.Background(), validatingAdmissionPolicy.Name, deleteOptions) + if err != nil { + expectations.ValidatingAdmissionPolicy.DeletionObserved(kvkey, key) + log.Log.Errorf("Failed to delete validatingAdmissionPolicy %+v: %v", validatingAdmissionPolicy, err) + return err + } + } + } else if !ok { + log.Log.Errorf(castFailedFmt, obj) + return nil + } + } + err = deleteDummyWebhookValidators(kv, clientset, stores, expectations) if err != nil { return err diff --git a/pkg/virt-operator/resource/apply/reconcile.go b/pkg/virt-operator/resource/apply/reconcile.go index bca3859ecfec..c2749a58c92c 100644 --- a/pkg/virt-operator/resource/apply/reconcile.go +++ b/pkg/virt-operator/resource/apply/reconcile.go @@ -30,7 +30,6 @@ import ( "github.com/blang/semver" secv1 "github.com/openshift/api/security/v1" promv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" - admissionregistrationv1 "k8s.io/api/admissionregistration/v1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -651,6 +650,16 @@ func (r *Reconciler) Sync(queue workqueue.RateLimitingInterface) (bool, error) { return false, nil } + err = r.createOrUpdateValidationAdmissionPolicyBindings() + if err != nil { + return false, err + } + + err = r.createOrUpdateValidationAdmissionPolicies() + if err != nil { + return false, err + } + err = r.createOrUpdateComponentsWithCertificates(queue) if err != nil { return false, err @@ -944,6 +953,59 @@ func (r *Reconciler) deleteObjectsNotInInstallStrategy() error { } } + // remove unused ValidatingAdmissionPolicyBinding + if r.stores.ValidatingAdmissionPolicyBindingEnabled { + for _, obj := range objects { + if validatingAdmissionPolicyBinding, ok := obj.(*admissionregistrationv1.ValidatingAdmissionPolicyBinding); ok && validatingAdmissionPolicyBinding.DeletionTimestamp == nil { + found := false + for _, targetValidatingAdmissionPolicyBinding := range r.targetStrategy.ValidatingAdmissionPolicyBindings() { + if targetValidatingAdmissionPolicyBinding.Name == validatingAdmissionPolicyBinding.Name { + found = true + break + } + } + if !found { + if key, err := controller.KeyFunc(validatingAdmissionPolicyBinding); err == nil { + r.expectations.ValidatingAdmissionPolicyBinding.AddExpectedDeletion(r.kvKey, key) + err := r.clientset.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Delete(context.Background(), validatingAdmissionPolicyBinding.Name, deleteOptions) + if err != nil { + r.expectations.ValidatingAdmissionPolicyBinding.DeletionObserved(r.kvKey, key) + log.Log.Errorf("Failed to delete validatingAdmissionPolicyBinding %+v: %v", validatingAdmissionPolicyBinding, err) + return err + } + } + } + } + } + } + + // remove unused ValidatingAdmissionPolicy + if r.stores.ValidatingAdmissionPolicyEnabled { + objects = r.stores.ValidatingAdmissionPolicyCache.List() + for _, obj := range objects { + if validatingAdmissionPolicy, ok := obj.(*admissionregistrationv1.ValidatingAdmissionPolicy); ok && validatingAdmissionPolicy.DeletionTimestamp == nil { + found := false + for _, targetValidatingAdmissionPolicy := range r.targetStrategy.ValidatingAdmissionPolicies() { + if targetValidatingAdmissionPolicy.Name == validatingAdmissionPolicy.Name { + found = true + break + } + } + if !found { + if key, err := controller.KeyFunc(validatingAdmissionPolicy); err == nil { + r.expectations.ValidatingAdmissionPolicy.AddExpectedDeletion(r.kvKey, key) + err := r.clientset.AdmissionregistrationV1().ValidatingAdmissionPolicies().Delete(context.Background(), validatingAdmissionPolicy.Name, deleteOptions) + if err != nil { + r.expectations.ValidatingAdmissionPolicy.DeletionObserved(r.kvKey, key) + log.Log.Errorf("Failed to delete validatingAdmissionPolicy %+v: %v", validatingAdmissionPolicy, err) + return err + } + } + } + } + } + } + // remove unused crds objects = r.stores.CrdCache.List() for _, obj := range objects { @@ -1324,6 +1386,21 @@ func (r *Reconciler) isFeatureGateEnabled(featureGate string) bool { return false } +// +//func (r *Reconciler) isResourceAvailable(group, version, name string) (bool, error) { +// apiURI := path.Join("/apis", group, version, name) +// result := r.clientset.RestClient().Get().RequestURI(apiURI).Do(context.Background()) +// +// if err := result.Error(); err != nil { +// if apierrors.IsNotFound(err) { +// return false, nil +// } +// return false, err +// } +// +// return true, nil +//} + func (r *Reconciler) exportProxyEnabled() bool { return r.isFeatureGateEnabled(virtconfig.VMExportGate) } diff --git a/pkg/virt-operator/resource/generate/install/strategy.go b/pkg/virt-operator/resource/generate/install/strategy.go index 0cdfa14ec727..0f0ea0328b8a 100644 --- a/pkg/virt-operator/resource/generate/install/strategy.go +++ b/pkg/virt-operator/resource/generate/install/strategy.go @@ -83,20 +83,22 @@ type Strategy struct { crds []*extv1.CustomResourceDefinition - services []*corev1.Service - deployments []*appsv1.Deployment - daemonSets []*appsv1.DaemonSet - validatingWebhookConfigurations []*admissionregistrationv1.ValidatingWebhookConfiguration - mutatingWebhookConfigurations []*admissionregistrationv1.MutatingWebhookConfiguration - apiServices []*apiregv1.APIService - certificateSecrets []*corev1.Secret - sccs []*secv1.SecurityContextConstraints - serviceMonitors []*promv1.ServiceMonitor - prometheusRules []*promv1.PrometheusRule - configMaps []*corev1.ConfigMap - routes []*routev1.Route - instancetypes []*instancetypev1beta1.VirtualMachineClusterInstancetype - preferences []*instancetypev1beta1.VirtualMachineClusterPreference + services []*corev1.Service + deployments []*appsv1.Deployment + daemonSets []*appsv1.DaemonSet + validatingWebhookConfigurations []*admissionregistrationv1.ValidatingWebhookConfiguration + mutatingWebhookConfigurations []*admissionregistrationv1.MutatingWebhookConfiguration + apiServices []*apiregv1.APIService + certificateSecrets []*corev1.Secret + sccs []*secv1.SecurityContextConstraints + serviceMonitors []*promv1.ServiceMonitor + prometheusRules []*promv1.PrometheusRule + configMaps []*corev1.ConfigMap + routes []*routev1.Route + instancetypes []*instancetypev1beta1.VirtualMachineClusterInstancetype + preferences []*instancetypev1beta1.VirtualMachineClusterPreference + validatingAdmissionPolicyBindings []*admissionregistrationv1.ValidatingAdmissionPolicyBinding + validatingAdmissionPolicies []*admissionregistrationv1.ValidatingAdmissionPolicy } func (ins *Strategy) ServiceAccounts() []*corev1.ServiceAccount { @@ -220,6 +222,14 @@ func (ins *Strategy) Preferences() []*instancetypev1beta1.VirtualMachineClusterP return ins.preferences } +func (ins *Strategy) ValidatingAdmissionPolicyBindings() []*admissionregistrationv1.ValidatingAdmissionPolicyBinding { + return ins.validatingAdmissionPolicyBindings +} + +func (ins *Strategy) ValidatingAdmissionPolicies() []*admissionregistrationv1.ValidatingAdmissionPolicy { + return ins.validatingAdmissionPolicies +} + func encodeManifests(manifests []byte) (string, error) { var buf bytes.Buffer @@ -373,6 +383,12 @@ func dumpInstallStrategyToBytes(strategy *Strategy) []byte { for _, entry := range strategy.mutatingWebhookConfigurations { marshalutil.MarshallObject(entry, writer) } + for _, entry := range strategy.validatingAdmissionPolicyBindings { + marshalutil.MarshallObject(entry, writer) + } + for _, entry := range strategy.validatingAdmissionPolicies { + marshalutil.MarshallObject(entry, writer) + } for _, entry := range strategy.apiServices { marshalutil.MarshallObject(entry, writer) } @@ -552,6 +568,10 @@ func GenerateCurrentInstallStrategy(config *operatorutil.KubeVirtDeploymentConfi strategy.configMaps = append(strategy.configMaps, components.NewCAConfigMaps(operatorNamespace)...) strategy.routes = append(strategy.routes, components.GetAllRoutes(operatorNamespace)...) + strategy.validatingAdmissionPolicyBindings = append(strategy.validatingAdmissionPolicyBindings, components.NewHandlerV1ValidatingAdmissionPolicyBinding()) + virtHandlerServiceAccount := getVirtHandlerServiceAccount(config.GetNamespace()) + strategy.validatingAdmissionPolicies = append(strategy.validatingAdmissionPolicies, components.NewHandlerV1ValidatingAdmissionPolicy(virtHandlerServiceAccount)) + instancetypes, err := components.NewClusterInstancetypes() if err != nil { return nil, fmt.Errorf("error generating instancetypes for environment %v", err) @@ -567,6 +587,11 @@ func GenerateCurrentInstallStrategy(config *operatorutil.KubeVirtDeploymentConfi return strategy, nil } +func getVirtHandlerServiceAccount(namespace string) string { + prefix := fmt.Sprintf("system:serviceaccount:%s", namespace) + return fmt.Sprintf("%s:%s", prefix, components.HandlerServiceAccountName) +} + func mostRecentConfigMap(configMaps []*corev1.ConfigMap) *corev1.ConfigMap { var configMap *corev1.ConfigMap // choose the most recent configmap if multiple match. @@ -674,6 +699,20 @@ func loadInstallStrategyFromBytes(data string) (*Strategy, error) { } webhook.TypeMeta = obj strategy.mutatingWebhookConfigurations = append(strategy.mutatingWebhookConfigurations, webhook) + case "ValidatingAdmissionPolicyBinding": + validatingAdmissionPolicyBinding := &admissionregistrationv1.ValidatingAdmissionPolicyBinding{} + if err := yaml.Unmarshal([]byte(entry), &validatingAdmissionPolicyBinding); err != nil { + return nil, err + } + validatingAdmissionPolicyBinding.TypeMeta = obj + strategy.validatingAdmissionPolicyBindings = append(strategy.validatingAdmissionPolicyBindings, validatingAdmissionPolicyBinding) + case "ValidatingAdmissionPolicy": + validatingAdmissionPolicy := &admissionregistrationv1.ValidatingAdmissionPolicy{} + if err := yaml.Unmarshal([]byte(entry), &validatingAdmissionPolicy); err != nil { + return nil, err + } + validatingAdmissionPolicy.TypeMeta = obj + strategy.validatingAdmissionPolicies = append(strategy.validatingAdmissionPolicies, validatingAdmissionPolicy) case "APIService": apiService := &apiregv1.APIService{} if err := yaml.Unmarshal([]byte(entry), &apiService); err != nil { diff --git a/pkg/virt-operator/util/BUILD.bazel b/pkg/virt-operator/util/BUILD.bazel index 0f06af4a218f..c713f6627a85 100644 --- a/pkg/virt-operator/util/BUILD.bazel +++ b/pkg/virt-operator/util/BUILD.bazel @@ -21,6 +21,7 @@ go_library( "//staging/src/kubevirt.io/client-go/version:go_default_library", "//vendor/github.com/openshift/api/security/v1:go_default_library", "//vendor/github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1:go_default_library", + "//vendor/k8s.io/api/admissionregistration/v1:go_default_library", "//vendor/k8s.io/api/apps/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", diff --git a/pkg/virt-operator/util/client.go b/pkg/virt-operator/util/client.go index c797cc8f62ad..6aedc92a86c1 100644 --- a/pkg/virt-operator/util/client.go +++ b/pkg/virt-operator/util/client.go @@ -24,6 +24,7 @@ import ( "time" promv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" "k8s.io/client-go/discovery" k8sv1 "k8s.io/api/core/v1" @@ -230,3 +231,45 @@ func IsPrometheusRuleEnabled(clientset kubecli.KubevirtClient) (bool, error) { return false, nil } + +// IsValidatingAdmissionPolicyBindingEnabled returns true if ValidatingAdmissionPolicyBinding resource is defined +// and false otherwise. +func IsValidatingAdmissionPolicyBindingEnabled(clientset kubecli.KubevirtClient) (bool, error) { + _, apis, err := clientset.DiscoveryClient().ServerGroupsAndResources() + if err != nil && !discovery.IsGroupDiscoveryFailedError(err) { + return false, err + } + + for _, api := range apis { + if api.GroupVersion == admissionregistrationv1.SchemeGroupVersion.String() { + for _, resource := range api.APIResources { + if resource.Name == "validatingadmissionpolicybindings" { + return true, nil + } + } + } + } + + return false, nil +} + +// IsValidatingAdmissionPolicyEnabled returns true if ValidatingAdmissionPolicy resource is defined +// and false otherwise. +func IsValidatingAdmissionPolicyEnabled(clientset kubecli.KubevirtClient) (bool, error) { + _, apis, err := clientset.DiscoveryClient().ServerGroupsAndResources() + if err != nil && !discovery.IsGroupDiscoveryFailedError(err) { + return false, err + } + + for _, api := range apis { + if api.GroupVersion == admissionregistrationv1.SchemeGroupVersion.String() { + for _, resource := range api.APIResources { + if resource.Name == "validatingadmissionpolicies" { + return true, nil + } + } + } + } + + return false, nil +} diff --git a/pkg/virt-operator/util/types.go b/pkg/virt-operator/util/types.go index 96a2a98da7a7..227ee1fae355 100644 --- a/pkg/virt-operator/util/types.go +++ b/pkg/virt-operator/util/types.go @@ -28,32 +28,36 @@ import ( ) type Stores struct { - ServiceAccountCache cache.Store - ClusterRoleCache cache.Store - ClusterRoleBindingCache cache.Store - RoleCache cache.Store - RoleBindingCache cache.Store - CrdCache cache.Store - ServiceCache cache.Store - DeploymentCache cache.Store - DaemonSetCache cache.Store - ValidationWebhookCache cache.Store - MutatingWebhookCache cache.Store - APIServiceCache cache.Store - SCCCache cache.Store - RouteCache cache.Store - InstallStrategyConfigMapCache cache.Store - InstallStrategyJobCache cache.Store - InfrastructurePodCache cache.Store - PodDisruptionBudgetCache cache.Store - ServiceMonitorCache cache.Store - NamespaceCache cache.Store - PrometheusRuleCache cache.Store - SecretCache cache.Store - ConfigMapCache cache.Store - IsOnOpenshift bool - ServiceMonitorEnabled bool - PrometheusRulesEnabled bool + ServiceAccountCache cache.Store + ClusterRoleCache cache.Store + ClusterRoleBindingCache cache.Store + RoleCache cache.Store + RoleBindingCache cache.Store + CrdCache cache.Store + ServiceCache cache.Store + DeploymentCache cache.Store + DaemonSetCache cache.Store + ValidationWebhookCache cache.Store + MutatingWebhookCache cache.Store + APIServiceCache cache.Store + SCCCache cache.Store + RouteCache cache.Store + InstallStrategyConfigMapCache cache.Store + InstallStrategyJobCache cache.Store + InfrastructurePodCache cache.Store + PodDisruptionBudgetCache cache.Store + ServiceMonitorCache cache.Store + NamespaceCache cache.Store + PrometheusRuleCache cache.Store + SecretCache cache.Store + ConfigMapCache cache.Store + ValidatingAdmissionPolicyBindingCache cache.Store + ValidatingAdmissionPolicyCache cache.Store + IsOnOpenshift bool + ServiceMonitorEnabled bool + PrometheusRulesEnabled bool + ValidatingAdmissionPolicyBindingEnabled bool + ValidatingAdmissionPolicyEnabled bool } func (s *Stores) AllEmpty() bool { @@ -75,7 +79,9 @@ func (s *Stores) AllEmpty() bool { IsStoreEmpty(s.ServiceMonitorCache) && IsStoreEmpty(s.PrometheusRuleCache) && IsStoreEmpty(s.SecretCache) && - IsStoreEmpty(s.ConfigMapCache) + IsStoreEmpty(s.ConfigMapCache) && + IsStoreEmpty(s.ValidatingAdmissionPolicyBindingCache) && + IsStoreEmpty(s.ValidatingAdmissionPolicyCache) // Don't add InstallStrategyConfigMapCache to this list. The install // strategies persist even after deletion and updates. @@ -103,53 +109,57 @@ func IsSCCStoreEmpty(store cache.Store) bool { } type Expectations struct { - ServiceAccount *controller.UIDTrackingControllerExpectations - ClusterRole *controller.UIDTrackingControllerExpectations - ClusterRoleBinding *controller.UIDTrackingControllerExpectations - Role *controller.UIDTrackingControllerExpectations - RoleBinding *controller.UIDTrackingControllerExpectations - Crd *controller.UIDTrackingControllerExpectations - Service *controller.UIDTrackingControllerExpectations - Deployment *controller.UIDTrackingControllerExpectations - DaemonSet *controller.UIDTrackingControllerExpectations - ValidationWebhook *controller.UIDTrackingControllerExpectations - MutatingWebhook *controller.UIDTrackingControllerExpectations - APIService *controller.UIDTrackingControllerExpectations - SCC *controller.UIDTrackingControllerExpectations - Route *controller.UIDTrackingControllerExpectations - InstallStrategyConfigMap *controller.UIDTrackingControllerExpectations - InstallStrategyJob *controller.UIDTrackingControllerExpectations - PodDisruptionBudget *controller.UIDTrackingControllerExpectations - ServiceMonitor *controller.UIDTrackingControllerExpectations - PrometheusRule *controller.UIDTrackingControllerExpectations - Secrets *controller.UIDTrackingControllerExpectations - ConfigMap *controller.UIDTrackingControllerExpectations + ServiceAccount *controller.UIDTrackingControllerExpectations + ClusterRole *controller.UIDTrackingControllerExpectations + ClusterRoleBinding *controller.UIDTrackingControllerExpectations + Role *controller.UIDTrackingControllerExpectations + RoleBinding *controller.UIDTrackingControllerExpectations + Crd *controller.UIDTrackingControllerExpectations + Service *controller.UIDTrackingControllerExpectations + Deployment *controller.UIDTrackingControllerExpectations + DaemonSet *controller.UIDTrackingControllerExpectations + ValidationWebhook *controller.UIDTrackingControllerExpectations + MutatingWebhook *controller.UIDTrackingControllerExpectations + APIService *controller.UIDTrackingControllerExpectations + SCC *controller.UIDTrackingControllerExpectations + Route *controller.UIDTrackingControllerExpectations + InstallStrategyConfigMap *controller.UIDTrackingControllerExpectations + InstallStrategyJob *controller.UIDTrackingControllerExpectations + PodDisruptionBudget *controller.UIDTrackingControllerExpectations + ServiceMonitor *controller.UIDTrackingControllerExpectations + PrometheusRule *controller.UIDTrackingControllerExpectations + Secrets *controller.UIDTrackingControllerExpectations + ConfigMap *controller.UIDTrackingControllerExpectations + ValidatingAdmissionPolicyBinding *controller.UIDTrackingControllerExpectations + ValidatingAdmissionPolicy *controller.UIDTrackingControllerExpectations } type Informers struct { - ServiceAccount cache.SharedIndexInformer - ClusterRole cache.SharedIndexInformer - ClusterRoleBinding cache.SharedIndexInformer - Role cache.SharedIndexInformer - RoleBinding cache.SharedIndexInformer - Crd cache.SharedIndexInformer - Service cache.SharedIndexInformer - Deployment cache.SharedIndexInformer - DaemonSet cache.SharedIndexInformer - ValidationWebhook cache.SharedIndexInformer - MutatingWebhook cache.SharedIndexInformer - APIService cache.SharedIndexInformer - SCC cache.SharedIndexInformer - Route cache.SharedIndexInformer - InstallStrategyConfigMap cache.SharedIndexInformer - InstallStrategyJob cache.SharedIndexInformer - InfrastructurePod cache.SharedIndexInformer - PodDisruptionBudget cache.SharedIndexInformer - ServiceMonitor cache.SharedIndexInformer - Namespace cache.SharedIndexInformer - PrometheusRule cache.SharedIndexInformer - Secrets cache.SharedIndexInformer - ConfigMap cache.SharedIndexInformer + ServiceAccount cache.SharedIndexInformer + ClusterRole cache.SharedIndexInformer + ClusterRoleBinding cache.SharedIndexInformer + Role cache.SharedIndexInformer + RoleBinding cache.SharedIndexInformer + Crd cache.SharedIndexInformer + Service cache.SharedIndexInformer + Deployment cache.SharedIndexInformer + DaemonSet cache.SharedIndexInformer + ValidationWebhook cache.SharedIndexInformer + MutatingWebhook cache.SharedIndexInformer + APIService cache.SharedIndexInformer + SCC cache.SharedIndexInformer + Route cache.SharedIndexInformer + InstallStrategyConfigMap cache.SharedIndexInformer + InstallStrategyJob cache.SharedIndexInformer + InfrastructurePod cache.SharedIndexInformer + PodDisruptionBudget cache.SharedIndexInformer + ServiceMonitor cache.SharedIndexInformer + Namespace cache.SharedIndexInformer + PrometheusRule cache.SharedIndexInformer + Secrets cache.SharedIndexInformer + ConfigMap cache.SharedIndexInformer + ValidatingAdmissionPolicyBinding cache.SharedIndexInformer + ValidatingAdmissionPolicy cache.SharedIndexInformer } func (e *Expectations) DeleteExpectations(key string) { @@ -174,6 +184,8 @@ func (e *Expectations) DeleteExpectations(key string) { e.PrometheusRule.DeleteExpectations(key) e.Secrets.DeleteExpectations(key) e.ConfigMap.DeleteExpectations(key) + e.ValidatingAdmissionPolicyBinding.DeleteExpectations(key) + e.ValidatingAdmissionPolicy.DeleteExpectations(key) } func (e *Expectations) ResetExpectations(key string) { @@ -198,6 +210,8 @@ func (e *Expectations) ResetExpectations(key string) { e.PrometheusRule.SetExpectations(key, 0, 0) e.Secrets.SetExpectations(key, 0, 0) e.ConfigMap.SetExpectations(key, 0, 0) + e.ValidatingAdmissionPolicyBinding.SetExpectations(key, 0, 0) + e.ValidatingAdmissionPolicy.SetExpectations(key, 0, 0) } func (e *Expectations) SatisfiedExpectations(key string) bool { @@ -221,5 +235,7 @@ func (e *Expectations) SatisfiedExpectations(key string) bool { e.ServiceMonitor.SatisfiedExpectations(key) && e.PrometheusRule.SatisfiedExpectations(key) && e.Secrets.SatisfiedExpectations(key) && - e.ConfigMap.SatisfiedExpectations(key) + e.ConfigMap.SatisfiedExpectations(key) && + e.ValidatingAdmissionPolicyBinding.SatisfiedExpectations(key) && + e.ValidatingAdmissionPolicy.SatisfiedExpectations(key) }