From 8f94d5e0413bfa1d9aa402be0138ccd6d7924127 Mon Sep 17 00:00:00 2001 From: "Brad P. Crochet" Date: Thu, 18 Aug 2022 12:55:08 -0400 Subject: [PATCH] Switch the bundle validate to use the API This uses the validators directly, but still has the same usage semantics, for the most part. Signed-off-by: Brad P. Crochet --- certification/engine/engine.go | 4 +- certification/internal/bundle/bundle.go | 46 ++-- .../internal/bundle/bundle_suite_test.go | 12 - certification/internal/bundle/bundle_test.go | 84 ++---- .../bundle/testdata/dockerconfig.json | 0 ...cached-operator.clusterserviceversion.yaml | 251 ++++++++++++++++++ .../invalid_bundle/metadata/annotations.yaml | 1 + ...cached-operator.clusterserviceversion.yaml | 236 ++++++++++++++++ .../metadata/annotations.yaml | 1 + ...cached-operator.clusterserviceversion.yaml | 236 ++++++++++++++++ .../no_annotations_file/metadata/.gitkeep | 0 ...cached-operator.clusterserviceversion.yaml | 236 ++++++++++++++++ .../valid_bundle/metadata/annotations.yaml | 4 + certification/internal/bundle/types.go | 10 +- .../policy/operator/deployable_by_olm.go | 7 +- .../policy/operator/deployable_by_olm_test.go | 91 +------ .../operator/testdata/dockerconfig.json | 0 ...cached-operator.clusterserviceversion.yaml | 251 ++++++++++++++++++ .../invalid_bundle/metadata/annotations.yaml | 4 + ...cached-operator.clusterserviceversion.yaml | 236 ++++++++++++++++ .../valid_bundle/metadata/annotations.yaml | 4 + .../operator/validate_operator_bundle.go | 38 +-- .../operator/validate_operator_bundle_test.go | 79 +----- go.mod | 23 ++ go.sum | 33 +++ 25 files changed, 1610 insertions(+), 277 deletions(-) create mode 100644 certification/internal/bundle/testdata/dockerconfig.json create mode 100644 certification/internal/bundle/testdata/invalid_bundle/manifests/memcached-operator.clusterserviceversion.yaml create mode 100644 certification/internal/bundle/testdata/invalid_bundle/metadata/annotations.yaml create mode 100644 certification/internal/bundle/testdata/malformed_annotations_file/manifests/memcached-operator.clusterserviceversion.yaml create mode 100644 certification/internal/bundle/testdata/malformed_annotations_file/metadata/annotations.yaml create mode 100644 certification/internal/bundle/testdata/no_annotations_file/manifests/memcached-operator.clusterserviceversion.yaml create mode 100644 certification/internal/bundle/testdata/no_annotations_file/metadata/.gitkeep create mode 100644 certification/internal/bundle/testdata/valid_bundle/manifests/memcached-operator.clusterserviceversion.yaml create mode 100644 certification/internal/bundle/testdata/valid_bundle/metadata/annotations.yaml create mode 100644 certification/internal/policy/operator/testdata/dockerconfig.json create mode 100644 certification/internal/policy/operator/testdata/invalid_bundle/manifests/memcached-operator.clusterserviceversion.yaml create mode 100644 certification/internal/policy/operator/testdata/invalid_bundle/metadata/annotations.yaml create mode 100644 certification/internal/policy/operator/testdata/valid_bundle/manifests/memcached-operator.clusterserviceversion.yaml create mode 100644 certification/internal/policy/operator/testdata/valid_bundle/metadata/annotations.yaml diff --git a/certification/engine/engine.go b/certification/engine/engine.go index 1b9485380..e2e02d6d9 100644 --- a/certification/engine/engine.go +++ b/certification/engine/engine.go @@ -56,8 +56,8 @@ func initializeChecks(ctx context.Context, p policy.Policy, cfg certification.Co return []certification.Check{ operatorpol.NewScorecardBasicSpecCheck(operatorsdk.New(cfg.ScorecardImage(), exec.Command), cfg.Namespace(), cfg.ServiceAccount(), cfg.Kubeconfig(), cfg.ScorecardWaitTime()), operatorpol.NewScorecardOlmSuiteCheck(operatorsdk.New(cfg.ScorecardImage(), exec.Command), cfg.Namespace(), cfg.ServiceAccount(), cfg.Kubeconfig(), cfg.ScorecardWaitTime()), - operatorpol.NewDeployableByOlmCheck(operatorsdk.New(cfg.ScorecardImage(), exec.Command), cfg.IndexImage(), cfg.DockerConfig(), cfg.Channel()), - operatorpol.NewValidateOperatorBundleCheck(operatorsdk.New(cfg.ScorecardImage(), exec.Command)), + operatorpol.NewDeployableByOlmCheck(cfg.IndexImage(), cfg.DockerConfig(), cfg.Channel()), + operatorpol.NewValidateOperatorBundleCheck(), operatorpol.NewCertifiedImagesCheck(pyxis.NewPyxisClient( certification.DefaultPyxisHost, "", diff --git a/certification/internal/bundle/bundle.go b/certification/internal/bundle/bundle.go index 0e12bcfb4..47c092959 100644 --- a/certification/internal/bundle/bundle.go +++ b/certification/internal/bundle/bundle.go @@ -8,10 +8,10 @@ import ( "path/filepath" "strings" - "github.com/redhat-openshift-ecosystem/openshift-preflight/certification/internal/operatorsdk" - "github.com/blang/semver" "github.com/operator-framework/api/pkg/manifests" + "github.com/operator-framework/api/pkg/validation" + olmvalidation "github.com/redhat-openshift-ecosystem/ocp-olm-catalog-validator/pkg/validation" log "github.com/sirupsen/logrus" rbacv1 "k8s.io/api/rbac/v1" "sigs.k8s.io/yaml" @@ -29,21 +29,22 @@ var ocpToKubeVersion = map[string]string{ const latestReleasedVersion = "4.11" -type operatorSdk interface { - BundleValidate(context.Context, string, operatorsdk.OperatorSdkBundleValidateOptions) (*operatorsdk.OperatorSdkBundleValidateReport, error) -} +func Validate(ctx context.Context, imagePath string) (*Report, error) { + log.Trace("reading annotations file from the bundle") + log.Debug("image extraction directory is ", imagePath) -func Validate(ctx context.Context, operatorSdk operatorSdk, imagePath string) (*operatorsdk.OperatorSdkBundleValidateReport, error) { - selector := []string{"community", "operatorhub", "alpha-deprecated-apis"} - opts := operatorsdk.OperatorSdkBundleValidateOptions{ - Selector: selector, - Verbose: true, - ContainerEngine: "none", - OutputFormat: "json-alpha1", + bundle, err := manifests.GetBundleFromDir(imagePath) + if err != nil { + return nil, fmt.Errorf("could not load bundle from path: %s: %v", imagePath, err) } + validators := validation.DefaultBundleValidators.WithValidators( + validation.AlphaDeprecatedAPIsValidator, + validation.OperatorHubValidator, + olmvalidation.OpenShiftValidator, + ) + + objs := bundle.ObjectsToValidate() - log.Trace("reading annotations file from the bundle") - log.Debug("image extraction directory is ", imagePath) // retrieve the operator metadata from bundle image annotationsFileName := filepath.Join(imagePath, "metadata", "annotations.yaml") annotationsFile, err := os.Open(annotationsFileName) @@ -55,6 +56,7 @@ func Validate(ctx context.Context, operatorSdk operatorSdk, imagePath string) (* return nil, fmt.Errorf("unable to get annotations.yaml from the bundle: %v", err) } + optionalValues := make(map[string]string) if annotations.OpenshiftVersions != "" { // Check that the label range contains >= 4.9 targetVersion, err := targetVersion(annotations.OpenshiftVersions) @@ -64,12 +66,22 @@ func Validate(ctx context.Context, operatorSdk operatorSdk, imagePath string) (* } if k8sVer, found := ocpToKubeVersion[targetVersion]; found { log.Debugf("OpenShift %s detected in annotations. Running with additional checks enabled.", targetVersion) - opts.OptionalValues = make(map[string]string) - opts.OptionalValues["k8s-version"] = k8sVer + optionalValues = make(map[string]string) + optionalValues["k8s-version"] = k8sVer + } + } + objs = append(objs, optionalValues) + + results := validators.Validate(objs...) + passed := true + for _, v := range results { + if v.HasError() { + passed = false + break } } - return operatorSdk.BundleValidate(ctx, imagePath, opts) + return &Report{Results: results, Passed: passed}, nil } func targetVersion(ocpLabelIndex string) (string, error) { diff --git a/certification/internal/bundle/bundle_suite_test.go b/certification/internal/bundle/bundle_suite_test.go index ea831af88..0bc91b6ff 100644 --- a/certification/internal/bundle/bundle_suite_test.go +++ b/certification/internal/bundle/bundle_suite_test.go @@ -1,12 +1,9 @@ package bundle import ( - "context" "errors" "testing" - "github.com/redhat-openshift-ecosystem/openshift-preflight/certification/internal/operatorsdk" - . "github.com/onsi/ginkgo/v2/dsl/core" . "github.com/onsi/gomega" ) @@ -16,15 +13,6 @@ func TestBundle(t *testing.T) { RunSpecs(t, "Bundle Utils Suite") } -type FakeOperatorSdk struct { - OperatorSdkReport operatorsdk.OperatorSdkScorecardReport - OperatorSdkBVReport operatorsdk.OperatorSdkBundleValidateReport -} - -func (f FakeOperatorSdk) BundleValidate(ctx context.Context, image string, opts operatorsdk.OperatorSdkBundleValidateOptions) (*operatorsdk.OperatorSdkBundleValidateReport, error) { - return &f.OperatorSdkBVReport, nil -} - // In order to test some negative paths, this io.Reader will just throw an error type errReader int diff --git a/certification/internal/bundle/bundle_test.go b/certification/internal/bundle/bundle_test.go index 09f5e6390..60711b6c2 100644 --- a/certification/internal/bundle/bundle_test.go +++ b/certification/internal/bundle/bundle_test.go @@ -3,95 +3,55 @@ package bundle import ( "bytes" "context" - "os" - "path/filepath" "github.com/redhat-openshift-ecosystem/openshift-preflight/certification" - . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/ginkgo/v2/dsl/core" + . "github.com/onsi/ginkgo/v2/dsl/table" . "github.com/onsi/gomega" ) var _ = Describe("BundleValidateCheck", func() { - const ( - manifestsDir = "manifests" - metadataDir = "metadata" - annotationFilename = "annotations.yaml" - annotations = `annotations: - com.redhat.openshift.versions: "v4.6-v4.9" - operators.operatorframework.io.bundle.package.v1: testPackage - operators.operatorframework.io.bundle.channel.default.v1: testChannel -` - ) - Describe("Bundle validation", func() { - var ( - imageRef certification.ImageReference - fakeEngine operatorSdk - ) - - BeforeEach(func() { - // mock bundle directory - tmpDir, err := os.MkdirTemp("", "bundle-metadata-*") - Expect(err).ToNot(HaveOccurred()) - - err = os.Mkdir(filepath.Join(tmpDir, metadataDir), 0o755) - Expect(err).ToNot(HaveOccurred()) - - err = os.Mkdir(filepath.Join(tmpDir, manifestsDir), 0o755) - Expect(err).ToNot(HaveOccurred()) - - err = os.WriteFile(filepath.Join(tmpDir, metadataDir, annotationFilename), []byte(annotations), 0o644) - Expect(err).ToNot(HaveOccurred()) - - imageRef.ImageFSPath = tmpDir - fakeEngine = FakeOperatorSdk{} - }) - - AfterEach(func() { - err := os.RemoveAll(imageRef.ImageFSPath) - Expect(err).ToNot(HaveOccurred()) - }) - Context("the annotations file is valid", func() { It("should pass", func() { - report, err := Validate(context.Background(), fakeEngine, imageRef.ImageFSPath) + imageRef := certification.ImageReference{ + ImageFSPath: "./testdata/valid_bundle", + } + report, err := Validate(context.Background(), imageRef.ImageFSPath) Expect(err).ToNot(HaveOccurred()) Expect(report).ToNot(BeNil()) }) }) Context("the annotations file does not exist", func() { - JustBeforeEach(func() { - err := os.Remove(filepath.Join(imageRef.ImageFSPath, metadataDir, annotationFilename)) - Expect(err).ToNot(HaveOccurred()) - }) It("should error", func() { - report, err := Validate(context.Background(), fakeEngine, imageRef.ImageFSPath) + imageRef := certification.ImageReference{ + ImageFSPath: "./testdata/no_annotations_file", + } + report, err := Validate(context.Background(), imageRef.ImageFSPath) Expect(err).To(HaveOccurred()) Expect(report).To(BeNil()) }) }) Context("the annotations file is malformed", func() { - JustBeforeEach(func() { - err := os.WriteFile(filepath.Join(imageRef.ImageFSPath, metadataDir, annotationFilename), []byte("badAnnotations"), 0o644) - Expect(err).ToNot(HaveOccurred()) - }) It("should error", func() { - report, err := Validate(context.Background(), fakeEngine, imageRef.ImageFSPath) + imageRef := certification.ImageReference{ + ImageFSPath: "./testdata/malformed_annotations_file", + } + report, err := Validate(context.Background(), imageRef.ImageFSPath) Expect(err).To(HaveOccurred()) Expect(report).To(BeNil()) }) }) Context("the annotations file is valid but has no annotations", func() { - JustBeforeEach(func() { - err := os.WriteFile(filepath.Join(imageRef.ImageFSPath, metadataDir, annotationFilename), []byte("annotations:"), 0o644) - Expect(err).ToNot(HaveOccurred()) - }) It("should fail gracefully", func() { - report, err := Validate(context.Background(), fakeEngine, imageRef.ImageFSPath) + imageRef := certification.ImageReference{ + ImageFSPath: "./testdata/invalid_bundle", + } + report, err := Validate(context.Background(), imageRef.ImageFSPath) Expect(err).ToNot(HaveOccurred()) Expect(report).ToNot(BeNil()) }) @@ -101,14 +61,6 @@ var _ = Describe("BundleValidateCheck", func() { Describe("While ensuring that container util is working", func() { // tests: extractAnnotationsBytes Context("with an annotations yaml data read from disk", func() { - Context("with the correct format", func() { - It("should properly marshal to a map[string]string", func() { - annotations, err := LoadAnnotations(context.TODO(), bytes.NewReader([]byte(annotations))) - Expect(err).ToNot(HaveOccurred()) - Expect(annotations.DefaultChannelName).To(Equal("testChannel")) - }) - }) - Context("containing no data read in from the yaml file", func() { data := []byte{} diff --git a/certification/internal/bundle/testdata/dockerconfig.json b/certification/internal/bundle/testdata/dockerconfig.json new file mode 100644 index 000000000..e69de29bb diff --git a/certification/internal/bundle/testdata/invalid_bundle/manifests/memcached-operator.clusterserviceversion.yaml b/certification/internal/bundle/testdata/invalid_bundle/manifests/memcached-operator.clusterserviceversion.yaml new file mode 100644 index 000000000..fc76657f3 --- /dev/null +++ b/certification/internal/bundle/testdata/invalid_bundle/manifests/memcached-operator.clusterserviceversion.yaml @@ -0,0 +1,251 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: ClusterServiceVersion +metadata: + annotations: + alm-examples: |- + [ + { + "apiVersion": "cache.example.com/v1alpha1", + "kind": "Memcached", + "metadata": { + "name": "memcached-sample" + }, + "spec": { + "size": 1 + } + } + ] + capabilities: Basic Install + name: memcached-operator.v0.0.1 + namespace: placeholder +spec: + apiservicedefinitions: {} + customresourcedefinitions: + owned: + - description: Memcached is the Schema for the memcacheds API + displayName: Memcached + kind: Memcached + name: memcacheds.cache.example.com + version: v1alpha1 + description: Memcached Operator description. TODO. + icon: + - base64data: "" + mediatype: "" + install: + spec: + clusterPermissions: + - rules: + - apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - cache.example.com + resources: + - memcacheds + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - cache.example.com + resources: + - memcacheds/finalizers + verbs: + - update + - apiGroups: + - cache.example.com + resources: + - memcacheds/status + verbs: + - get + - patch + - update + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create + serviceAccountName: memcached-operator-controller-manager + deployments: + - name: memcached-operator-controller-manager + spec: + replicas: 1 + selector: + matchLabels: + control-plane: controller-manager + strategy: {} + template: + metadata: + labels: + control-plane: controller-manager + spec: + containers: + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=10 + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + resources: {} + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=127.0.0.1:8080 + - --leader-elect + command: + - /manager + image: quay.io/example/memcached-operator:v0.0.1 + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + securityContext: + allowPrivilegeEscalation: false + securityContext: + runAsNonRoot: true + serviceAccountName: memcached-operator-controller-manager + terminationGracePeriodSeconds: 10 + permissions: + - rules: + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + serviceAccountName: memcached-operator-controller-manager + strategy: deployment + installModes: + - supported: false + type: OwnNamespace + - supported: false + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces + keywords: + - memcached-operator + links: + - name: Memcached Operator + url: https://memcached-operator.domain + maintainers: + - email: your@email.com + name: Maintainer Name + maturity: alpha + provider: + name: Provider Name + url: https://your.domain + version: 0.0.1 + webhookdefinitions: + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: memcached-operator-controller-manager + failurePolicy: Fail + generateName: vmemcached.kb.io + rules: + - apiGroups: + - cache.example.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - memcacheds + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-cache-example-com-v1alpha1-memcached + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: memcached-operator-controller-manager + failurePolicy: Fail + generateName: mmemcached.kb.io + rules: + - apiGroups: + - cache.example.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - memcacheds + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-cache-example-com-v1alpha1-memcached diff --git a/certification/internal/bundle/testdata/invalid_bundle/metadata/annotations.yaml b/certification/internal/bundle/testdata/invalid_bundle/metadata/annotations.yaml new file mode 100644 index 000000000..9ca887e66 --- /dev/null +++ b/certification/internal/bundle/testdata/invalid_bundle/metadata/annotations.yaml @@ -0,0 +1 @@ +annotations: diff --git a/certification/internal/bundle/testdata/malformed_annotations_file/manifests/memcached-operator.clusterserviceversion.yaml b/certification/internal/bundle/testdata/malformed_annotations_file/manifests/memcached-operator.clusterserviceversion.yaml new file mode 100644 index 000000000..bdfc29153 --- /dev/null +++ b/certification/internal/bundle/testdata/malformed_annotations_file/manifests/memcached-operator.clusterserviceversion.yaml @@ -0,0 +1,236 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: ClusterServiceVersion +metadata: + annotations: + capabilities: Basic Install + name: memcached-operator.v0.0.1 + namespace: placeholder +spec: + apiservicedefinitions: {} + description: Memcached Operator description. TODO. + displayName: Memcached Operator + install: + spec: + clusterPermissions: + - rules: + - apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - cache.example.com + resources: + - memcacheds + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - cache.example.com + resources: + - memcacheds/finalizers + verbs: + - update + - apiGroups: + - cache.example.com + resources: + - memcacheds/status + verbs: + - get + - patch + - update + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create + serviceAccountName: memcached-operator-controller-manager + deployments: + - name: memcached-operator-controller-manager + spec: + replicas: 1 + selector: + matchLabels: + control-plane: controller-manager + strategy: {} + template: + metadata: + labels: + control-plane: controller-manager + spec: + containers: + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=10 + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + resources: {} + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=127.0.0.1:8080 + - --leader-elect + command: + - /manager + image: quay.io/example/memcached-operator:v0.0.1 + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 100m + memory: 30Mi + requests: + cpu: 100m + memory: 20Mi + securityContext: + allowPrivilegeEscalation: false + securityContext: + runAsNonRoot: true + serviceAccountName: memcached-operator-controller-manager + terminationGracePeriodSeconds: 10 + permissions: + - rules: + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + serviceAccountName: memcached-operator-controller-manager + strategy: deployment + installModes: + - supported: false + type: OwnNamespace + - supported: false + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces + keywords: + - memcached-operator + links: + - name: Memcached Operator + url: https://memcached-operator.domain + maintainers: + - email: your@email.com + name: Maintainer Name + maturity: alpha + provider: + name: Provider Name + url: https://your.domain + version: 0.0.1 + webhookdefinitions: + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: memcached-operator-controller-manager + failurePolicy: Fail + generateName: vmemcached.kb.io + rules: + - apiGroups: + - cache.example.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - memcacheds + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-cache-example-com-v1alpha1-memcached + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: memcached-operator-controller-manager + failurePolicy: Fail + generateName: mmemcached.kb.io + rules: + - apiGroups: + - cache.example.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - memcacheds + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-cache-example-com-v1alpha1-memcached diff --git a/certification/internal/bundle/testdata/malformed_annotations_file/metadata/annotations.yaml b/certification/internal/bundle/testdata/malformed_annotations_file/metadata/annotations.yaml new file mode 100644 index 000000000..ae5b039c6 --- /dev/null +++ b/certification/internal/bundle/testdata/malformed_annotations_file/metadata/annotations.yaml @@ -0,0 +1 @@ +badAnnotations diff --git a/certification/internal/bundle/testdata/no_annotations_file/manifests/memcached-operator.clusterserviceversion.yaml b/certification/internal/bundle/testdata/no_annotations_file/manifests/memcached-operator.clusterserviceversion.yaml new file mode 100644 index 000000000..bdfc29153 --- /dev/null +++ b/certification/internal/bundle/testdata/no_annotations_file/manifests/memcached-operator.clusterserviceversion.yaml @@ -0,0 +1,236 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: ClusterServiceVersion +metadata: + annotations: + capabilities: Basic Install + name: memcached-operator.v0.0.1 + namespace: placeholder +spec: + apiservicedefinitions: {} + description: Memcached Operator description. TODO. + displayName: Memcached Operator + install: + spec: + clusterPermissions: + - rules: + - apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - cache.example.com + resources: + - memcacheds + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - cache.example.com + resources: + - memcacheds/finalizers + verbs: + - update + - apiGroups: + - cache.example.com + resources: + - memcacheds/status + verbs: + - get + - patch + - update + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create + serviceAccountName: memcached-operator-controller-manager + deployments: + - name: memcached-operator-controller-manager + spec: + replicas: 1 + selector: + matchLabels: + control-plane: controller-manager + strategy: {} + template: + metadata: + labels: + control-plane: controller-manager + spec: + containers: + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=10 + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + resources: {} + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=127.0.0.1:8080 + - --leader-elect + command: + - /manager + image: quay.io/example/memcached-operator:v0.0.1 + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 100m + memory: 30Mi + requests: + cpu: 100m + memory: 20Mi + securityContext: + allowPrivilegeEscalation: false + securityContext: + runAsNonRoot: true + serviceAccountName: memcached-operator-controller-manager + terminationGracePeriodSeconds: 10 + permissions: + - rules: + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + serviceAccountName: memcached-operator-controller-manager + strategy: deployment + installModes: + - supported: false + type: OwnNamespace + - supported: false + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces + keywords: + - memcached-operator + links: + - name: Memcached Operator + url: https://memcached-operator.domain + maintainers: + - email: your@email.com + name: Maintainer Name + maturity: alpha + provider: + name: Provider Name + url: https://your.domain + version: 0.0.1 + webhookdefinitions: + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: memcached-operator-controller-manager + failurePolicy: Fail + generateName: vmemcached.kb.io + rules: + - apiGroups: + - cache.example.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - memcacheds + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-cache-example-com-v1alpha1-memcached + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: memcached-operator-controller-manager + failurePolicy: Fail + generateName: mmemcached.kb.io + rules: + - apiGroups: + - cache.example.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - memcacheds + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-cache-example-com-v1alpha1-memcached diff --git a/certification/internal/bundle/testdata/no_annotations_file/metadata/.gitkeep b/certification/internal/bundle/testdata/no_annotations_file/metadata/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/certification/internal/bundle/testdata/valid_bundle/manifests/memcached-operator.clusterserviceversion.yaml b/certification/internal/bundle/testdata/valid_bundle/manifests/memcached-operator.clusterserviceversion.yaml new file mode 100644 index 000000000..bdfc29153 --- /dev/null +++ b/certification/internal/bundle/testdata/valid_bundle/manifests/memcached-operator.clusterserviceversion.yaml @@ -0,0 +1,236 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: ClusterServiceVersion +metadata: + annotations: + capabilities: Basic Install + name: memcached-operator.v0.0.1 + namespace: placeholder +spec: + apiservicedefinitions: {} + description: Memcached Operator description. TODO. + displayName: Memcached Operator + install: + spec: + clusterPermissions: + - rules: + - apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - cache.example.com + resources: + - memcacheds + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - cache.example.com + resources: + - memcacheds/finalizers + verbs: + - update + - apiGroups: + - cache.example.com + resources: + - memcacheds/status + verbs: + - get + - patch + - update + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create + serviceAccountName: memcached-operator-controller-manager + deployments: + - name: memcached-operator-controller-manager + spec: + replicas: 1 + selector: + matchLabels: + control-plane: controller-manager + strategy: {} + template: + metadata: + labels: + control-plane: controller-manager + spec: + containers: + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=10 + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + resources: {} + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=127.0.0.1:8080 + - --leader-elect + command: + - /manager + image: quay.io/example/memcached-operator:v0.0.1 + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 100m + memory: 30Mi + requests: + cpu: 100m + memory: 20Mi + securityContext: + allowPrivilegeEscalation: false + securityContext: + runAsNonRoot: true + serviceAccountName: memcached-operator-controller-manager + terminationGracePeriodSeconds: 10 + permissions: + - rules: + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + serviceAccountName: memcached-operator-controller-manager + strategy: deployment + installModes: + - supported: false + type: OwnNamespace + - supported: false + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces + keywords: + - memcached-operator + links: + - name: Memcached Operator + url: https://memcached-operator.domain + maintainers: + - email: your@email.com + name: Maintainer Name + maturity: alpha + provider: + name: Provider Name + url: https://your.domain + version: 0.0.1 + webhookdefinitions: + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: memcached-operator-controller-manager + failurePolicy: Fail + generateName: vmemcached.kb.io + rules: + - apiGroups: + - cache.example.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - memcacheds + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-cache-example-com-v1alpha1-memcached + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: memcached-operator-controller-manager + failurePolicy: Fail + generateName: mmemcached.kb.io + rules: + - apiGroups: + - cache.example.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - memcacheds + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-cache-example-com-v1alpha1-memcached diff --git a/certification/internal/bundle/testdata/valid_bundle/metadata/annotations.yaml b/certification/internal/bundle/testdata/valid_bundle/metadata/annotations.yaml new file mode 100644 index 000000000..54fd9aaad --- /dev/null +++ b/certification/internal/bundle/testdata/valid_bundle/metadata/annotations.yaml @@ -0,0 +1,4 @@ +annotations: + com.redhat.openshift.versions: "v4.6-v4.9" + operators.operatorframework.io.bundle.package.v1: testPackage + operators.operatorframework.io.bundle.channel.default.v1: testChannel diff --git a/certification/internal/bundle/types.go b/certification/internal/bundle/types.go index 24026894c..185e3363c 100644 --- a/certification/internal/bundle/types.go +++ b/certification/internal/bundle/types.go @@ -1,6 +1,9 @@ package bundle -import "github.com/operator-framework/api/pkg/manifests" +import ( + "github.com/operator-framework/api/pkg/manifests" + validationerrors "github.com/operator-framework/api/pkg/validation/errors" +) type AnnotationsFile struct { Annotations Annotations `json:"annotations" yaml:"annotations"` @@ -11,3 +14,8 @@ type Annotations struct { OpenshiftVersions string `json:"com.redhat.openshift.versions,omitempty" yaml:"com.redhat.openshift.versions,omitempty"` } + +type Report struct { + Results []validationerrors.ManifestResult + Passed bool +} diff --git a/certification/internal/policy/operator/deployable_by_olm.go b/certification/internal/policy/operator/deployable_by_olm.go index 4648275d0..8fd291fd8 100644 --- a/certification/internal/policy/operator/deployable_by_olm.go +++ b/certification/internal/policy/operator/deployable_by_olm.go @@ -50,7 +50,6 @@ type DeployableByOlmCheck struct { // channel is optional. If empty, we will introspect. channel string - OperatorSdk operatorSdk openshiftClient openshift.Client client crclient.Client csvReady bool @@ -92,13 +91,11 @@ func (p *DeployableByOlmCheck) initOpenShifeEngine() { // in scope are public. An empty channel value implies that the check should // introspect the channel from the bundle. indexImage is required. func NewDeployableByOlmCheck( - operatorSdk operatorSdk, indexImage, dockerConfig, channel string, ) *DeployableByOlmCheck { return &DeployableByOlmCheck{ - OperatorSdk: operatorSdk, dockerConfig: dockerConfig, indexImage: indexImage, channel: channel, @@ -110,7 +107,7 @@ func (p *DeployableByOlmCheck) Validate(ctx context.Context, bundleRef certifica return false, fmt.Errorf("%v", err) } p.initOpenShifeEngine() - if report, err := bundle.Validate(ctx, p.OperatorSdk, bundleRef.ImageFSPath); err != nil || !report.Passed { + if report, err := bundle.Validate(ctx, bundleRef.ImageFSPath); err != nil || !report.Passed { return false, fmt.Errorf("%v", err) } @@ -649,6 +646,6 @@ func (p *DeployableByOlmCheck) Metadata() certification.Metadata { func (p *DeployableByOlmCheck) Help() certification.HelpText { return certification.HelpText{ Message: "It is required that your operator could be deployed by OLM", - Suggestion: "Follow the guidelines on the operatorsdk website to learn how to package your operator https://sdk.operatorframework.io/docs/olm-integration/cli-overview/", + Suggestion: "Follow the guidelines on the operator-sdk website to learn how to package your operator https://sdk.operatorframework.io/docs/olm-integration/cli-overview/", } } diff --git a/certification/internal/policy/operator/deployable_by_olm_test.go b/certification/internal/policy/operator/deployable_by_olm_test.go index c0c498f2f..e882038f8 100644 --- a/certification/internal/policy/operator/deployable_by_olm_test.go +++ b/certification/internal/policy/operator/deployable_by_olm_test.go @@ -3,13 +3,11 @@ package operator import ( "context" "os" - "path/filepath" "time" "github.com/redhat-openshift-ecosystem/openshift-preflight/certification" "github.com/redhat-openshift-ecosystem/openshift-preflight/certification/artifacts" "github.com/redhat-openshift-ecosystem/openshift-preflight/certification/internal/openshift" - "github.com/redhat-openshift-ecosystem/openshift-preflight/certification/internal/operatorsdk" fakecranev1 "github.com/google/go-containerregistry/pkg/v1/fake" . "github.com/onsi/ginkgo/v2" @@ -23,90 +21,24 @@ import ( var _ = Describe("DeployableByOLMCheck", func() { var ( deployableByOLMCheck DeployableByOlmCheck - fakeEngine operatorSdk imageRef certification.ImageReference - tmpDockerDir string - client crclient.Client ) - const ( - metadataDir = "metadata" - manifestDir = "manifests" - registryConfigDir = ".docker" - annotationFilename = "annotations.yaml" - csvFilename = "test-operator.clusterserviceversion.yaml" - registryConfigFilename = "config.json" - annotations = `annotations: - operators.operatorframework.io.bundle.package.v1: testPackage - operators.operatorframework.io.bundle.channel.default.v1: testChannel - operators.operatorframework.io.bundle.channels.v1: testChannel -` - registryAuthToken = `{ -"auths": { - "quay.io": { - "auth": "auth-token-test" - } - } -}` - csvStr = `apiVersion: operators.coreos.com/v1alpha1 -kind: ClusterServiceVersion -spec: - installModes: - - supported: false - type: OwnNamespace - - supported: false - type: SingleNamespace - - supported: false - type: MultiNamespace - - supported: true - type: AllNamespaces -` - ) BeforeEach(func() { // override default timeout subscriptionTimeout = 1 * time.Second csvTimeout = 1 * time.Second - // mock bundle directory - tmpDir, err := os.MkdirTemp("", "bundle-metadata-*") - Expect(err).ToNot(HaveOccurred()) - - Expect(os.Mkdir(filepath.Join(tmpDir, metadataDir), 0o755)).To(Succeed()) - Expect(os.WriteFile(filepath.Join(tmpDir, metadataDir, annotationFilename), []byte(annotations), 0o644)).To(Succeed()) - - // mock csv file - Expect(os.Mkdir(filepath.Join(tmpDir, manifestDir), 0o755)).To(Succeed()) - Expect(os.WriteFile(filepath.Join(tmpDir, manifestDir, csvFilename), []byte(csvStr), 0o644)).To(Succeed()) - - // mock docker config file - tmpDockerDir, err = os.MkdirTemp("", "docker-config-*") - Expect(err).ToNot(HaveOccurred()) - - Expect(os.Mkdir(filepath.Join(tmpDockerDir, registryConfigDir), 0o755)).To(Succeed()) - Expect(os.WriteFile(filepath.Join( - tmpDockerDir, - registryConfigDir, - registryConfigFilename), - []byte(registryAuthToken), - 0o644)).To(Succeed()) - fakeImage := fakecranev1.FakeImage{} imageRef.ImageInfo = &fakeImage - imageRef.ImageFSPath = tmpDir - - report := operatorsdk.OperatorSdkBundleValidateReport{ - Passed: true, - Outputs: []operatorsdk.OperatorSdkBundleValidateOutput{}, - } - fakeEngine = FakeOperatorSdk{ - OperatorSdkBVReport: report, - } + imageRef.ImageFSPath = "./testdata/valid_bundle" now := metav1.Now() og.Status.LastUpdated = &now - deployableByOLMCheck = *NewDeployableByOlmCheck(fakeEngine, "test_indeximage", "", "") + deployableByOLMCheck = *NewDeployableByOlmCheck("test_indeximage", "", "") scheme := apiruntime.NewScheme() Expect(openshift.AddSchemes(scheme)).To(Succeed()) + var client crclient.Client client = fake.NewClientBuilder(). WithScheme(scheme). WithObjects(&csv, &csvDefault, &csvMarketplace, &ns, &secret, &sub, &og). @@ -114,6 +46,9 @@ spec: Build() deployableByOLMCheck.client = client + // Temp artifacts dir + tmpDir, err := os.MkdirTemp("", "deployable-by-olm-*") + Expect(err).ToNot(HaveOccurred()) artifacts.SetDir(tmpDir) DeferCleanup(os.RemoveAll, tmpDir) DeferCleanup(artifacts.Reset) @@ -130,12 +65,12 @@ spec: Context("When installedCSV field of Subscription is not set", func() { BeforeEach(func() { badSub := sub - Expect(client.Get(context.TODO(), crclient.ObjectKey{ + Expect(deployableByOLMCheck.client.Get(context.TODO(), crclient.ObjectKey{ Name: "testPackage", Namespace: "testPackage", }, &badSub)).To(Succeed()) badSub.Status.InstalledCSV = "" - Expect(client.Update(context.TODO(), &badSub, &crclient.UpdateOptions{})).To(Succeed()) + Expect(deployableByOLMCheck.client.Update(context.TODO(), &badSub, &crclient.UpdateOptions{})).To(Succeed()) }) It("Should fail Validate", func() { ok, err := deployableByOLMCheck.Validate(context.TODO(), imageRef) @@ -155,7 +90,8 @@ spec: }) Context("When index image is in a private registry and CSV has been created successfully", func() { BeforeEach(func() { - deployableByOLMCheck.dockerConfig = filepath.Join(tmpDockerDir, registryConfigDir, registryConfigFilename) + // dockerconfig.json is just an empty file. It just needs to exist. + deployableByOLMCheck.dockerConfig = "./testdata/dockerconfig.json" }) It("Should pass Validate", func() { ok, err := deployableByOLMCheck.Validate(context.TODO(), imageRef) @@ -197,11 +133,4 @@ spec: Entry("registry.access.redhat.com", []string{"registry.access.redhat.com/ubi8/ubi"}, true), Entry("quay.io", []string{"quay.io/rocrisp/preflight-operator-bundle:v1"}, false), ) - AfterEach(func() { - err := os.RemoveAll(imageRef.ImageFSPath) - Expect(err).ToNot(HaveOccurred()) - - err = os.RemoveAll(tmpDockerDir) - Expect(err).ToNot(HaveOccurred()) - }) }) diff --git a/certification/internal/policy/operator/testdata/dockerconfig.json b/certification/internal/policy/operator/testdata/dockerconfig.json new file mode 100644 index 000000000..e69de29bb diff --git a/certification/internal/policy/operator/testdata/invalid_bundle/manifests/memcached-operator.clusterserviceversion.yaml b/certification/internal/policy/operator/testdata/invalid_bundle/manifests/memcached-operator.clusterserviceversion.yaml new file mode 100644 index 000000000..fc76657f3 --- /dev/null +++ b/certification/internal/policy/operator/testdata/invalid_bundle/manifests/memcached-operator.clusterserviceversion.yaml @@ -0,0 +1,251 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: ClusterServiceVersion +metadata: + annotations: + alm-examples: |- + [ + { + "apiVersion": "cache.example.com/v1alpha1", + "kind": "Memcached", + "metadata": { + "name": "memcached-sample" + }, + "spec": { + "size": 1 + } + } + ] + capabilities: Basic Install + name: memcached-operator.v0.0.1 + namespace: placeholder +spec: + apiservicedefinitions: {} + customresourcedefinitions: + owned: + - description: Memcached is the Schema for the memcacheds API + displayName: Memcached + kind: Memcached + name: memcacheds.cache.example.com + version: v1alpha1 + description: Memcached Operator description. TODO. + icon: + - base64data: "" + mediatype: "" + install: + spec: + clusterPermissions: + - rules: + - apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - cache.example.com + resources: + - memcacheds + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - cache.example.com + resources: + - memcacheds/finalizers + verbs: + - update + - apiGroups: + - cache.example.com + resources: + - memcacheds/status + verbs: + - get + - patch + - update + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create + serviceAccountName: memcached-operator-controller-manager + deployments: + - name: memcached-operator-controller-manager + spec: + replicas: 1 + selector: + matchLabels: + control-plane: controller-manager + strategy: {} + template: + metadata: + labels: + control-plane: controller-manager + spec: + containers: + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=10 + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + resources: {} + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=127.0.0.1:8080 + - --leader-elect + command: + - /manager + image: quay.io/example/memcached-operator:v0.0.1 + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + securityContext: + allowPrivilegeEscalation: false + securityContext: + runAsNonRoot: true + serviceAccountName: memcached-operator-controller-manager + terminationGracePeriodSeconds: 10 + permissions: + - rules: + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + serviceAccountName: memcached-operator-controller-manager + strategy: deployment + installModes: + - supported: false + type: OwnNamespace + - supported: false + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces + keywords: + - memcached-operator + links: + - name: Memcached Operator + url: https://memcached-operator.domain + maintainers: + - email: your@email.com + name: Maintainer Name + maturity: alpha + provider: + name: Provider Name + url: https://your.domain + version: 0.0.1 + webhookdefinitions: + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: memcached-operator-controller-manager + failurePolicy: Fail + generateName: vmemcached.kb.io + rules: + - apiGroups: + - cache.example.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - memcacheds + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-cache-example-com-v1alpha1-memcached + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: memcached-operator-controller-manager + failurePolicy: Fail + generateName: mmemcached.kb.io + rules: + - apiGroups: + - cache.example.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - memcacheds + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-cache-example-com-v1alpha1-memcached diff --git a/certification/internal/policy/operator/testdata/invalid_bundle/metadata/annotations.yaml b/certification/internal/policy/operator/testdata/invalid_bundle/metadata/annotations.yaml new file mode 100644 index 000000000..54fd9aaad --- /dev/null +++ b/certification/internal/policy/operator/testdata/invalid_bundle/metadata/annotations.yaml @@ -0,0 +1,4 @@ +annotations: + com.redhat.openshift.versions: "v4.6-v4.9" + operators.operatorframework.io.bundle.package.v1: testPackage + operators.operatorframework.io.bundle.channel.default.v1: testChannel diff --git a/certification/internal/policy/operator/testdata/valid_bundle/manifests/memcached-operator.clusterserviceversion.yaml b/certification/internal/policy/operator/testdata/valid_bundle/manifests/memcached-operator.clusterserviceversion.yaml new file mode 100644 index 000000000..bdfc29153 --- /dev/null +++ b/certification/internal/policy/operator/testdata/valid_bundle/manifests/memcached-operator.clusterserviceversion.yaml @@ -0,0 +1,236 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: ClusterServiceVersion +metadata: + annotations: + capabilities: Basic Install + name: memcached-operator.v0.0.1 + namespace: placeholder +spec: + apiservicedefinitions: {} + description: Memcached Operator description. TODO. + displayName: Memcached Operator + install: + spec: + clusterPermissions: + - rules: + - apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - cache.example.com + resources: + - memcacheds + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - cache.example.com + resources: + - memcacheds/finalizers + verbs: + - update + - apiGroups: + - cache.example.com + resources: + - memcacheds/status + verbs: + - get + - patch + - update + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create + serviceAccountName: memcached-operator-controller-manager + deployments: + - name: memcached-operator-controller-manager + spec: + replicas: 1 + selector: + matchLabels: + control-plane: controller-manager + strategy: {} + template: + metadata: + labels: + control-plane: controller-manager + spec: + containers: + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=10 + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + resources: {} + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=127.0.0.1:8080 + - --leader-elect + command: + - /manager + image: quay.io/example/memcached-operator:v0.0.1 + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 100m + memory: 30Mi + requests: + cpu: 100m + memory: 20Mi + securityContext: + allowPrivilegeEscalation: false + securityContext: + runAsNonRoot: true + serviceAccountName: memcached-operator-controller-manager + terminationGracePeriodSeconds: 10 + permissions: + - rules: + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + serviceAccountName: memcached-operator-controller-manager + strategy: deployment + installModes: + - supported: false + type: OwnNamespace + - supported: false + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces + keywords: + - memcached-operator + links: + - name: Memcached Operator + url: https://memcached-operator.domain + maintainers: + - email: your@email.com + name: Maintainer Name + maturity: alpha + provider: + name: Provider Name + url: https://your.domain + version: 0.0.1 + webhookdefinitions: + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: memcached-operator-controller-manager + failurePolicy: Fail + generateName: vmemcached.kb.io + rules: + - apiGroups: + - cache.example.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - memcacheds + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-cache-example-com-v1alpha1-memcached + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: memcached-operator-controller-manager + failurePolicy: Fail + generateName: mmemcached.kb.io + rules: + - apiGroups: + - cache.example.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - memcacheds + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-cache-example-com-v1alpha1-memcached diff --git a/certification/internal/policy/operator/testdata/valid_bundle/metadata/annotations.yaml b/certification/internal/policy/operator/testdata/valid_bundle/metadata/annotations.yaml new file mode 100644 index 000000000..54fd9aaad --- /dev/null +++ b/certification/internal/policy/operator/testdata/valid_bundle/metadata/annotations.yaml @@ -0,0 +1,4 @@ +annotations: + com.redhat.openshift.versions: "v4.6-v4.9" + operators.operatorframework.io.bundle.package.v1: testPackage + operators.operatorframework.io.bundle.channel.default.v1: testChannel diff --git a/certification/internal/policy/operator/validate_operator_bundle.go b/certification/internal/policy/operator/validate_operator_bundle.go index 7fa90b1e0..6690229d5 100644 --- a/certification/internal/policy/operator/validate_operator_bundle.go +++ b/certification/internal/policy/operator/validate_operator_bundle.go @@ -6,7 +6,6 @@ import ( "github.com/redhat-openshift-ecosystem/openshift-preflight/certification" "github.com/redhat-openshift-ecosystem/openshift-preflight/certification/internal/bundle" - "github.com/redhat-openshift-ecosystem/openshift-preflight/certification/internal/operatorsdk" log "github.com/sirupsen/logrus" ) @@ -15,18 +14,14 @@ var _ certification.Check = &ValidateOperatorBundleCheck{} // ValidateOperatorBundleCheck evaluates the image and ensures that it passes bundle validation // as executed by `operator-sdk bundle validate` -type ValidateOperatorBundleCheck struct { - OperatorSdk operatorSdk -} +type ValidateOperatorBundleCheck struct{} -func NewValidateOperatorBundleCheck(operatorSdk operatorSdk) *ValidateOperatorBundleCheck { - return &ValidateOperatorBundleCheck{ - OperatorSdk: operatorSdk, - } +func NewValidateOperatorBundleCheck() *ValidateOperatorBundleCheck { + return &ValidateOperatorBundleCheck{} } func (p *ValidateOperatorBundleCheck) Validate(ctx context.Context, bundleRef certification.ImageReference) (bool, error) { - report, err := p.getDataToValidate(ctx, bundleRef.ImageFSPath) + report, err := p.dataToValidate(ctx, bundleRef.ImageFSPath) if err != nil { return false, fmt.Errorf("error while executing operator-sdk bundle validate: %v", err) } @@ -34,24 +29,19 @@ func (p *ValidateOperatorBundleCheck) Validate(ctx context.Context, bundleRef ce return p.validate(ctx, report) } -func (p *ValidateOperatorBundleCheck) getDataToValidate(ctx context.Context, imagePath string) (*operatorsdk.OperatorSdkBundleValidateReport, error) { - return bundle.Validate(ctx, p.OperatorSdk, imagePath) +func (p *ValidateOperatorBundleCheck) dataToValidate(ctx context.Context, imagePath string) (*bundle.Report, error) { + return bundle.Validate(ctx, imagePath) } -//nolint:unparam // ctx is unused. Keep for future use. -func (p *ValidateOperatorBundleCheck) validate(ctx context.Context, report *operatorsdk.OperatorSdkBundleValidateReport) (bool, error) { - if !report.Passed || len(report.Outputs) > 0 { - for _, output := range report.Outputs { - var logFn func(...interface{}) - switch output.Type { - case "warning": - logFn = log.Warn - case "error": - logFn = log.Error - default: - logFn = log.Debug +func (p *ValidateOperatorBundleCheck) validate(ctx context.Context, report *bundle.Report) (bool, error) { //nolint:unparam // save context for future use + if !report.Passed || len(report.Results) > 0 { + for _, output := range report.Results { + for _, result := range output.Errors { + log.Error(result.Error()) + } + for _, result := range output.Warnings { + log.Warn(result.Error()) } - logFn(output.Message) } } return report.Passed, nil diff --git a/certification/internal/policy/operator/validate_operator_bundle_test.go b/certification/internal/policy/operator/validate_operator_bundle_test.go index 2ccaef860..873434429 100644 --- a/certification/internal/policy/operator/validate_operator_bundle_test.go +++ b/certification/internal/policy/operator/validate_operator_bundle_test.go @@ -2,103 +2,44 @@ package operator import ( "context" - "os" - "path/filepath" "github.com/redhat-openshift-ecosystem/openshift-preflight/certification" - "github.com/redhat-openshift-ecosystem/openshift-preflight/certification/internal/operatorsdk" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("BundleValidateCheck", func() { - const ( - metadataDir = "metadata" - annotationFilename = "annotations.yaml" - annotations = `annotations: - com.redhat.openshift.versions: "v4.6-v4.9" - operators.operatorframework.io.bundle.package.v1: testPackage - operators.operatorframework.io.bundle.channel.default.v1: testChannel -` - ) - - var ( - bundleValidateCheck ValidateOperatorBundleCheck - fakeEngine operatorSdk - imageRef certification.ImageReference - ) + var bundleValidateCheck ValidateOperatorBundleCheck BeforeEach(func() { - stdout := `{ - "passed": true, - "outputs": null -}` - stderr := "" - report := operatorsdk.OperatorSdkBundleValidateReport{ - Stdout: stdout, - Stderr: stderr, - Passed: true, - Outputs: []operatorsdk.OperatorSdkBundleValidateOutput{}, - } - fakeEngine = FakeOperatorSdk{ - OperatorSdkBVReport: report, - } - - // mock bundle directory - tmpDir, err := os.MkdirTemp("", "bundle-metadata-*") - Expect(err).ToNot(HaveOccurred()) - - err = os.Mkdir(filepath.Join(tmpDir, metadataDir), 0o755) - Expect(err).ToNot(HaveOccurred()) - - err = os.WriteFile(filepath.Join(tmpDir, metadataDir, annotationFilename), []byte(annotations), 0o644) - Expect(err).ToNot(HaveOccurred()) - - imageRef.ImageFSPath = tmpDir - - bundleValidateCheck = *NewValidateOperatorBundleCheck(fakeEngine) + bundleValidateCheck = *NewValidateOperatorBundleCheck() }) AssertMetaData(&bundleValidateCheck) + // TODO: Add more tests and bundles to testdata/ that excecise each of the + // validations that we use. Describe("Operator Bundle Validate", func() { Context("When Operator Bundle Validate passes", func() { It("Should pass Validate", func() { + imageRef := certification.ImageReference{ + ImageFSPath: "./testdata/valid_bundle", + } ok, err := bundleValidateCheck.Validate(context.TODO(), imageRef) Expect(err).ToNot(HaveOccurred()) Expect(ok).To(BeTrue()) }) }) Context("When Operator Bundle Validate does not Pass", func() { - BeforeEach(func() { - engine := fakeEngine.(FakeOperatorSdk) - engine.OperatorSdkBVReport.Passed = false - engine.OperatorSdkBVReport.Outputs = []operatorsdk.OperatorSdkBundleValidateOutput{ - {Type: "warning", Message: "This is a warning"}, - {Type: "error", Message: "This is an error"}, - } - fakeEngine = engine - bundleValidateCheck = *NewValidateOperatorBundleCheck(fakeEngine) - }) It("Should not pass Validate", func() { + imageRef := certification.ImageReference{ + ImageFSPath: "./testdata/invalid_bundle", + } ok, err := bundleValidateCheck.Validate(context.TODO(), imageRef) Expect(err).ToNot(HaveOccurred()) Expect(ok).To(BeFalse()) }) }) }) - Describe("Checking that OperatorSdk errors are handled correctly", func() { - BeforeEach(func() { - fakeEngine = BadOperatorSdk{} - bundleValidateCheck = *NewValidateOperatorBundleCheck(fakeEngine) - }) - Context("When OperatorSdk throws an error", func() { - It("should fail Validate and return an error", func() { - ok, err := bundleValidateCheck.Validate(context.TODO(), certification.ImageReference{ImageURI: "dummy/image"}) - Expect(err).To(HaveOccurred()) - Expect(ok).To(BeFalse()) - }) - }) - }) }) diff --git a/go.mod b/go.mod index 174d0c5a3..6e66e2127 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/openshift/client-go v0.0.0-20210521082421-73d9475a9142 github.com/operator-framework/api v0.16.0 github.com/operator-framework/operator-manifest-tools v0.2.1 + github.com/redhat-openshift-ecosystem/ocp-olm-catalog-validator v0.1.0 github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29 github.com/sirupsen/logrus v1.8.1 github.com/spf13/cobra v1.5.0 @@ -28,6 +29,8 @@ require ( require ( github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e // indirect + github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect @@ -38,6 +41,7 @@ require ( github.com/docker/docker-credential-helpers v0.6.4 // indirect github.com/emicklei/go-restful v2.9.5+incompatible // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect + github.com/felixge/httpsnoop v1.0.1 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/go-logr/logr v1.2.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect @@ -46,10 +50,12 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect + github.com/google/cel-go v0.10.1 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/go-cmp v0.5.8 // indirect github.com/google/gofuzz v1.1.0 // indirect github.com/google/uuid v1.3.0 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect @@ -70,6 +76,7 @@ require ( github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.1 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.12.1 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect @@ -77,8 +84,20 @@ require ( github.com/spf13/afero v1.8.2 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/stoewer/go-strcase v1.2.0 // indirect + github.com/stretchr/testify v1.7.1 // indirect github.com/subosito/gotenv v1.3.0 // indirect github.com/vbatts/tar-split v0.11.2 // indirect + go.opentelemetry.io/contrib v0.20.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0 // indirect + go.opentelemetry.io/otel v0.20.0 // indirect + go.opentelemetry.io/otel/exporters/otlp v0.20.0 // indirect + go.opentelemetry.io/otel/metric v0.20.0 // indirect + go.opentelemetry.io/otel/sdk v0.20.0 // indirect + go.opentelemetry.io/otel/sdk/export/metric v0.20.0 // indirect + go.opentelemetry.io/otel/sdk/metric v0.20.0 // indirect + go.opentelemetry.io/otel/trace v0.20.0 // indirect + go.opentelemetry.io/proto/otlp v0.7.0 // indirect golang.org/x/net v0.0.0-20220524220425-1d687d428aca // indirect golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401 // indirect golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect @@ -89,17 +108,21 @@ require ( golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect + google.golang.org/grpc v1.46.2 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.66.4 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.24.0 // indirect + k8s.io/apiserver v0.24.0 // indirect k8s.io/client-go v0.24.0 // indirect k8s.io/component-base v0.24.0 // indirect k8s.io/klog/v2 v2.60.1 // indirect k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30 // indirect sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect ) diff --git a/go.sum b/go.sum index c6deaf255..86a114250 100644 --- a/go.sum +++ b/go.sum @@ -93,6 +93,7 @@ github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e h1:GCzyKMDDjSGnlpl3clrdAK7I1AaVoaiKDOYkUzChZzg= github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -101,6 +102,7 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/ashanbrown/forbidigo v1.2.0/go.mod h1:vVW7PEdqEFqapJe95xHkTfB1+XvZXBFg8t0sG2FIxmI= github.com/ashanbrown/makezero v0.0.0-20210520155254-b6261585ddde/go.mod h1:oG9Dnez7/ESBqc4EdrdNlryeo7d0KcW1ftXHm7nU/UU= @@ -108,6 +110,7 @@ github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -141,7 +144,11 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= @@ -200,6 +207,7 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/esimonov/ifshort v1.0.3/go.mod h1:yZqNJUrNn20K8Q9n2CrjTKYyVEmX209Hgu+M1LBpeZE= @@ -213,6 +221,7 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= +github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -226,6 +235,7 @@ github.com/fullstorydev/grpcurl v1.6.0/go.mod h1:ZQ+ayqbKMJNhzLmbpCiurTVlaK2M/3n github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-critic/go-critic v0.6.1/go.mod h1:SdNCfU0yF3UBjtaZGw6586/WocupMOJuiqgom5DsQxM= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -337,6 +347,7 @@ github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/cel-go v0.10.1 h1:MQBGSZGnDwh7T/un+mzGKOMz3x+4E/GDPprWjDL+1Jg= github.com/google/cel-go v0.10.1/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w= github.com/google/cel-spec v0.6.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA= github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= @@ -426,6 +437,7 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= @@ -735,6 +747,8 @@ github.com/quasilyte/go-ruleguard/dsl v0.3.10/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQ github.com/quasilyte/go-ruleguard/rules v0.0.0-20201231183845-9e62ed36efe1/go.mod h1:7JTjp89EGyU1d6XfBiXihJNG37wB2VRkd125Q1u7Plc= github.com/quasilyte/go-ruleguard/rules v0.0.0-20210428214800-545e0d2e0bf7/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50= github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= +github.com/redhat-openshift-ecosystem/ocp-olm-catalog-validator v0.1.0 h1:4EHstmf5aq2aalaQwb4Fila8GJeDd+O3A6ASC70fQio= +github.com/redhat-openshift-ecosystem/ocp-olm-catalog-validator v0.1.0/go.mod h1:GfYbSrt58GvB8zQlTIaSAMx7d9uAJGnXLDggRqw7C3s= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -806,6 +820,7 @@ github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhU github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= +github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -893,17 +908,28 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/contrib v0.20.0 h1:ubFQUn0VCZ0gPwIoJfBJVpeBlyRMxu8Mm/huKWYd9p0= go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0 h1:Q3C9yzW6I9jqEc8sawxzxZmY48fs9u220KXq6d5s3XU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= +go.opentelemetry.io/otel v0.20.0 h1:eaP0Fqu7SXHwvjiqDq83zImeehOHX8doTvU9AwXON8g= go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= +go.opentelemetry.io/otel/exporters/otlp v0.20.0 h1:PTNgq9MRmQqqJY0REVbZFvwkYOA85vbdQU/nVfxDyqg= go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= +go.opentelemetry.io/otel/metric v0.20.0 h1:4kzhXFP+btKm4jwxpjIqjs41A7MakRFUS86bqLHTIw8= go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= +go.opentelemetry.io/otel/oteltest v0.20.0 h1:HiITxCawalo5vQzdHfKeZurV8x7ljcqAgiWzF6Vaeaw= go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= +go.opentelemetry.io/otel/sdk v0.20.0 h1:JsxtGXd06J8jrnya7fdI/U/MR6yXA5DtbZy+qoHQlr8= go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= +go.opentelemetry.io/otel/sdk/export/metric v0.20.0 h1:c5VRjxCXdQlx1HjzwGdQHzZaVI82b5EbBgOu2ljD92g= go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= +go.opentelemetry.io/otel/sdk/metric v0.20.0 h1:7ao1wpzHRVKf0OQ7GIxiQJA6X7DLX9o14gmVon7mMK8= go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= +go.opentelemetry.io/otel/trace v0.20.0 h1:1DL6EXUdcg95gukhuRRvLDO/4X5THh/5dIV52lqtnbw= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= +go.opentelemetry.io/proto/otlp v0.7.0 h1:rwOQPCuKAKmwGKq2aVNnYIibI6wnV7EvzgfTCzcdGg8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -1409,6 +1435,8 @@ google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEc google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd h1:e0TwkXOdbnH/1x5rc5MZ/VYyiZ4v+RdVfrGMqEwT68I= +google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1438,6 +1466,9 @@ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2 h1:u+MLGgVf7vRdjEYZ8wDFhAVNmhkbJ5hmrA1LMWK1CAQ= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1513,6 +1544,7 @@ k8s.io/apiextensions-apiserver v0.24.0/go.mod h1:iuVe4aEpe6827lvO6yWQVxiPSpPoSKV k8s.io/apimachinery v0.21.1/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY= k8s.io/apimachinery v0.24.0 h1:ydFCyC/DjCvFCHK5OPMKBlxayQytB8pxy8YQInd5UyQ= k8s.io/apimachinery v0.24.0/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/apiserver v0.24.0 h1:GR7kGsjOMfilRvlG3Stxv/3uz/ryvJ/aZXc5pqdsNV0= k8s.io/apiserver v0.24.0/go.mod h1:WFx2yiOMawnogNToVvUYT9nn1jaIkMKj41ZYCVycsBA= k8s.io/client-go v0.21.1/go.mod h1:/kEw4RgW+3xnBGzvp9IWxKSNA+lXn3A7AuH3gdOAzLs= k8s.io/client-go v0.24.0 h1:lbE4aB1gTHvYFSwm6eD3OF14NhFDKCejlnsGYlSJe5U= @@ -1544,6 +1576,7 @@ mvdan.cc/unparam v0.0.0-20210104141923-aac4ce9116a7/go.mod h1:hBpJkZE8H/sb+VRFvw rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30 h1:dUk62HQ3ZFhD48Qr8MIXCiKA8wInBQCtuE4QGfFW7yA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30/go.mod h1:fEO7lRTdivWO2qYVCVG7dEADOMo/MLDCVr8So2g88Uw= sigs.k8s.io/controller-runtime v0.12.1 h1:4BJY01xe9zKQti8oRjj/NeHKRXthf1YkYJAgLONFFoI= sigs.k8s.io/controller-runtime v0.12.1/go.mod h1:BKhxlA4l7FPK4AQcsuL4X6vZeWnKDXez/vp1Y8dxTU0=