From ce5ce6939c4fc452930d0cd292f712db4983d734 Mon Sep 17 00:00:00 2001 From: Sunny Date: Tue, 23 Apr 2019 13:48:03 +0530 Subject: [PATCH 1/3] validate: validate csv spec.crd.owned & descriptors spec.customresourcedefinitions.owned CRDs must have displayName and description and values must not be empty. Validate the attributes of csv owned crd descriptors. --- operatorcourier/validate.py | 201 ++++++++---- .../csvemptyattrspecdesc.invalid.bundle.yaml | 306 ++++++++++++++++++ .../csvemptycrdownedattr.invalid.bundle.yaml | 302 +++++++++++++++++ ...csvmissingattrspecdesc.invalid.bundle.yaml | 299 +++++++++++++++++ ...csvmissingcrdownedattr.invalid.bundle.yaml | 300 +++++++++++++++++ tests/test_validate.py | 28 ++ 6 files changed, 1372 insertions(+), 64 deletions(-) create mode 100644 tests/test_files/bundles/verification/csvemptyattrspecdesc.invalid.bundle.yaml create mode 100644 tests/test_files/bundles/verification/csvemptycrdownedattr.invalid.bundle.yaml create mode 100644 tests/test_files/bundles/verification/csvmissingattrspecdesc.invalid.bundle.yaml create mode 100644 tests/test_files/bundles/verification/csvmissingcrdownedattr.invalid.bundle.yaml diff --git a/operatorcourier/validate.py b/operatorcourier/validate.py index 43e890b..7047fa0 100644 --- a/operatorcourier/validate.py +++ b/operatorcourier/validate.py @@ -186,79 +186,127 @@ def _csv_spec_validation(self, spec, bundleData): valid = False if "customresourcedefinitions" in spec: - customresourcedefinitions = spec["customresourcedefinitions"] + if self._csv_crd_validation( + spec["customresourcedefinitions"], bundleData) is False: + valid = False - crdList = [] - for crd in bundleData[self.crdKey]: - try: - name = crd["metadata"]["name"] - crdList.append(name) - except KeyError: - pass - - if "owned" not in customresourcedefinitions: - self._log_error("spec.customresourcedefinitions.owned" - "not defined for csv") - return False + return valid - for csvOwnedCrd in customresourcedefinitions["owned"]: - if "name" not in csvOwnedCrd: - self._log_error("name not defined for item in " - "spec.customresourcedefinitions.") - valid = False - elif csvOwnedCrd["name"] not in crdList: - self._log_error("custom resource definition %s referenced in csv " - "not defined in root list of crds", - csvOwnedCrd["name"]) + def _csv_crd_validation(self, customresourcedefinitions, bundleData): + valid = True + + crdList = [] + for crd in bundleData[self.crdKey]: + try: + name = crd["metadata"]["name"] + crdList.append(name) + except KeyError: + pass + + if "owned" not in customresourcedefinitions: + self._log_error("spec.customresourcedefinitions.owned" + "not defined for csv") + return False + + for csvOwnedCrd in customresourcedefinitions["owned"]: + if "name" not in csvOwnedCrd: + self._log_error("name not defined for item in " + "spec.customresourcedefinitions.") + valid = False + elif csvOwnedCrd["name"] not in crdList: + self._log_error("custom resource definition %s referenced in csv " + "not defined in root list of crds", + csvOwnedCrd["name"]) + valid = False + + if "kind" not in csvOwnedCrd: + self._log_error("kind not defined for item in " + "spec.customresourcedefinitions.") + valid = False + if "version" not in csvOwnedCrd: + self._log_error("version not defined for item in " + "spec.customresourcedefinitions.") + valid = False + + # Values of name, version and kind above are compared with their + # values in the associated CRD files later. Empty string check + # is not needed. + # displayName and description should be checked for empty + # strings. + if "displayName" not in csvOwnedCrd: + self._log_error("displayName not defined for item in " + "spec.customresourcedefinitions.") + valid = False + elif not csvOwnedCrd["displayName"]: + self._log_error("displayName is empty for item in " + "spec.customresourcedefinitions.") + valid = False + if "description" not in csvOwnedCrd: + self._log_error("description not defined for item in " + "spec.customresourcedefinitions.") + valid = False + elif not csvOwnedCrd["description"]: + self._log_error("description is empty for item in " + "spec.customresourcedefinitions.") + valid = False + + if "specDescriptors" in csvOwnedCrd and "name" in csvOwnedCrd: + if self._csv_descriptors_validation( + csvOwnedCrd["specDescriptors"], + csvOwnedCrd["name"]) is False: valid = False - if "kind" not in csvOwnedCrd: - self._log_error("kind not defined for item in " - "spec.customresourcedefinitions.") + if "statusDescriptors" in csvOwnedCrd and "name" in csvOwnedCrd: + if self._csv_descriptors_validation( + csvOwnedCrd["statusDescriptors"], + csvOwnedCrd["name"]) is False: valid = False - if "version" not in csvOwnedCrd: - self._log_error("version not defined for item in " - "spec.customresourcedefinitions.") + + if "actionDescriptors" in csvOwnedCrd and "name" in csvOwnedCrd: + if self._csv_descriptors_validation( + csvOwnedCrd["actionDescriptors"], + csvOwnedCrd["name"]) is False: valid = False - for crd in bundleData[self.crdKey]: - if 'name' not in csvOwnedCrd: - continue - if 'metadata' not in crd or 'name' not in crd['metadata']: - continue - if csvOwnedCrd['name'] != crd['metadata']['name']: - continue - - if 'kind' in csvOwnedCrd: - if 'spec' in crd: - if 'names' in crd['spec']: - if 'kind' in crd['spec']['names']: - if csvOwnedCrd['kind'] != \ - crd['spec']['names']['kind']: - self._log_error('CRD.spec.names.kind does not ' - 'match CSV.spec.crd.owned.kind') - valid = False - - if 'version' in csvOwnedCrd: - if 'spec' in crd: - if 'version' in crd['spec']: - if csvOwnedCrd['version'] != crd['spec']['version']: - self._log_error('CRD.spec.version does not match ' - 'CSV.spec.crd.owned.version') + for crd in bundleData[self.crdKey]: + if 'name' not in csvOwnedCrd: + continue + if 'metadata' not in crd or 'name' not in crd['metadata']: + continue + if csvOwnedCrd['name'] != crd['metadata']['name']: + continue + + if 'kind' in csvOwnedCrd: + if 'spec' in crd: + if 'names' in crd['spec']: + if 'kind' in crd['spec']['names']: + if csvOwnedCrd['kind'] != \ + crd['spec']['names']['kind']: + self._log_error('CRD.spec.names.kind does not ' + 'match CSV.spec.crd.owned.kind') + valid = False + + if 'version' in csvOwnedCrd: + if 'spec' in crd: + if 'version' in crd['spec']: + if csvOwnedCrd['version'] != crd['spec']['version']: + self._log_error('CRD.spec.version does not match ' + 'CSV.spec.crd.owned.version') + valid = False + + if 'name' in csvOwnedCrd: + if 'spec' in crd: + if 'names' in crd['spec'] and 'group' in crd['spec']: + if 'plural' in crd['spec']['names']: + if csvOwnedCrd['name'] != \ + crd['spec']['names']['plural'] + '.' + \ + crd['spec']['group']: + self._log_error("`CRD.spec.names.plural`." + "`CRD.spec.group` does not " + "match " + "CSV.spec.crd.owned.name") valid = False - if 'name' in csvOwnedCrd: - if 'spec' in crd: - if 'names' in crd['spec'] and 'group' in crd['spec']: - if 'plural' in crd['spec']['names']: - if csvOwnedCrd['name'] != \ - crd['spec']['names']['plural'] + '.' + \ - crd['spec']['group']: - self._log_error("`CRD.spec.names.plural`." - "`CRD.spec.group` does not " - "match " - "CSV.spec.crd.owned.name") - valid = False return valid def _csv_spec_install_validation(self, install): @@ -314,6 +362,31 @@ def _csv_spec_install_validation(self, install): return valid + def _csv_descriptors_validation(self, descriptors, crdName): + valid = True + + # required attributes of descriptors + attributeList = ["displayName", "description", "path"] + + # validate a descriptor for a given attribute + def validate_descriptor(descriptor, resourceName, attribute): + if attribute not in descriptor: + self._log_error("%s is not defined for descriptors in %s" + % (attribute, resourceName)) + return False + elif not descriptor[attribute]: + self._log_error("%s is empty for descriptors in %s" + % (attribute, resourceName)) + return False + return True + + for desc in descriptors: + for attr in attributeList: + if validate_descriptor(desc, crdName, attr) is False: + valid = False + + return valid + def _csv_metadata_validation(self, metadata): valid = True diff --git a/tests/test_files/bundles/verification/csvemptyattrspecdesc.invalid.bundle.yaml b/tests/test_files/bundles/verification/csvemptyattrspecdesc.invalid.bundle.yaml new file mode 100644 index 0000000..acb46e0 --- /dev/null +++ b/tests/test_files/bundles/verification/csvemptyattrspecdesc.invalid.bundle.yaml @@ -0,0 +1,306 @@ +# CSV.spec.customresourcedefinitions.owned.actionDescriptors.{description,displayName} is empty in this file +data: + clusterServiceVersions: | + - apiVersion: operators.coreos.com/v1alpha1 + kind: ClusterServiceVersion + metadata: + annotations: + alm-examples: '[{"apiVersion":"etcd.database.coreos.com/v1beta2","kind":"EtcdCluster","metadata":{"name":"example","namespace":"default"},"spec":{"size":3,"version":"3.2.13"}},{"apiVersion":"etcd.database.coreos.com/v1beta2","kind":"EtcdRestore","metadata":{"name":"example-etcd-cluster"},"spec":{"etcdCluster":{"name":"example-etcd-cluster"},"backupStorageType":"S3","s3":{"path":"","awsSecret":""}}},{"apiVersion":"etcd.database.coreos.com/v1beta2","kind":"EtcdBackup","metadata":{"name":"example-etcd-cluster-backup"},"spec":{"etcdEndpoints":[""],"storageType":"S3","s3":{"path":"","awsSecret":""}}}]' + categories: openshift required + certified: 'true' + containerImage: quay.io/openshift/origin-operator-marketplace:latest + createdAt: 2019/11/15 + description: An operator to run the OpenShift marketplace + healthIndex: B + repository: https://github.com/operator-framework/operator-marketplace + support: Red Hat + name: marketplace-operator.v0.0.1 + namespace: placeholder + spec: + customresourcedefinitions: + owned: + - description: Represents an OperatorSource. + displayName: Operator Source + kind: OperatorSource + name: operatorsources.marketplace.redhat.com + specDescriptors: + - description: The type of the operator source. + displayName: Type + path: type + - description: Points to the remote app registry server from where operator + manifests can be fetched. + displayName: Endpoint + path: endpoint + - description: 'The namespace in app registry. + + Only operator manifests under this namespace will be visible. + + Please note that this is not a k8s namespace.' + displayName: Registry Namespace + path: registryNamespace + statusDescriptors: + - description: Current status of the CatalogSourceConfig + displayName: Current Phase Name + path: currentPhase.phase.name + - description: Message associated with the current status + displayName: Current Phase Message + path: currentPhase.phase.message + actionDescriptors: + - description: + displayName: + path: + version: v1alpha1 + - description: Represents a CatalogSourceConfig object which is used to configure + a CatalogSource. + displayName: Catalog Source Config + kind: CatalogSourceConfig + name: catalogsourceconfigs.marketplace.redhat.com + specDescriptors: + - description: The namespace where the operators will be enabled. + displayName: Target Namespace + path: targetNamespace + - description: List of operator(s) which will be enabled in the target namespace + displayName: Packages + path: packages + statusDescriptors: + - description: Current status of the CatalogSourceConfig + displayName: Current Phase Name + path: currentPhase.phase.name + - description: Message associated with the current status + displayName: Current Phase Message + path: currentPhase.phase.message + version: v1alpha1 + description: Marketplace is a gateway for users to consume off-cluster Operators + which will include Red Hat, ISV, optional OpenShift and community content. + displayName: marketplace-operator + install: + spec: + clusterPermissions: + - rules: + - apiGroups: + - marketplace.redhat.com + resources: + - '*' + verbs: + - '*' + - apiGroups: + - '' + resources: + - services + - configmaps + verbs: + - '*' + - apiGroups: + - operators.coreos.com + resources: + - catalogsources + verbs: + - '*' + serviceAccountName: marketplace-operator + deployments: + - name: marketplace-operator + spec: + replicas: 1 + selector: + matchLabels: + name: marketplace-operator + template: + metadata: + labels: + name: marketplace-operator + name: marketplace-operator + spec: + containers: + - command: + - marketplace-operator + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: OPERATOR_NAME + value: marketplace-operator + image: quay.io/openshift/origin-operator-marketplace:latest + imagePullPolicy: Always + livenessProbe: + httpGet: + path: /healthz + port: 8080 + name: marketplace-operator + ports: + - containerPort: 60000 + name: metrics + - containerPort: 8080 + name: healthz + readinessProbe: + httpGet: + path: /healthz + port: 8080 + serviceAccountName: marketplace-operator + strategy: deployment + installModes: + - supported: true + type: OwnNamespace + - supported: true + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces + keywords: + - marketplace + - catalog + - olm + - admin + labels: + name: marketplace-operator + links: + - name: Markplace Operator Source Code + url: https://github.com/operator-framework/operator-marketplace + maintainers: + - email: aos-marketplace@redhat.com + name: AOS Marketplace Team + maturity: alpha + provider: + name: Red Hat + selector: + matchLabels: + name: marketplace-operator + version: 0.0.1 + customResourceDefinitions: | + - apiVersion: apiextensions.k8s.io/v1beta1 + kind: CustomResourceDefinition + metadata: + annotations: + description: Represents a CatalogSourceConfig. + displayName: Catalog Source Config + name: catalogsourceconfigs.marketplace.redhat.com + spec: + additionalPrinterColumns: + - JSONPath: .spec.targetNamespace + description: The namespace where the operators will be enabled + name: TargetNamespace + type: string + - JSONPath: .spec.packages + description: List of operator(s) which will be enabled in the target namespace + name: Packages + type: string + - JSONPath: .status.currentPhase.phase.name + description: Current status of the CatalogSourceConfig + name: Status + type: string + - JSONPath: .status.currentPhase.phase.message + description: Message associated with the current status + name: Message + type: string + - JSONPath: .metadata.creationTimestamp + name: Age + type: date + group: marketplace.redhat.com + names: + kind: CatalogSourceConfig + listKind: CatalogSourceConfigList + plural: catalogsourceconfigs + shortNames: + - csc + singular: catalogsourceconfig + scope: Namespaced + validation: + openAPIV3Schema: + properties: + spec: + description: Spec for a CatalogSourceConfig + properties: + packages: + description: Comma separated list of operator(s) without spaces + which will be enabled in the target namespace + type: string + targetNamespace: + description: The namespace where the operators will be enabled + type: string + required: + - targetNamespace + - packages + type: object + version: v1alpha1 + - apiVersion: apiextensions.k8s.io/v1beta1 + kind: CustomResourceDefinition + metadata: + annotations: + description: Represents an OperatorSource. + displayName: Operator Source + name: operatorsources.marketplace.redhat.com + spec: + additionalPrinterColumns: + - JSONPath: .spec.type + description: The type of the OperatorSource + name: Type + type: string + - JSONPath: .spec.endpoint + description: The endpoint of the OperatorSource + name: Endpoint + type: string + - JSONPath: .spec.registryNamespace + description: App registry namespace + name: Registry + type: string + - JSONPath: .spec.displayName + description: Display (pretty) name to indicate the OperatorSource's name + name: DisplayName + type: string + - JSONPath: .spec.publisher + description: Publisher of the OperatorSource + name: Publisher + type: string + - JSONPath: .status.currentPhase.phase.name + description: Current status of the OperatorSource + name: Status + type: string + - JSONPath: .status.currentPhase.phase.message + description: Message associated with the current status + name: Message + type: string + - JSONPath: .metadata.creationTimestamp + name: Age + type: date + group: marketplace.redhat.com + names: + kind: OperatorSource + listKind: OperatorSourceList + plural: operatorsources + shortNames: + - opsrc + singular: operatorsource + scope: Namespaced + validation: + openAPIV3Schema: + properties: + spec: + description: Spec for an OperatorSource. + properties: + endpoint: + description: Points to the remote app registry server from where + operator manifests can be fetched. + type: string + registryNamespace: + description: 'The namespace in app registry. + + Only operator manifests under this namespace will be visible. + + Please note that this is not a k8s namespace.' + type: string + type: + description: The type of the OperatorSource + pattern: appregistry + type: string + required: + - type + - endpoint + - registryNamespace + type: object + version: v1alpha1 + packages: | + - channels: + - currentCSV: marketplace-operator.v0.0.1 + name: alpha + packageName: marketplace diff --git a/tests/test_files/bundles/verification/csvemptycrdownedattr.invalid.bundle.yaml b/tests/test_files/bundles/verification/csvemptycrdownedattr.invalid.bundle.yaml new file mode 100644 index 0000000..3f971f6 --- /dev/null +++ b/tests/test_files/bundles/verification/csvemptycrdownedattr.invalid.bundle.yaml @@ -0,0 +1,302 @@ +# CSV.spec.customresourcedefinitions.owned.{description,displayName} are empty in this file +data: + clusterServiceVersions: | + - apiVersion: operators.coreos.com/v1alpha1 + kind: ClusterServiceVersion + metadata: + annotations: + alm-examples: '[{"apiVersion":"etcd.database.coreos.com/v1beta2","kind":"EtcdCluster","metadata":{"name":"example","namespace":"default"},"spec":{"size":3,"version":"3.2.13"}},{"apiVersion":"etcd.database.coreos.com/v1beta2","kind":"EtcdRestore","metadata":{"name":"example-etcd-cluster"},"spec":{"etcdCluster":{"name":"example-etcd-cluster"},"backupStorageType":"S3","s3":{"path":"","awsSecret":""}}},{"apiVersion":"etcd.database.coreos.com/v1beta2","kind":"EtcdBackup","metadata":{"name":"example-etcd-cluster-backup"},"spec":{"etcdEndpoints":[""],"storageType":"S3","s3":{"path":"","awsSecret":""}}}]' + categories: openshift required + certified: 'true' + containerImage: quay.io/openshift/origin-operator-marketplace:latest + createdAt: 2019/11/15 + description: An operator to run the OpenShift marketplace + healthIndex: B + repository: https://github.com/operator-framework/operator-marketplace + support: Red Hat + name: marketplace-operator.v0.0.1 + namespace: placeholder + spec: + customresourcedefinitions: + owned: + - kind: OperatorSource + displayName: + description: + name: operatorsources.marketplace.redhat.com + specDescriptors: + - description: The type of the operator source. + displayName: Type + path: type + - description: Points to the remote app registry server from where operator + manifests can be fetched. + displayName: Endpoint + path: endpoint + - description: 'The namespace in app registry. + + Only operator manifests under this namespace will be visible. + + Please note that this is not a k8s namespace.' + displayName: Registry Namespace + path: registryNamespace + statusDescriptors: + - description: Current status of the CatalogSourceConfig + displayName: Current Phase Name + path: currentPhase.phase.name + - description: Message associated with the current status + displayName: Current Phase Message + path: currentPhase.phase.message + version: v1alpha1 + - description: Represents a CatalogSourceConfig object which is used to configure + a CatalogSource. + displayName: Catalog Source Config + kind: CatalogSourceConfig + name: catalogsourceconfigs.marketplace.redhat.com + specDescriptors: + - description: The namespace where the operators will be enabled. + displayName: Target Namespace + path: targetNamespace + - description: List of operator(s) which will be enabled in the target namespace + displayName: Packages + path: packages + statusDescriptors: + - description: Current status of the CatalogSourceConfig + displayName: Current Phase Name + path: currentPhase.phase.name + - description: Message associated with the current status + displayName: Current Phase Message + path: currentPhase.phase.message + version: v1alpha1 + description: Marketplace is a gateway for users to consume off-cluster Operators + which will include Red Hat, ISV, optional OpenShift and community content. + displayName: marketplace-operator + install: + spec: + clusterPermissions: + - rules: + - apiGroups: + - marketplace.redhat.com + resources: + - '*' + verbs: + - '*' + - apiGroups: + - '' + resources: + - services + - configmaps + verbs: + - '*' + - apiGroups: + - operators.coreos.com + resources: + - catalogsources + verbs: + - '*' + serviceAccountName: marketplace-operator + deployments: + - name: marketplace-operator + spec: + replicas: 1 + selector: + matchLabels: + name: marketplace-operator + template: + metadata: + labels: + name: marketplace-operator + name: marketplace-operator + spec: + containers: + - command: + - marketplace-operator + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: OPERATOR_NAME + value: marketplace-operator + image: quay.io/openshift/origin-operator-marketplace:latest + imagePullPolicy: Always + livenessProbe: + httpGet: + path: /healthz + port: 8080 + name: marketplace-operator + ports: + - containerPort: 60000 + name: metrics + - containerPort: 8080 + name: healthz + readinessProbe: + httpGet: + path: /healthz + port: 8080 + serviceAccountName: marketplace-operator + strategy: deployment + installModes: + - supported: true + type: OwnNamespace + - supported: true + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces + keywords: + - marketplace + - catalog + - olm + - admin + labels: + name: marketplace-operator + links: + - name: Markplace Operator Source Code + url: https://github.com/operator-framework/operator-marketplace + maintainers: + - email: aos-marketplace@redhat.com + name: AOS Marketplace Team + maturity: alpha + provider: + name: Red Hat + selector: + matchLabels: + name: marketplace-operator + version: 0.0.1 + customResourceDefinitions: | + - apiVersion: apiextensions.k8s.io/v1beta1 + kind: CustomResourceDefinition + metadata: + annotations: + description: Represents a CatalogSourceConfig. + displayName: Catalog Source Config + name: catalogsourceconfigs.marketplace.redhat.com + spec: + additionalPrinterColumns: + - JSONPath: .spec.targetNamespace + description: The namespace where the operators will be enabled + name: TargetNamespace + type: string + - JSONPath: .spec.packages + description: List of operator(s) which will be enabled in the target namespace + name: Packages + type: string + - JSONPath: .status.currentPhase.phase.name + description: Current status of the CatalogSourceConfig + name: Status + type: string + - JSONPath: .status.currentPhase.phase.message + description: Message associated with the current status + name: Message + type: string + - JSONPath: .metadata.creationTimestamp + name: Age + type: date + group: marketplace.redhat.com + names: + kind: CatalogSourceConfig + listKind: CatalogSourceConfigList + plural: catalogsourceconfigs + shortNames: + - csc + singular: catalogsourceconfig + scope: Namespaced + validation: + openAPIV3Schema: + properties: + spec: + description: Spec for a CatalogSourceConfig + properties: + packages: + description: Comma separated list of operator(s) without spaces + which will be enabled in the target namespace + type: string + targetNamespace: + description: The namespace where the operators will be enabled + type: string + required: + - targetNamespace + - packages + type: object + version: v1alpha1 + - apiVersion: apiextensions.k8s.io/v1beta1 + kind: CustomResourceDefinition + metadata: + annotations: + description: Represents an OperatorSource. + displayName: Operator Source + name: operatorsources.marketplace.redhat.com + spec: + additionalPrinterColumns: + - JSONPath: .spec.type + description: The type of the OperatorSource + name: Type + type: string + - JSONPath: .spec.endpoint + description: The endpoint of the OperatorSource + name: Endpoint + type: string + - JSONPath: .spec.registryNamespace + description: App registry namespace + name: Registry + type: string + - JSONPath: .spec.displayName + description: Display (pretty) name to indicate the OperatorSource's name + name: DisplayName + type: string + - JSONPath: .spec.publisher + description: Publisher of the OperatorSource + name: Publisher + type: string + - JSONPath: .status.currentPhase.phase.name + description: Current status of the OperatorSource + name: Status + type: string + - JSONPath: .status.currentPhase.phase.message + description: Message associated with the current status + name: Message + type: string + - JSONPath: .metadata.creationTimestamp + name: Age + type: date + group: marketplace.redhat.com + names: + kind: OperatorSource + listKind: OperatorSourceList + plural: operatorsources + shortNames: + - opsrc + singular: operatorsource + scope: Namespaced + validation: + openAPIV3Schema: + properties: + spec: + description: Spec for an OperatorSource. + properties: + endpoint: + description: Points to the remote app registry server from where + operator manifests can be fetched. + type: string + registryNamespace: + description: 'The namespace in app registry. + + Only operator manifests under this namespace will be visible. + + Please note that this is not a k8s namespace.' + type: string + type: + description: The type of the OperatorSource + pattern: appregistry + type: string + required: + - type + - endpoint + - registryNamespace + type: object + version: v1alpha1 + packages: | + - channels: + - currentCSV: marketplace-operator.v0.0.1 + name: alpha + packageName: marketplace diff --git a/tests/test_files/bundles/verification/csvmissingattrspecdesc.invalid.bundle.yaml b/tests/test_files/bundles/verification/csvmissingattrspecdesc.invalid.bundle.yaml new file mode 100644 index 0000000..5d0a9b8 --- /dev/null +++ b/tests/test_files/bundles/verification/csvmissingattrspecdesc.invalid.bundle.yaml @@ -0,0 +1,299 @@ +# CSV.spec.customresourcedefinitions.owned.specDescriptors.{description,displayName,path} is missing in this file +data: + clusterServiceVersions: | + - apiVersion: operators.coreos.com/v1alpha1 + kind: ClusterServiceVersion + metadata: + annotations: + alm-examples: '[{"apiVersion":"etcd.database.coreos.com/v1beta2","kind":"EtcdCluster","metadata":{"name":"example","namespace":"default"},"spec":{"size":3,"version":"3.2.13"}},{"apiVersion":"etcd.database.coreos.com/v1beta2","kind":"EtcdRestore","metadata":{"name":"example-etcd-cluster"},"spec":{"etcdCluster":{"name":"example-etcd-cluster"},"backupStorageType":"S3","s3":{"path":"","awsSecret":""}}},{"apiVersion":"etcd.database.coreos.com/v1beta2","kind":"EtcdBackup","metadata":{"name":"example-etcd-cluster-backup"},"spec":{"etcdEndpoints":[""],"storageType":"S3","s3":{"path":"","awsSecret":""}}}]' + categories: openshift required + certified: 'true' + containerImage: quay.io/openshift/origin-operator-marketplace:latest + createdAt: 2019/11/15 + description: An operator to run the OpenShift marketplace + healthIndex: B + repository: https://github.com/operator-framework/operator-marketplace + support: Red Hat + name: marketplace-operator.v0.0.1 + namespace: placeholder + spec: + customresourcedefinitions: + owned: + - description: Represents an OperatorSource. + displayName: Operator Source + kind: OperatorSource + name: operatorsources.marketplace.redhat.com + specDescriptors: + - description: The type of the operator source. + manifests can be fetched. + - displayName: Endpoint + path: endpoint + - description: 'The namespace in app registry. + + Only operator manifests under this namespace will be visible. + + Please note that this is not a k8s namespace.' + displayName: Registry Namespace + path: registryNamespace + statusDescriptors: + - description: Current status of the CatalogSourceConfig + displayName: Current Phase Name + path: currentPhase.phase.name + - description: Message associated with the current status + displayName: Current Phase Message + path: currentPhase.phase.message + version: v1alpha1 + - description: Represents a CatalogSourceConfig object which is used to configure + a CatalogSource. + displayName: Catalog Source Config + kind: CatalogSourceConfig + name: catalogsourceconfigs.marketplace.redhat.com + specDescriptors: + - description: The namespace where the operators will be enabled. + displayName: Target Namespace + path: targetNamespace + - description: List of operator(s) which will be enabled in the target namespace + displayName: Packages + path: packages + statusDescriptors: + - description: Current status of the CatalogSourceConfig + displayName: Current Phase Name + path: currentPhase.phase.name + - description: Message associated with the current status + displayName: Current Phase Message + path: currentPhase.phase.message + version: v1alpha1 + description: Marketplace is a gateway for users to consume off-cluster Operators + which will include Red Hat, ISV, optional OpenShift and community content. + displayName: marketplace-operator + install: + spec: + clusterPermissions: + - rules: + - apiGroups: + - marketplace.redhat.com + resources: + - '*' + verbs: + - '*' + - apiGroups: + - '' + resources: + - services + - configmaps + verbs: + - '*' + - apiGroups: + - operators.coreos.com + resources: + - catalogsources + verbs: + - '*' + serviceAccountName: marketplace-operator + deployments: + - name: marketplace-operator + spec: + replicas: 1 + selector: + matchLabels: + name: marketplace-operator + template: + metadata: + labels: + name: marketplace-operator + name: marketplace-operator + spec: + containers: + - command: + - marketplace-operator + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: OPERATOR_NAME + value: marketplace-operator + image: quay.io/openshift/origin-operator-marketplace:latest + imagePullPolicy: Always + livenessProbe: + httpGet: + path: /healthz + port: 8080 + name: marketplace-operator + ports: + - containerPort: 60000 + name: metrics + - containerPort: 8080 + name: healthz + readinessProbe: + httpGet: + path: /healthz + port: 8080 + serviceAccountName: marketplace-operator + strategy: deployment + installModes: + - supported: true + type: OwnNamespace + - supported: true + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces + keywords: + - marketplace + - catalog + - olm + - admin + labels: + name: marketplace-operator + links: + - name: Markplace Operator Source Code + url: https://github.com/operator-framework/operator-marketplace + maintainers: + - email: aos-marketplace@redhat.com + name: AOS Marketplace Team + maturity: alpha + provider: + name: Red Hat + selector: + matchLabels: + name: marketplace-operator + version: 0.0.1 + customResourceDefinitions: | + - apiVersion: apiextensions.k8s.io/v1beta1 + kind: CustomResourceDefinition + metadata: + annotations: + description: Represents a CatalogSourceConfig. + displayName: Catalog Source Config + name: catalogsourceconfigs.marketplace.redhat.com + spec: + additionalPrinterColumns: + - JSONPath: .spec.targetNamespace + description: The namespace where the operators will be enabled + name: TargetNamespace + type: string + - JSONPath: .spec.packages + description: List of operator(s) which will be enabled in the target namespace + name: Packages + type: string + - JSONPath: .status.currentPhase.phase.name + description: Current status of the CatalogSourceConfig + name: Status + type: string + - JSONPath: .status.currentPhase.phase.message + description: Message associated with the current status + name: Message + type: string + - JSONPath: .metadata.creationTimestamp + name: Age + type: date + group: marketplace.redhat.com + names: + kind: CatalogSourceConfig + listKind: CatalogSourceConfigList + plural: catalogsourceconfigs + shortNames: + - csc + singular: catalogsourceconfig + scope: Namespaced + validation: + openAPIV3Schema: + properties: + spec: + description: Spec for a CatalogSourceConfig + properties: + packages: + description: Comma separated list of operator(s) without spaces + which will be enabled in the target namespace + type: string + targetNamespace: + description: The namespace where the operators will be enabled + type: string + required: + - targetNamespace + - packages + type: object + version: v1alpha1 + - apiVersion: apiextensions.k8s.io/v1beta1 + kind: CustomResourceDefinition + metadata: + annotations: + description: Represents an OperatorSource. + displayName: Operator Source + name: operatorsources.marketplace.redhat.com + spec: + additionalPrinterColumns: + - JSONPath: .spec.type + description: The type of the OperatorSource + name: Type + type: string + - JSONPath: .spec.endpoint + description: The endpoint of the OperatorSource + name: Endpoint + type: string + - JSONPath: .spec.registryNamespace + description: App registry namespace + name: Registry + type: string + - JSONPath: .spec.displayName + description: Display (pretty) name to indicate the OperatorSource's name + name: DisplayName + type: string + - JSONPath: .spec.publisher + description: Publisher of the OperatorSource + name: Publisher + type: string + - JSONPath: .status.currentPhase.phase.name + description: Current status of the OperatorSource + name: Status + type: string + - JSONPath: .status.currentPhase.phase.message + description: Message associated with the current status + name: Message + type: string + - JSONPath: .metadata.creationTimestamp + name: Age + type: date + group: marketplace.redhat.com + names: + kind: OperatorSource + listKind: OperatorSourceList + plural: operatorsources + shortNames: + - opsrc + singular: operatorsource + scope: Namespaced + validation: + openAPIV3Schema: + properties: + spec: + description: Spec for an OperatorSource. + properties: + endpoint: + description: Points to the remote app registry server from where + operator manifests can be fetched. + type: string + registryNamespace: + description: 'The namespace in app registry. + + Only operator manifests under this namespace will be visible. + + Please note that this is not a k8s namespace.' + type: string + type: + description: The type of the OperatorSource + pattern: appregistry + type: string + required: + - type + - endpoint + - registryNamespace + type: object + version: v1alpha1 + packages: | + - channels: + - currentCSV: marketplace-operator.v0.0.1 + name: alpha + packageName: marketplace diff --git a/tests/test_files/bundles/verification/csvmissingcrdownedattr.invalid.bundle.yaml b/tests/test_files/bundles/verification/csvmissingcrdownedattr.invalid.bundle.yaml new file mode 100644 index 0000000..b4c4199 --- /dev/null +++ b/tests/test_files/bundles/verification/csvmissingcrdownedattr.invalid.bundle.yaml @@ -0,0 +1,300 @@ +# CSV.spec.customresourcedefinitions.owned.{description,displayName} is missing in this file +data: + clusterServiceVersions: | + - apiVersion: operators.coreos.com/v1alpha1 + kind: ClusterServiceVersion + metadata: + annotations: + alm-examples: '[{"apiVersion":"etcd.database.coreos.com/v1beta2","kind":"EtcdCluster","metadata":{"name":"example","namespace":"default"},"spec":{"size":3,"version":"3.2.13"}},{"apiVersion":"etcd.database.coreos.com/v1beta2","kind":"EtcdRestore","metadata":{"name":"example-etcd-cluster"},"spec":{"etcdCluster":{"name":"example-etcd-cluster"},"backupStorageType":"S3","s3":{"path":"","awsSecret":""}}},{"apiVersion":"etcd.database.coreos.com/v1beta2","kind":"EtcdBackup","metadata":{"name":"example-etcd-cluster-backup"},"spec":{"etcdEndpoints":[""],"storageType":"S3","s3":{"path":"","awsSecret":""}}}]' + categories: openshift required + certified: 'true' + containerImage: quay.io/openshift/origin-operator-marketplace:latest + createdAt: 2019/11/15 + description: An operator to run the OpenShift marketplace + healthIndex: B + repository: https://github.com/operator-framework/operator-marketplace + support: Red Hat + name: marketplace-operator.v0.0.1 + namespace: placeholder + spec: + customresourcedefinitions: + owned: + - kind: OperatorSource + name: operatorsources.marketplace.redhat.com + specDescriptors: + - description: The type of the operator source. + displayName: Type + path: type + - description: Points to the remote app registry server from where operator + manifests can be fetched. + displayName: Endpoint + path: endpoint + - description: 'The namespace in app registry. + + Only operator manifests under this namespace will be visible. + + Please note that this is not a k8s namespace.' + displayName: Registry Namespace + path: registryNamespace + statusDescriptors: + - description: Current status of the CatalogSourceConfig + displayName: Current Phase Name + path: currentPhase.phase.name + - description: Message associated with the current status + displayName: Current Phase Message + path: currentPhase.phase.message + version: v1alpha1 + - description: Represents a CatalogSourceConfig object which is used to configure + a CatalogSource. + displayName: Catalog Source Config + kind: CatalogSourceConfig + name: catalogsourceconfigs.marketplace.redhat.com + specDescriptors: + - description: The namespace where the operators will be enabled. + displayName: Target Namespace + path: targetNamespace + - description: List of operator(s) which will be enabled in the target namespace + displayName: Packages + path: packages + statusDescriptors: + - description: Current status of the CatalogSourceConfig + displayName: Current Phase Name + path: currentPhase.phase.name + - description: Message associated with the current status + displayName: Current Phase Message + path: currentPhase.phase.message + version: v1alpha1 + description: Marketplace is a gateway for users to consume off-cluster Operators + which will include Red Hat, ISV, optional OpenShift and community content. + displayName: marketplace-operator + install: + spec: + clusterPermissions: + - rules: + - apiGroups: + - marketplace.redhat.com + resources: + - '*' + verbs: + - '*' + - apiGroups: + - '' + resources: + - services + - configmaps + verbs: + - '*' + - apiGroups: + - operators.coreos.com + resources: + - catalogsources + verbs: + - '*' + serviceAccountName: marketplace-operator + deployments: + - name: marketplace-operator + spec: + replicas: 1 + selector: + matchLabels: + name: marketplace-operator + template: + metadata: + labels: + name: marketplace-operator + name: marketplace-operator + spec: + containers: + - command: + - marketplace-operator + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: OPERATOR_NAME + value: marketplace-operator + image: quay.io/openshift/origin-operator-marketplace:latest + imagePullPolicy: Always + livenessProbe: + httpGet: + path: /healthz + port: 8080 + name: marketplace-operator + ports: + - containerPort: 60000 + name: metrics + - containerPort: 8080 + name: healthz + readinessProbe: + httpGet: + path: /healthz + port: 8080 + serviceAccountName: marketplace-operator + strategy: deployment + installModes: + - supported: true + type: OwnNamespace + - supported: true + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces + keywords: + - marketplace + - catalog + - olm + - admin + labels: + name: marketplace-operator + links: + - name: Markplace Operator Source Code + url: https://github.com/operator-framework/operator-marketplace + maintainers: + - email: aos-marketplace@redhat.com + name: AOS Marketplace Team + maturity: alpha + provider: + name: Red Hat + selector: + matchLabels: + name: marketplace-operator + version: 0.0.1 + customResourceDefinitions: | + - apiVersion: apiextensions.k8s.io/v1beta1 + kind: CustomResourceDefinition + metadata: + annotations: + description: Represents a CatalogSourceConfig. + displayName: Catalog Source Config + name: catalogsourceconfigs.marketplace.redhat.com + spec: + additionalPrinterColumns: + - JSONPath: .spec.targetNamespace + description: The namespace where the operators will be enabled + name: TargetNamespace + type: string + - JSONPath: .spec.packages + description: List of operator(s) which will be enabled in the target namespace + name: Packages + type: string + - JSONPath: .status.currentPhase.phase.name + description: Current status of the CatalogSourceConfig + name: Status + type: string + - JSONPath: .status.currentPhase.phase.message + description: Message associated with the current status + name: Message + type: string + - JSONPath: .metadata.creationTimestamp + name: Age + type: date + group: marketplace.redhat.com + names: + kind: CatalogSourceConfig + listKind: CatalogSourceConfigList + plural: catalogsourceconfigs + shortNames: + - csc + singular: catalogsourceconfig + scope: Namespaced + validation: + openAPIV3Schema: + properties: + spec: + description: Spec for a CatalogSourceConfig + properties: + packages: + description: Comma separated list of operator(s) without spaces + which will be enabled in the target namespace + type: string + targetNamespace: + description: The namespace where the operators will be enabled + type: string + required: + - targetNamespace + - packages + type: object + version: v1alpha1 + - apiVersion: apiextensions.k8s.io/v1beta1 + kind: CustomResourceDefinition + metadata: + annotations: + description: Represents an OperatorSource. + displayName: Operator Source + name: operatorsources.marketplace.redhat.com + spec: + additionalPrinterColumns: + - JSONPath: .spec.type + description: The type of the OperatorSource + name: Type + type: string + - JSONPath: .spec.endpoint + description: The endpoint of the OperatorSource + name: Endpoint + type: string + - JSONPath: .spec.registryNamespace + description: App registry namespace + name: Registry + type: string + - JSONPath: .spec.displayName + description: Display (pretty) name to indicate the OperatorSource's name + name: DisplayName + type: string + - JSONPath: .spec.publisher + description: Publisher of the OperatorSource + name: Publisher + type: string + - JSONPath: .status.currentPhase.phase.name + description: Current status of the OperatorSource + name: Status + type: string + - JSONPath: .status.currentPhase.phase.message + description: Message associated with the current status + name: Message + type: string + - JSONPath: .metadata.creationTimestamp + name: Age + type: date + group: marketplace.redhat.com + names: + kind: OperatorSource + listKind: OperatorSourceList + plural: operatorsources + shortNames: + - opsrc + singular: operatorsource + scope: Namespaced + validation: + openAPIV3Schema: + properties: + spec: + description: Spec for an OperatorSource. + properties: + endpoint: + description: Points to the remote app registry server from where + operator manifests can be fetched. + type: string + registryNamespace: + description: 'The namespace in app registry. + + Only operator manifests under this namespace will be visible. + + Please note that this is not a k8s namespace.' + type: string + type: + description: The type of the OperatorSource + pattern: appregistry + type: string + required: + - type + - endpoint + - registryNamespace + type: object + version: v1alpha1 + packages: | + - channels: + - currentCSV: marketplace-operator.v0.0.1 + name: alpha + packageName: marketplace diff --git a/tests/test_validate.py b/tests/test_validate.py index 73e41ea..da24b97 100644 --- a/tests/test_validate.py +++ b/tests/test_validate.py @@ -44,6 +44,34 @@ def test_valid_bundles(bundle, expected_validation_results_dict): "csvinstallstrategywrongvalue.invalid.bundle.yaml", {'errors': ["csv spec.install.strategy must be one of ['deployment']"], 'warnings': ['csv spec.icon not defined']}), + ("tests/test_files/bundles/verification/csvmissingcrdownedattr.invalid.bundle.yaml", + {'errors': [ + 'displayName not defined for item in spec.customresourcedefinitions.', + 'description not defined for item in spec.customresourcedefinitions.'], + 'warnings': ['csv spec.icon not defined']}), + ("tests/test_files/bundles/verification/csvemptycrdownedattr.invalid.bundle.yaml", + {'errors': ['displayName is empty for item in spec.customresourcedefinitions.', + 'description is empty for item in spec.customresourcedefinitions.'], + 'warnings': ['csv spec.icon not defined']}), + ("tests/test_files/bundles/verification/csvmissingattrspecdesc.invalid.bundle.yaml", + {'errors': [ + 'displayName is not defined for descriptors in ' + 'operatorsources.marketplace.redhat.com', + 'path is not defined for descriptors in ' + 'operatorsources.marketplace.redhat.com', + 'description is not defined for descriptors in ' + 'operatorsources.marketplace.redhat.com'], + 'warnings': ['csv spec.icon not defined']}), + ("tests/test_files/bundles/verification/csvemptyattrspecdesc.invalid.bundle.yaml", + {'errors': [ + 'displayName is empty for descriptors in ' + 'operatorsources.marketplace.redhat.com', + 'description is empty for descriptors in ' + 'operatorsources.marketplace.redhat.com', + 'path is empty for descriptors in ' + 'operatorsources.marketplace.redhat.com', + ], + 'warnings': ['csv spec.icon not defined']}), ]) def test_invalid_bundle(bundle, expected_validation_results_dict): valid, validation_results_dict = get_validation_results(bundle) From 07c881a8ba756f99fa70631ec099f42e71d174a8 Mon Sep 17 00:00:00 2001 From: Sunny Date: Tue, 23 Apr 2019 21:13:06 +0530 Subject: [PATCH 2/3] validate: add validation for apiservicedefinitions --- operatorcourier/validate.py | 53 +++ ...csvmissingasdownedattr.invalid.bundle.yaml | 321 ++++++++++++++++++ ...nitoring.v0.2.0.clusterserviceversion.yaml | 3 +- tests/test_validate.py | 10 +- 4 files changed, 384 insertions(+), 3 deletions(-) create mode 100644 tests/test_files/bundles/verification/csvmissingasdownedattr.invalid.bundle.yaml diff --git a/operatorcourier/validate.py b/operatorcourier/validate.py index 7047fa0..db5c5b4 100644 --- a/operatorcourier/validate.py +++ b/operatorcourier/validate.py @@ -190,6 +190,10 @@ def _csv_spec_validation(self, spec, bundleData): spec["customresourcedefinitions"], bundleData) is False: valid = False + if "apiservicedefinitions" in spec: + if self._csv_asd_validation(spec["apiservicedefinitions"]) is False: + valid = False + return valid def _csv_crd_validation(self, customresourcedefinitions, bundleData): @@ -309,6 +313,55 @@ def _csv_crd_validation(self, customresourcedefinitions, bundleData): return valid + def _csv_asd_validation(self, apiservicedefinitions): + valid = True + + if "owned" not in apiservicedefinitions: + self._log_error("spec.apiservicedefinitions.owned" + "not defined for csv") + return False + + # required attributes of owned apiservicedefinitions + attributeList = ["group", "version", "kind", "name", "deploymentName", + "displayName", "description"] + + # validate the owned apiservicedefinitions + def validate_owned(resource, attribute): + if attribute not in resource: + self._log_error( + "%s not defined for item in spec.apiservicedefinitions." % attribute) + return False + elif not resource[attribute]: + self._log_error("%s is empty for item in " + "spec.apiservicedefinitions." % attribute) + return False + return True + + for csvOwnedAsd in apiservicedefinitions["owned"]: + for attr in attributeList: + if validate_owned(csvOwnedAsd, attr) is False: + valid = False + + if "specDescriptors" in csvOwnedAsd and "name" in csvOwnedAsd: + if self._csv_descriptors_validation( + csvOwnedAsd["specDescriptors"], + csvOwnedAsd["name"]) is False: + valid = False + + if "statusDescriptors" in csvOwnedAsd and "name" in csvOwnedAsd: + if self._csv_descriptors_validation( + csvOwnedAsd["statusDescriptors"], + csvOwnedAsd["name"]) is False: + valid = False + + if "actionDescriptors" in csvOwnedAsd and "name" in csvOwnedAsd: + if self._csv_descriptors_validation( + csvOwnedAsd["actionDescriptors"], + csvOwnedAsd["name"]) is False: + valid = False + + return valid + def _csv_spec_install_validation(self, install): valid = True diff --git a/tests/test_files/bundles/verification/csvmissingasdownedattr.invalid.bundle.yaml b/tests/test_files/bundles/verification/csvmissingasdownedattr.invalid.bundle.yaml new file mode 100644 index 0000000..5a56ecb --- /dev/null +++ b/tests/test_files/bundles/verification/csvmissingasdownedattr.invalid.bundle.yaml @@ -0,0 +1,321 @@ +# CSV.spec.apiservicedefinitions.owned.{kind,deploymentName} and +# CSV.spec.apiservicedefinitions.owned.specDescriptors.{description,displayName} +# are empty in this file +data: + clusterServiceVersions: | + - apiVersion: operators.coreos.com/v1alpha1 + kind: ClusterServiceVersion + metadata: + annotations: + alm-examples: '[{"apiVersion":"etcd.database.coreos.com/v1beta2","kind":"EtcdCluster","metadata":{"name":"example","namespace":"default"},"spec":{"size":3,"version":"3.2.13"}},{"apiVersion":"etcd.database.coreos.com/v1beta2","kind":"EtcdRestore","metadata":{"name":"example-etcd-cluster"},"spec":{"etcdCluster":{"name":"example-etcd-cluster"},"backupStorageType":"S3","s3":{"path":"","awsSecret":""}}},{"apiVersion":"etcd.database.coreos.com/v1beta2","kind":"EtcdBackup","metadata":{"name":"example-etcd-cluster-backup"},"spec":{"etcdEndpoints":[""],"storageType":"S3","s3":{"path":"","awsSecret":""}}}]' + categories: openshift required + certified: 'true' + containerImage: quay.io/openshift/origin-operator-marketplace:latest + createdAt: 2019/11/15 + description: An operator to run the OpenShift marketplace + healthIndex: B + repository: https://github.com/operator-framework/operator-marketplace + support: Red Hat + name: marketplace-operator.v0.0.1 + namespace: placeholder + spec: + apiservicedefinitions: + owned: + - description: Some description + displayName: Some Display Name + kind: SomeKind + name: somename.redhat.com + group: somegroup.redhat.com + version: v1alpha1 + deploymentName: some deployment name + - version: v1alpha1 + group: somegroup.redhat.com + kind: + description: Some description 2 + displayName: Some Display Name 2 + name: someothername.redhat.com + specDescriptors: + - path: type + customresourcedefinitions: + owned: + - description: Represents an OperatorSource. + displayName: Operator Source + kind: OperatorSource + name: operatorsources.marketplace.redhat.com + specDescriptors: + - description: The type of the operator source. + displayName: Type + path: type + - description: Points to the remote app registry server from where operator + manifests can be fetched. + displayName: Endpoint + path: endpoint + - description: 'The namespace in app registry. + + Only operator manifests under this namespace will be visible. + + Please note that this is not a k8s namespace.' + displayName: Registry Namespace + path: registryNamespace + statusDescriptors: + - description: Current status of the CatalogSourceConfig + displayName: Current Phase Name + path: currentPhase.phase.name + - description: Message associated with the current status + displayName: Current Phase Message + path: currentPhase.phase.message + version: v1alpha1 + - description: Represents a CatalogSourceConfig object which is used to configure + a CatalogSource. + displayName: Catalog Source Config + kind: CatalogSourceConfig + name: catalogsourceconfigs.marketplace.redhat.com + specDescriptors: + - description: The namespace where the operators will be enabled. + displayName: Target Namespace + path: targetNamespace + - description: List of operator(s) which will be enabled in the target namespace + displayName: Packages + path: packages + statusDescriptors: + - description: Current status of the CatalogSourceConfig + displayName: Current Phase Name + path: currentPhase.phase.name + - description: Message associated with the current status + displayName: Current Phase Message + path: currentPhase.phase.message + version: v1alpha1 + description: Marketplace is a gateway for users to consume off-cluster Operators + which will include Red Hat, ISV, optional OpenShift and community content. + displayName: marketplace-operator + install: + spec: + clusterPermissions: + - rules: + - apiGroups: + - marketplace.redhat.com + resources: + - '*' + verbs: + - '*' + - apiGroups: + - '' + resources: + - services + - configmaps + verbs: + - '*' + - apiGroups: + - operators.coreos.com + resources: + - catalogsources + verbs: + - '*' + serviceAccountName: marketplace-operator + deployments: + - name: marketplace-operator + spec: + replicas: 1 + selector: + matchLabels: + name: marketplace-operator + template: + metadata: + labels: + name: marketplace-operator + name: marketplace-operator + spec: + containers: + - command: + - marketplace-operator + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: OPERATOR_NAME + value: marketplace-operator + image: quay.io/openshift/origin-operator-marketplace:latest + imagePullPolicy: Always + livenessProbe: + httpGet: + path: /healthz + port: 8080 + name: marketplace-operator + ports: + - containerPort: 60000 + name: metrics + - containerPort: 8080 + name: healthz + readinessProbe: + httpGet: + path: /healthz + port: 8080 + serviceAccountName: marketplace-operator + strategy: deployment + installModes: + - supported: true + type: OwnNamespace + - supported: true + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces + keywords: + - marketplace + - catalog + - olm + - admin + labels: + name: marketplace-operator + links: + - name: Markplace Operator Source Code + url: https://github.com/operator-framework/operator-marketplace + maintainers: + - email: aos-marketplace@redhat.com + name: AOS Marketplace Team + maturity: alpha + provider: + name: Red Hat + selector: + matchLabels: + name: marketplace-operator + version: 0.0.1 + customResourceDefinitions: | + - apiVersion: apiextensions.k8s.io/v1beta1 + kind: CustomResourceDefinition + metadata: + annotations: + description: Represents a CatalogSourceConfig. + displayName: Catalog Source Config + name: catalogsourceconfigs.marketplace.redhat.com + spec: + additionalPrinterColumns: + - JSONPath: .spec.targetNamespace + description: The namespace where the operators will be enabled + name: TargetNamespace + type: string + - JSONPath: .spec.packages + description: List of operator(s) which will be enabled in the target namespace + name: Packages + type: string + - JSONPath: .status.currentPhase.phase.name + description: Current status of the CatalogSourceConfig + name: Status + type: string + - JSONPath: .status.currentPhase.phase.message + description: Message associated with the current status + name: Message + type: string + - JSONPath: .metadata.creationTimestamp + name: Age + type: date + group: marketplace.redhat.com + names: + kind: CatalogSourceConfig + listKind: CatalogSourceConfigList + plural: catalogsourceconfigs + shortNames: + - csc + singular: catalogsourceconfig + scope: Namespaced + validation: + openAPIV3Schema: + properties: + spec: + description: Spec for a CatalogSourceConfig + properties: + packages: + description: Comma separated list of operator(s) without spaces + which will be enabled in the target namespace + type: string + targetNamespace: + description: The namespace where the operators will be enabled + type: string + required: + - targetNamespace + - packages + type: object + version: v1alpha1 + - apiVersion: apiextensions.k8s.io/v1beta1 + kind: CustomResourceDefinition + metadata: + annotations: + description: Represents an OperatorSource. + displayName: Operator Source + name: operatorsources.marketplace.redhat.com + spec: + additionalPrinterColumns: + - JSONPath: .spec.type + description: The type of the OperatorSource + name: Type + type: string + - JSONPath: .spec.endpoint + description: The endpoint of the OperatorSource + name: Endpoint + type: string + - JSONPath: .spec.registryNamespace + description: App registry namespace + name: Registry + type: string + - JSONPath: .spec.displayName + description: Display (pretty) name to indicate the OperatorSource's name + name: DisplayName + type: string + - JSONPath: .spec.publisher + description: Publisher of the OperatorSource + name: Publisher + type: string + - JSONPath: .status.currentPhase.phase.name + description: Current status of the OperatorSource + name: Status + type: string + - JSONPath: .status.currentPhase.phase.message + description: Message associated with the current status + name: Message + type: string + - JSONPath: .metadata.creationTimestamp + name: Age + type: date + group: marketplace.redhat.com + names: + kind: OperatorSource + listKind: OperatorSourceList + plural: operatorsources + shortNames: + - opsrc + singular: operatorsource + scope: Namespaced + validation: + openAPIV3Schema: + properties: + spec: + description: Spec for an OperatorSource. + properties: + endpoint: + description: Points to the remote app registry server from where + operator manifests can be fetched. + type: string + registryNamespace: + description: 'The namespace in app registry. + + Only operator manifests under this namespace will be visible. + + Please note that this is not a k8s namespace.' + type: string + type: + description: The type of the OperatorSource + pattern: appregistry + type: string + required: + - type + - endpoint + - registryNamespace + type: object + version: v1alpha1 + packages: | + - channels: + - currentCSV: marketplace-operator.v0.0.1 + name: alpha + packageName: marketplace diff --git a/tests/test_files/yaml_source_dir/valid_yamls_with_single_crd/dynatrace-monitoring.v0.2.0.clusterserviceversion.yaml b/tests/test_files/yaml_source_dir/valid_yamls_with_single_crd/dynatrace-monitoring.v0.2.0.clusterserviceversion.yaml index 1034d34..a900816 100644 --- a/tests/test_files/yaml_source_dir/valid_yamls_with_single_crd/dynatrace-monitoring.v0.2.0.clusterserviceversion.yaml +++ b/tests/test_files/yaml_source_dir/valid_yamls_with_single_crd/dynatrace-monitoring.v0.2.0.clusterserviceversion.yaml @@ -38,7 +38,8 @@ metadata: name: dynatrace-monitoring.v0.2.0 namespace: "placeholder" spec: - apiservicedefinitions: {} + apiservicedefinitions: + owned: {} customresourcedefinitions: owned: - description: Dyantrace OneAgent monitoring agent diff --git a/tests/test_validate.py b/tests/test_validate.py index da24b97..c611453 100644 --- a/tests/test_validate.py +++ b/tests/test_validate.py @@ -69,8 +69,14 @@ def test_valid_bundles(bundle, expected_validation_results_dict): 'description is empty for descriptors in ' 'operatorsources.marketplace.redhat.com', 'path is empty for descriptors in ' - 'operatorsources.marketplace.redhat.com', - ], + 'operatorsources.marketplace.redhat.com'], + 'warnings': ['csv spec.icon not defined']}), + ("tests/test_files/bundles/verification/csvmissingasdownedattr.invalid.bundle.yaml", + {'errors': [ + 'kind is empty for item in spec.apiservicedefinitions.', + 'deploymentName not defined for item in spec.apiservicedefinitions.', + 'displayName is not defined for descriptors in someothername.redhat.com', + 'description is not defined for descriptors in someothername.redhat.com'], 'warnings': ['csv spec.icon not defined']}), ]) def test_invalid_bundle(bundle, expected_validation_results_dict): From 983e781c30de92963ca74fff7f88469842b8c4bd Mon Sep 17 00:00:00 2001 From: Sunny Date: Sun, 5 May 2019 21:50:06 +0530 Subject: [PATCH 3/3] Remove owned requirement --- operatorcourier/validate.py | 259 +++++++++--------- ...nitoring.v0.2.0.clusterserviceversion.yaml | 3 +- 2 files changed, 127 insertions(+), 135 deletions(-) diff --git a/operatorcourier/validate.py b/operatorcourier/validate.py index db5c5b4..4f7c711 100644 --- a/operatorcourier/validate.py +++ b/operatorcourier/validate.py @@ -207,158 +207,151 @@ def _csv_crd_validation(self, customresourcedefinitions, bundleData): except KeyError: pass - if "owned" not in customresourcedefinitions: - self._log_error("spec.customresourcedefinitions.owned" - "not defined for csv") - return False - - for csvOwnedCrd in customresourcedefinitions["owned"]: - if "name" not in csvOwnedCrd: - self._log_error("name not defined for item in " - "spec.customresourcedefinitions.") - valid = False - elif csvOwnedCrd["name"] not in crdList: - self._log_error("custom resource definition %s referenced in csv " - "not defined in root list of crds", - csvOwnedCrd["name"]) - valid = False - - if "kind" not in csvOwnedCrd: - self._log_error("kind not defined for item in " - "spec.customresourcedefinitions.") - valid = False - if "version" not in csvOwnedCrd: - self._log_error("version not defined for item in " - "spec.customresourcedefinitions.") - valid = False - - # Values of name, version and kind above are compared with their - # values in the associated CRD files later. Empty string check - # is not needed. - # displayName and description should be checked for empty - # strings. - if "displayName" not in csvOwnedCrd: - self._log_error("displayName not defined for item in " - "spec.customresourcedefinitions.") - valid = False - elif not csvOwnedCrd["displayName"]: - self._log_error("displayName is empty for item in " - "spec.customresourcedefinitions.") - valid = False - if "description" not in csvOwnedCrd: - self._log_error("description not defined for item in " - "spec.customresourcedefinitions.") - valid = False - elif not csvOwnedCrd["description"]: - self._log_error("description is empty for item in " - "spec.customresourcedefinitions.") - valid = False - - if "specDescriptors" in csvOwnedCrd and "name" in csvOwnedCrd: - if self._csv_descriptors_validation( - csvOwnedCrd["specDescriptors"], - csvOwnedCrd["name"]) is False: + if "owned" in customresourcedefinitions: + for csvOwnedCrd in customresourcedefinitions["owned"]: + if "name" not in csvOwnedCrd: + self._log_error("name not defined for item in " + "spec.customresourcedefinitions.") + valid = False + elif csvOwnedCrd["name"] not in crdList: + self._log_error("custom resource definition %s referenced in csv " + "not defined in root list of crds", + csvOwnedCrd["name"]) valid = False - if "statusDescriptors" in csvOwnedCrd and "name" in csvOwnedCrd: - if self._csv_descriptors_validation( - csvOwnedCrd["statusDescriptors"], - csvOwnedCrd["name"]) is False: + if "kind" not in csvOwnedCrd: + self._log_error("kind not defined for item in " + "spec.customresourcedefinitions.") + valid = False + if "version" not in csvOwnedCrd: + self._log_error("version not defined for item in " + "spec.customresourcedefinitions.") valid = False - if "actionDescriptors" in csvOwnedCrd and "name" in csvOwnedCrd: - if self._csv_descriptors_validation( - csvOwnedCrd["actionDescriptors"], - csvOwnedCrd["name"]) is False: + # Values of name, version and kind above are compared with their + # values in the associated CRD files later. Empty string check + # is not needed. + # displayName and description should be checked for empty + # strings. + if "displayName" not in csvOwnedCrd: + self._log_error("displayName not defined for item in " + "spec.customresourcedefinitions.") + valid = False + elif not csvOwnedCrd["displayName"]: + self._log_error("displayName is empty for item in " + "spec.customresourcedefinitions.") + valid = False + if "description" not in csvOwnedCrd: + self._log_error("description not defined for item in " + "spec.customresourcedefinitions.") + valid = False + elif not csvOwnedCrd["description"]: + self._log_error("description is empty for item in " + "spec.customresourcedefinitions.") valid = False - for crd in bundleData[self.crdKey]: - if 'name' not in csvOwnedCrd: - continue - if 'metadata' not in crd or 'name' not in crd['metadata']: - continue - if csvOwnedCrd['name'] != crd['metadata']['name']: - continue - - if 'kind' in csvOwnedCrd: - if 'spec' in crd: - if 'names' in crd['spec']: - if 'kind' in crd['spec']['names']: - if csvOwnedCrd['kind'] != \ - crd['spec']['names']['kind']: - self._log_error('CRD.spec.names.kind does not ' - 'match CSV.spec.crd.owned.kind') - valid = False + if "specDescriptors" in csvOwnedCrd and "name" in csvOwnedCrd: + if self._csv_descriptors_validation( + csvOwnedCrd["specDescriptors"], + csvOwnedCrd["name"]) is False: + valid = False - if 'version' in csvOwnedCrd: - if 'spec' in crd: - if 'version' in crd['spec']: - if csvOwnedCrd['version'] != crd['spec']['version']: - self._log_error('CRD.spec.version does not match ' - 'CSV.spec.crd.owned.version') - valid = False + if "statusDescriptors" in csvOwnedCrd and "name" in csvOwnedCrd: + if self._csv_descriptors_validation( + csvOwnedCrd["statusDescriptors"], + csvOwnedCrd["name"]) is False: + valid = False + + if "actionDescriptors" in csvOwnedCrd and "name" in csvOwnedCrd: + if self._csv_descriptors_validation( + csvOwnedCrd["actionDescriptors"], + csvOwnedCrd["name"]) is False: + valid = False - if 'name' in csvOwnedCrd: - if 'spec' in crd: - if 'names' in crd['spec'] and 'group' in crd['spec']: - if 'plural' in crd['spec']['names']: - if csvOwnedCrd['name'] != \ - crd['spec']['names']['plural'] + '.' + \ - crd['spec']['group']: - self._log_error("`CRD.spec.names.plural`." - "`CRD.spec.group` does not " - "match " - "CSV.spec.crd.owned.name") + for crd in bundleData[self.crdKey]: + if 'name' not in csvOwnedCrd: + continue + if 'metadata' not in crd or 'name' not in crd['metadata']: + continue + if csvOwnedCrd['name'] != crd['metadata']['name']: + continue + + if 'kind' in csvOwnedCrd: + if 'spec' in crd: + if 'names' in crd['spec']: + if 'kind' in crd['spec']['names']: + if csvOwnedCrd['kind'] != \ + crd['spec']['names']['kind']: + self._log_error('CRD.spec.names.kind does not ' + 'match CSV.spec.crd.owned.kind') + valid = False + + if 'version' in csvOwnedCrd: + if 'spec' in crd: + if 'version' in crd['spec']: + if csvOwnedCrd['version'] != crd['spec']['version']: + self._log_error('CRD.spec.version does not match ' + 'CSV.spec.crd.owned.version') valid = False + if 'name' in csvOwnedCrd: + if 'spec' in crd: + if 'names' in crd['spec'] and 'group' in crd['spec']: + if 'plural' in crd['spec']['names']: + if csvOwnedCrd['name'] != \ + crd['spec']['names']['plural'] + '.' + \ + crd['spec']['group']: + self._log_error("`CRD.spec.names.plural`." + "`CRD.spec.group` does not " + "match " + "CSV.spec.crd.owned.name") + valid = False + return valid def _csv_asd_validation(self, apiservicedefinitions): valid = True - if "owned" not in apiservicedefinitions: - self._log_error("spec.apiservicedefinitions.owned" - "not defined for csv") - return False - - # required attributes of owned apiservicedefinitions - attributeList = ["group", "version", "kind", "name", "deploymentName", - "displayName", "description"] - - # validate the owned apiservicedefinitions - def validate_owned(resource, attribute): - if attribute not in resource: - self._log_error( - "%s not defined for item in spec.apiservicedefinitions." % attribute) - return False - elif not resource[attribute]: - self._log_error("%s is empty for item in " - "spec.apiservicedefinitions." % attribute) - return False - return True + if "owned" in apiservicedefinitions: + # required attributes of owned apiservicedefinitions + attributeList = ["group", "version", "kind", "name", "deploymentName", + "displayName", "description"] - for csvOwnedAsd in apiservicedefinitions["owned"]: - for attr in attributeList: - if validate_owned(csvOwnedAsd, attr) is False: - valid = False + # validate the owned apiservicedefinitions + def validate_owned(resource, attribute): + if attribute not in resource: + self._log_error( + "%s not defined for item in spec.apiservicedefinitions." + % attribute) + return False + elif not resource[attribute]: + self._log_error("%s is empty for item in " + "spec.apiservicedefinitions." % attribute) + return False + return True + + for csvOwnedAsd in apiservicedefinitions["owned"]: + for attr in attributeList: + if validate_owned(csvOwnedAsd, attr) is False: + valid = False - if "specDescriptors" in csvOwnedAsd and "name" in csvOwnedAsd: - if self._csv_descriptors_validation( - csvOwnedAsd["specDescriptors"], - csvOwnedAsd["name"]) is False: - valid = False + if "specDescriptors" in csvOwnedAsd and "name" in csvOwnedAsd: + if self._csv_descriptors_validation( + csvOwnedAsd["specDescriptors"], + csvOwnedAsd["name"]) is False: + valid = False - if "statusDescriptors" in csvOwnedAsd and "name" in csvOwnedAsd: - if self._csv_descriptors_validation( - csvOwnedAsd["statusDescriptors"], - csvOwnedAsd["name"]) is False: - valid = False + if "statusDescriptors" in csvOwnedAsd and "name" in csvOwnedAsd: + if self._csv_descriptors_validation( + csvOwnedAsd["statusDescriptors"], + csvOwnedAsd["name"]) is False: + valid = False - if "actionDescriptors" in csvOwnedAsd and "name" in csvOwnedAsd: - if self._csv_descriptors_validation( - csvOwnedAsd["actionDescriptors"], - csvOwnedAsd["name"]) is False: - valid = False + if "actionDescriptors" in csvOwnedAsd and "name" in csvOwnedAsd: + if self._csv_descriptors_validation( + csvOwnedAsd["actionDescriptors"], + csvOwnedAsd["name"]) is False: + valid = False return valid diff --git a/tests/test_files/yaml_source_dir/valid_yamls_with_single_crd/dynatrace-monitoring.v0.2.0.clusterserviceversion.yaml b/tests/test_files/yaml_source_dir/valid_yamls_with_single_crd/dynatrace-monitoring.v0.2.0.clusterserviceversion.yaml index a900816..1034d34 100644 --- a/tests/test_files/yaml_source_dir/valid_yamls_with_single_crd/dynatrace-monitoring.v0.2.0.clusterserviceversion.yaml +++ b/tests/test_files/yaml_source_dir/valid_yamls_with_single_crd/dynatrace-monitoring.v0.2.0.clusterserviceversion.yaml @@ -38,8 +38,7 @@ metadata: name: dynatrace-monitoring.v0.2.0 namespace: "placeholder" spec: - apiservicedefinitions: - owned: {} + apiservicedefinitions: {} customresourcedefinitions: owned: - description: Dyantrace OneAgent monitoring agent