diff --git a/changelog/fragments/2729-addition.yaml b/changelog/fragments/2729-addition.yaml new file mode 100644 index 00000000000..bd469618d6d --- /dev/null +++ b/changelog/fragments/2729-addition.yaml @@ -0,0 +1,10 @@ +# entries is a list of entries to include in +# release notes and/or the migration guide +entries: + - description: > + The CSV generator adds admission webhook config manifests present in --deploy-dir + to new and existing CSV manifests. + + kind: "addition" + + breaking: false diff --git a/go.mod b/go.mod index 2aff1f3e79d..90a969ea10d 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/mattn/go-isatty v0.0.12 github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/mapstructure v1.1.2 - github.com/operator-framework/api v0.3.0 + github.com/operator-framework/api v0.3.1 github.com/operator-framework/operator-registry v1.6.2-0.20200330184612-11867930adb5 github.com/pborman/uuid v1.2.0 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index a6c4ac93727..1b657db8b7d 100644 --- a/go.sum +++ b/go.sum @@ -723,8 +723,8 @@ github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/operator-framework/api v0.1.1/go.mod h1:yzNYR7qyJqRGOOp+bT6Z/iYSbSPNxeh3Si93Gx/3OBY= -github.com/operator-framework/api v0.3.0 h1:Ey7hpg/pwzIBkJtFICksbQLORLYmIjlPdGt+6GNeWT0= -github.com/operator-framework/api v0.3.0/go.mod h1:tOJUgbX7u1fBEEjt2cz3QjXzydBPJ19SwWwmAjiSz/s= +github.com/operator-framework/api v0.3.1 h1:rjQ4lbfKSlccH2hCXKw8giybgjskiE17dm+9xIfRSK8= +github.com/operator-framework/api v0.3.1/go.mod h1:tOJUgbX7u1fBEEjt2cz3QjXzydBPJ19SwWwmAjiSz/s= github.com/operator-framework/operator-registry v1.5.3/go.mod h1:agrQlkWOo1q8U1SAaLSS2WQ+Z9vswNT2M2HFib9iuLY= github.com/operator-framework/operator-registry v1.6.2-0.20200330184612-11867930adb5 h1:o7Ugm+uXWF2B1oyJU9q/wd2R4w4d57K5ByZoxXJaPcI= github.com/operator-framework/operator-registry v1.6.2-0.20200330184612-11867930adb5/go.mod h1:SHff373z8asEkPo6aWpN0qId4Y/feQTjZxRF8PRhti8= diff --git a/internal/generate/olm-catalog/csv.go b/internal/generate/olm-catalog/csv.go index 541e335ac60..19f3a94b6ef 100644 --- a/internal/generate/olm-catalog/csv.go +++ b/internal/generate/olm-catalog/csv.go @@ -435,6 +435,10 @@ func (g BundleGenerator) updateCSVFromManifests(csv *olmapiv1alpha1.ClusterServi err = collection.addDeployments(manifest) case "CustomResourceDefinition": // Skip for now and add explicitly from CRDsDir input. + case "ValidatingWebhookConfiguration": + err = collection.addValidatingWebhookConfigurations(manifest) + case "MutatingWebhookConfiguration": + err = collection.addMutatingWebhookConfigurations(manifest) default: err = collection.addOthers(manifest) } diff --git a/internal/generate/olm-catalog/csv_updaters.go b/internal/generate/olm-catalog/csv_updaters.go index 986c634229d..69428c446ad 100644 --- a/internal/generate/olm-catalog/csv_updaters.go +++ b/internal/generate/olm-catalog/csv_updaters.go @@ -29,6 +29,7 @@ import ( operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" log "github.com/sirupsen/logrus" + admissionregv1 "k8s.io/api/admissionregistration/v1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" @@ -45,6 +46,8 @@ type manifestCollection struct { ClusterRoles []rbacv1.ClusterRole Deployments []appsv1.Deployment CustomResourceDefinitions []apiextv1beta1.CustomResourceDefinition + ValidatingWebhooks []admissionregv1.ValidatingWebhook + MutatingWebhooks []admissionregv1.MutatingWebhook CustomResources []unstructured.Unstructured Others []unstructured.Unstructured } @@ -88,6 +91,32 @@ func (c *manifestCollection) addDeployments(rawManifests ...[]byte) error { return nil } +// addValidatingWebhookConfigurations assumes all manifest data in rawManifests +// are ValidatingWebhookConfigurations and adds their webhooks to the collection. +func (c *manifestCollection) addValidatingWebhookConfigurations(rawManifests ...[]byte) error { + for _, rawManifest := range rawManifests { + webhookConfig := admissionregv1.ValidatingWebhookConfiguration{} + if err := yaml.Unmarshal(rawManifest, &webhookConfig); err != nil { + return fmt.Errorf("error adding ValidatingWebhookConfig to manifest collection: %v", err) + } + c.ValidatingWebhooks = append(c.ValidatingWebhooks, webhookConfig.Webhooks...) + } + return nil +} + +// addMutatingWebhookConfigurations assumes all manifest data in rawManifests +// are MutatingWebhookConfigurations and adds their webhooks to the collection. +func (c *manifestCollection) addMutatingWebhookConfigurations(rawManifests ...[]byte) error { + for _, rawManifest := range rawManifests { + webhookConfig := admissionregv1.MutatingWebhookConfiguration{} + if err := yaml.Unmarshal(rawManifest, &webhookConfig); err != nil { + return fmt.Errorf("error adding MutatingWebhookConfig to manifest collection: %v", err) + } + c.MutatingWebhooks = append(c.MutatingWebhooks, webhookConfig.Webhooks...) + } + return nil +} + // addOthers assumes add manifest data in rawManifests are able to be // unmarshalled into an Unstructured object and adds them to the collection. func (c *manifestCollection) addOthers(rawManifests ...[]byte) error { @@ -145,6 +174,7 @@ func (c manifestCollection) apply(csv *operatorsv1alpha1.ClusterServiceVersion) if err := c.applyCustomResources(csv); err != nil { return fmt.Errorf("error applying Custom Resource: %v", err) } + c.applyWebhooks(csv) return nil } @@ -355,6 +385,66 @@ func prettifyJSON(b []byte) ([]byte, error) { return out.Bytes(), err } +// applyWebhooks updates csv's webhookDefinitions with any +// mutating and validating webhooks in the collection. +func (c manifestCollection) applyWebhooks(csv *operatorsv1alpha1.ClusterServiceVersion) { + webhookDescriptions := []operatorsv1alpha1.WebhookDescription{} + for _, webhook := range c.ValidatingWebhooks { + webhookDescriptions = append(webhookDescriptions, validatingToWebhookDescription(webhook)) + } + for _, webhook := range c.MutatingWebhooks { + webhookDescriptions = append(webhookDescriptions, mutatingToWebhookDescription(webhook)) + } + csv.Spec.WebhookDefinitions = webhookDescriptions +} + +// validatingToWebhookDescription transforms webhook into a WebhookDescription. +func validatingToWebhookDescription(webhook admissionregv1.ValidatingWebhook) operatorsv1alpha1.WebhookDescription { + description := operatorsv1alpha1.WebhookDescription{ + Type: operatorsv1alpha1.ValidatingAdmissionWebhook, + Name: webhook.Name, + Rules: webhook.Rules, + FailurePolicy: webhook.FailurePolicy, + MatchPolicy: webhook.MatchPolicy, + ObjectSelector: webhook.ObjectSelector, + SideEffects: webhook.SideEffects, + TimeoutSeconds: webhook.TimeoutSeconds, + AdmissionReviewVersions: webhook.AdmissionReviewVersions, + } + if serviceRef := webhook.ClientConfig.Service; serviceRef != nil { + if serviceRef.Port != nil { + description.ContainerPort = *serviceRef.Port + } + description.DeploymentName = strings.TrimSuffix(serviceRef.Name, "-service") + description.WebhookPath = serviceRef.Path + } + return description +} + +// mutatingToWebhookDescription transforms webhook into a WebhookDescription. +func mutatingToWebhookDescription(webhook admissionregv1.MutatingWebhook) operatorsv1alpha1.WebhookDescription { + description := operatorsv1alpha1.WebhookDescription{ + Type: operatorsv1alpha1.MutatingAdmissionWebhook, + Name: webhook.Name, + Rules: webhook.Rules, + FailurePolicy: webhook.FailurePolicy, + MatchPolicy: webhook.MatchPolicy, + ObjectSelector: webhook.ObjectSelector, + SideEffects: webhook.SideEffects, + TimeoutSeconds: webhook.TimeoutSeconds, + AdmissionReviewVersions: webhook.AdmissionReviewVersions, + ReinvocationPolicy: webhook.ReinvocationPolicy, + } + if serviceRef := webhook.ClientConfig.Service; serviceRef != nil { + if serviceRef.Port != nil { + description.ContainerPort = *serviceRef.Port + } + description.DeploymentName = strings.TrimSuffix(serviceRef.Name, "-service") + description.WebhookPath = serviceRef.Path + } + return description +} + // deduplicate removes duplicate objects from the collection, since we are // collecting an arbitrary list of manifests. func (c *manifestCollection) deduplicate() error { @@ -422,6 +512,30 @@ func (c *manifestCollection) deduplicate() error { } c.CustomResources = crs + validatingWebhooks := []admissionregv1.ValidatingWebhook{} + for _, webhook := range c.ValidatingWebhooks { + hasHash, err := addToHashes(&webhook, hashes) + if err != nil { + return err + } + if !hasHash { + validatingWebhooks = append(validatingWebhooks, webhook) + } + } + c.ValidatingWebhooks = validatingWebhooks + + mutatingWebhooks := []admissionregv1.MutatingWebhook{} + for _, webhook := range c.MutatingWebhooks { + hasHash, err := addToHashes(&webhook, hashes) + if err != nil { + return err + } + if !hasHash { + mutatingWebhooks = append(mutatingWebhooks, webhook) + } + } + c.MutatingWebhooks = mutatingWebhooks + return nil } diff --git a/internal/generate/testdata/go/deploy/olm-catalog/memcached-operator/0.0.2/memcached-operator.v0.0.2.clusterserviceversion.yaml b/internal/generate/testdata/go/deploy/olm-catalog/memcached-operator/0.0.2/memcached-operator.v0.0.2.clusterserviceversion.yaml index 063f57f480e..280e3879bbd 100644 --- a/internal/generate/testdata/go/deploy/olm-catalog/memcached-operator/0.0.2/memcached-operator.v0.0.2.clusterserviceversion.yaml +++ b/internal/generate/testdata/go/deploy/olm-catalog/memcached-operator/0.0.2/memcached-operator.v0.0.2.clusterserviceversion.yaml @@ -175,3 +175,38 @@ spec: name: Example url: www.example.com version: 0.0.2 + webhookdefinitions: + - admissionReviewVersions: null + deploymentName: memcached-operator-webhook + failurePolicy: Fail + name: vmemcached.kb.io + rules: + - apiGroups: + - cache.example.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - memcacheds + sideEffects: null + type: ValidatingAdmissionWebhook + webhookPath: /validate-cache-example-com-v1alpha1-memcached + - admissionReviewVersions: null + deploymentName: memcached-operator-webhook + failurePolicy: Fail + name: mmemcached.kb.io + rules: + - apiGroups: + - cache.example.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - memcacheds + sideEffects: null + type: MutatingAdmissionWebhook + webhookPath: /mutate-cache-example-com-v1alpha1-memcached diff --git a/internal/generate/testdata/go/deploy/olm-catalog/memcached-operator/0.0.3/memcached-operator.v0.0.3.clusterserviceversion.yaml b/internal/generate/testdata/go/deploy/olm-catalog/memcached-operator/0.0.3/memcached-operator.v0.0.3.clusterserviceversion.yaml index a4a7890c6f9..c3cbcc597e2 100644 --- a/internal/generate/testdata/go/deploy/olm-catalog/memcached-operator/0.0.3/memcached-operator.v0.0.3.clusterserviceversion.yaml +++ b/internal/generate/testdata/go/deploy/olm-catalog/memcached-operator/0.0.3/memcached-operator.v0.0.3.clusterserviceversion.yaml @@ -176,3 +176,38 @@ spec: url: www.example.com replaces: memcached-operator.v0.0.2 version: 0.0.3 + webhookdefinitions: + - admissionReviewVersions: null + deploymentName: memcached-operator-webhook + failurePolicy: Fail + name: vmemcached.kb.io + rules: + - apiGroups: + - cache.example.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - memcacheds + sideEffects: null + type: ValidatingAdmissionWebhook + webhookPath: /validate-cache-example-com-v1alpha1-memcached + - admissionReviewVersions: null + deploymentName: memcached-operator-webhook + failurePolicy: Fail + name: mmemcached.kb.io + rules: + - apiGroups: + - cache.example.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - memcacheds + sideEffects: null + type: MutatingAdmissionWebhook + webhookPath: /mutate-cache-example-com-v1alpha1-memcached diff --git a/internal/generate/testdata/go/deploy/olm-catalog/memcached-operator/manifests/memcached-operator.clusterserviceversion.yaml b/internal/generate/testdata/go/deploy/olm-catalog/memcached-operator/manifests/memcached-operator.clusterserviceversion.yaml index 063f57f480e..280e3879bbd 100644 --- a/internal/generate/testdata/go/deploy/olm-catalog/memcached-operator/manifests/memcached-operator.clusterserviceversion.yaml +++ b/internal/generate/testdata/go/deploy/olm-catalog/memcached-operator/manifests/memcached-operator.clusterserviceversion.yaml @@ -175,3 +175,38 @@ spec: name: Example url: www.example.com version: 0.0.2 + webhookdefinitions: + - admissionReviewVersions: null + deploymentName: memcached-operator-webhook + failurePolicy: Fail + name: vmemcached.kb.io + rules: + - apiGroups: + - cache.example.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - memcacheds + sideEffects: null + type: ValidatingAdmissionWebhook + webhookPath: /validate-cache-example-com-v1alpha1-memcached + - admissionReviewVersions: null + deploymentName: memcached-operator-webhook + failurePolicy: Fail + name: mmemcached.kb.io + rules: + - apiGroups: + - cache.example.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - memcacheds + sideEffects: null + type: MutatingAdmissionWebhook + webhookPath: /mutate-cache-example-com-v1alpha1-memcached diff --git a/internal/generate/testdata/go/deploy/olm-catalog/memcached-operator/noupdate/memcached-operator.v0.0.3.clusterserviceversion.yaml b/internal/generate/testdata/go/deploy/olm-catalog/memcached-operator/noupdate/memcached-operator.v0.0.3.clusterserviceversion.yaml index 368428ec65c..758aba83895 100644 --- a/internal/generate/testdata/go/deploy/olm-catalog/memcached-operator/noupdate/memcached-operator.v0.0.3.clusterserviceversion.yaml +++ b/internal/generate/testdata/go/deploy/olm-catalog/memcached-operator/noupdate/memcached-operator.v0.0.3.clusterserviceversion.yaml @@ -172,3 +172,38 @@ spec: maturity: alpha provider: {} version: 0.0.3 + webhookdefinitions: + - admissionReviewVersions: null + deploymentName: memcached-operator-webhook + failurePolicy: Fail + name: vmemcached.kb.io + rules: + - apiGroups: + - cache.example.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - memcacheds + sideEffects: null + type: ValidatingAdmissionWebhook + webhookPath: /validate-cache-example-com-v1alpha1-memcached + - admissionReviewVersions: null + deploymentName: memcached-operator-webhook + failurePolicy: Fail + name: mmemcached.kb.io + rules: + - apiGroups: + - cache.example.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - memcacheds + sideEffects: null + type: MutatingAdmissionWebhook + webhookPath: /mutate-cache-example-com-v1alpha1-memcached diff --git a/internal/generate/testdata/go/deploy/webhooks/cache-example-com-v1alpha1-memcached.webhook.yaml b/internal/generate/testdata/go/deploy/webhooks/cache-example-com-v1alpha1-memcached.webhook.yaml new file mode 100644 index 00000000000..063f60f2f91 --- /dev/null +++ b/internal/generate/testdata/go/deploy/webhooks/cache-example-com-v1alpha1-memcached.webhook.yaml @@ -0,0 +1,50 @@ +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: MutatingWebhookConfiguration +metadata: + creationTimestamp: null + name: memcached-operator-mutating-webhook-configuration +webhooks: +- clientConfig: + caBundle: Cg== + service: + name: memcached-operator-webhook-service + namespace: memcached-operator-system + path: /mutate-cache-example-com-v1alpha1-memcached + failurePolicy: Fail + name: mmemcached.kb.io + rules: + - apiGroups: + - cache.example.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - memcacheds +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: ValidatingWebhookConfiguration +metadata: + creationTimestamp: null + name: memcached-operator-validating-webhook-configuration +webhooks: +- clientConfig: + caBundle: Cg== + service: + name: memcached-operator-webhook-service + namespace: memcached-operator-system + path: /validate-cache-example-com-v1alpha1-memcached + failurePolicy: Fail + name: vmemcached.kb.io + rules: + - apiGroups: + - cache.example.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - memcacheds