diff --git a/pkg/validation/errors/error.go b/pkg/validation/errors/error.go index dce5d70ad..4ff55c0dd 100644 --- a/pkg/validation/errors/error.go +++ b/pkg/validation/errors/error.go @@ -243,7 +243,3 @@ func invalidObject(lvl Level, detail string, value interface{}) Error { func WarnInvalidObject(detail string, value interface{}) Error { return failedValidation(LevelWarn, detail, value) } - -func WarnMissingIcon(detail string) Error { - return failedValidation(LevelWarn, detail, "") -} diff --git a/pkg/validation/internal/operatorhub.go b/pkg/validation/internal/operatorhub.go index 8558ce673..e94e62b0c 100644 --- a/pkg/validation/internal/operatorhub.go +++ b/pkg/validation/internal/operatorhub.go @@ -74,16 +74,19 @@ func validateBundleOperatorHub(bundle *manifests.Bundle) errors.ManifestResult { return result } - errs := validateHubCSVSpec(*bundle.CSV) + errs, warns := validateHubCSVSpec(*bundle.CSV) for _, err := range errs { result.Add(errors.ErrInvalidCSV(err.Error(), bundle.CSV.GetName())) } - + for _, warn := range warns { + result.Add(errors.WarnInvalidCSV(warn.Error(), bundle.CSV.GetName())) + } return result } -func validateHubCSVSpec(csv v1alpha1.ClusterServiceVersion) []error { +func validateHubCSVSpec(csv v1alpha1.ClusterServiceVersion) ([]error, []error) { var errs []error + var warns []error if csv.Spec.Provider.Name == "" { errs = append(errs, fmt.Errorf("csv.Spec.Provider.Name not specified")) @@ -146,7 +149,7 @@ func validateHubCSVSpec(csv v1alpha1.ClusterServiceVersion) []error { } } } else { - errs = append(errs, errors.WarnMissingIcon("csv.Spec.Icon not specified")) + warns = append(warns, fmt.Errorf("csv.Spec.Icon not specified")) } if categories, ok := csv.ObjectMeta.Annotations["categories"]; ok { @@ -158,7 +161,7 @@ func validateHubCSVSpec(csv v1alpha1.ClusterServiceVersion) []error { customCategories, err := extractCategories(customCategoriesPath) if err != nil { errs = append(errs, fmt.Errorf("could not extract custom categories from categories %#v: %s", customCategories, err)) - return errs + return errs, warns } for _, category := range categorySlice { if _, ok := customCategories[strings.TrimSpace(category)]; !ok { @@ -175,7 +178,7 @@ func validateHubCSVSpec(csv v1alpha1.ClusterServiceVersion) []error { } } - return errs + return errs, warns } type categories struct { diff --git a/pkg/validation/internal/operatorhub_test.go b/pkg/validation/internal/operatorhub_test.go index 149559052..083a908bd 100644 --- a/pkg/validation/internal/operatorhub_test.go +++ b/pkg/validation/internal/operatorhub_test.go @@ -128,3 +128,47 @@ func TestExtractCategories(t *testing.T) { } } } + +func TestWarnNoIcon(t *testing.T) { + var table = []struct { + description string + directory string + hasError bool + errStrings []string + warnStrings []string + }{ + { + description: "valid bundle no icon", + directory: "./testdata/valid_bundle_no_icon", + hasError: false, + errStrings: []string{}, + warnStrings: []string{"Warning: Value : (etcdoperator.v0.9.4) csv.Spec.Icon not specified"}, + }, + } + + for _, tt := range table { + // Validate the bundle object + bundle, err := manifests.GetBundleFromDir(tt.directory) + require.NoError(t, err) + + results := OperatorHubValidator.Validate(bundle) + + if len(results) > 0 { + require.Equal(t, tt.hasError, results[0].HasError()) + if results[0].HasError() { + require.Equal(t, len(tt.errStrings), len(results[0].Errors)) + for _, err := range results[0].Errors { + errString := err.Error() + require.Contains(t, tt.errStrings, errString) + } + } + if results[0].HasWarn() { + require.Equal(t, len(tt.warnStrings), len(results[0].Warnings)) + for _, warn := range results[0].Warnings { + warnString := warn.Error() + require.Contains(t, tt.warnStrings, warnString) + } + } + } + } +} diff --git a/pkg/validation/internal/testdata/valid_bundle_no_icon/etcdbackups.etcd.database.coreos.com.crd.yaml b/pkg/validation/internal/testdata/valid_bundle_no_icon/etcdbackups.etcd.database.coreos.com.crd.yaml new file mode 100644 index 000000000..5afc088b9 --- /dev/null +++ b/pkg/validation/internal/testdata/valid_bundle_no_icon/etcdbackups.etcd.database.coreos.com.crd.yaml @@ -0,0 +1,13 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: etcdbackups.etcd.database.coreos.com +spec: + group: etcd.database.coreos.com + names: + kind: EtcdBackup + listKind: EtcdBackupList + plural: etcdbackups + singular: etcdbackup + scope: Namespaced + version: v1beta2 diff --git a/pkg/validation/internal/testdata/valid_bundle_no_icon/etcdclusters.etcd.database.coreos.com.crd.yaml b/pkg/validation/internal/testdata/valid_bundle_no_icon/etcdclusters.etcd.database.coreos.com.crd.yaml new file mode 100644 index 000000000..01111e5c5 --- /dev/null +++ b/pkg/validation/internal/testdata/valid_bundle_no_icon/etcdclusters.etcd.database.coreos.com.crd.yaml @@ -0,0 +1,16 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: etcdclusters.etcd.database.coreos.com +spec: + group: etcd.database.coreos.com + names: + kind: EtcdCluster + listKind: EtcdClusterList + plural: etcdclusters + shortNames: + - etcdclus + - etcd + singular: etcdcluster + scope: Namespaced + version: v1beta2 diff --git a/pkg/validation/internal/testdata/valid_bundle_no_icon/etcdoperator.v0.9.4.clusterserviceversion.yaml b/pkg/validation/internal/testdata/valid_bundle_no_icon/etcdoperator.v0.9.4.clusterserviceversion.yaml new file mode 100644 index 000000000..38f5f3b9b --- /dev/null +++ b/pkg/validation/internal/testdata/valid_bundle_no_icon/etcdoperator.v0.9.4.clusterserviceversion.yaml @@ -0,0 +1,306 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: ClusterServiceVersion +metadata: + annotations: + alm-examples: "[\n {\n \"apiVersion\": \"etcd.database.coreos.com/v1beta2\"\ + ,\n \"kind\": \"EtcdCluster\",\n \"metadata\": {\n \"name\": \"example\"\ + \n },\n \"spec\": {\n \"size\": 3,\n \"version\": \"3.2.13\"\ + \n }\n },\n {\n \"apiVersion\": \"etcd.database.coreos.com/v1beta2\"\ + ,\n \"kind\": \"EtcdRestore\",\n \"metadata\": {\n \"name\": \"example-etcd-cluster-restore\"\ + \n },\n \"spec\": {\n \"etcdCluster\": {\n \"name\": \"example-etcd-cluster\"\ + \n },\n \"backupStorageType\": \"S3\",\n \"s3\": {\n \"\ + path\": \"\",\n \"awsSecret\": \"\"\n \ + \ }\n }\n },\n {\n \"apiVersion\": \"etcd.database.coreos.com/v1beta2\"\ + ,\n \"kind\": \"EtcdBackup\",\n \"metadata\": {\n \"name\": \"example-etcd-cluster-backup\"\ + \n },\n \"spec\": {\n \"etcdEndpoints\": [\"\"\ + ],\n \"storageType\":\"S3\",\n \"s3\": {\n \"path\": \"\"\ + ,\n \"awsSecret\": \"\"\n }\n }\n }\n]\n" + capabilities: Full Lifecycle + categories: Database, Big Data + containerImage: quay.io/coreos/etcd-operator@sha256:66a37fd61a06a43969854ee6d3e21087a98b93838e284a6086b13917f96b0d9b + createdAt: 2019-02-28 01:03:00 + description: Create and maintain highly-available etcd clusters on Kubernetes + repository: https://github.com/coreos/etcd-operator + tectonic-visibility: ocs + name: etcdoperator.v0.9.4 + namespace: placeholder +spec: + customresourcedefinitions: + owned: + - description: Represents a cluster of etcd nodes. + displayName: etcd Cluster + kind: EtcdCluster + name: etcdclusters.etcd.database.coreos.com + resources: + - kind: Service + version: v1 + - kind: Pod + version: v1 + specDescriptors: + - description: The desired number of member Pods for the etcd cluster. + displayName: Size + path: size + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:podCount + - description: Limits describes the minimum/maximum amount of compute resources + required/allowed + displayName: Resource Requirements + path: pod.resources + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:resourceRequirements + statusDescriptors: + - description: The status of each of the member Pods for the etcd cluster. + displayName: Member Status + path: members + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:podStatuses + - description: The service at which the running etcd cluster can be accessed. + displayName: Service + path: serviceName + x-descriptors: + - urn:alm:descriptor:io.kubernetes:Service + - description: The current size of the etcd cluster. + displayName: Cluster Size + path: size + - description: The current version of the etcd cluster. + displayName: Current Version + path: currentVersion + - description: The target version of the etcd cluster, after upgrading. + displayName: Target Version + path: targetVersion + - description: The current status of the etcd cluster. + displayName: Status + path: phase + x-descriptors: + - urn:alm:descriptor:io.kubernetes.phase + - description: Explanation for the current status of the cluster. + displayName: Status Details + path: reason + x-descriptors: + - urn:alm:descriptor:io.kubernetes.phase:reason + version: v1beta2 + - description: Represents the intent to backup an etcd cluster. + displayName: etcd Backup + kind: EtcdBackup + name: etcdbackups.etcd.database.coreos.com + specDescriptors: + - description: Specifies the endpoints of an etcd cluster. + displayName: etcd Endpoint(s) + path: etcdEndpoints + x-descriptors: + - urn:alm:descriptor:etcd:endpoint + - description: The full AWS S3 path where the backup is saved. + displayName: S3 Path + path: s3.path + x-descriptors: + - urn:alm:descriptor:aws:s3:path + - description: The name of the secret object that stores the AWS credential + and config files. + displayName: AWS Secret + path: s3.awsSecret + x-descriptors: + - urn:alm:descriptor:io.kubernetes:Secret + statusDescriptors: + - description: Indicates if the backup was successful. + displayName: Succeeded + path: succeeded + x-descriptors: + - urn:alm:descriptor:text + - description: Indicates the reason for any backup related failures. + displayName: Reason + path: reason + x-descriptors: + - urn:alm:descriptor:io.kubernetes.phase:reason + version: v1beta2 + - description: Represents the intent to restore an etcd cluster from a backup. + displayName: etcd Restore + kind: EtcdRestore + name: etcdrestores.etcd.database.coreos.com + specDescriptors: + - description: References the EtcdCluster which should be restored, + displayName: etcd Cluster + path: etcdCluster.name + x-descriptors: + - urn:alm:descriptor:io.kubernetes:EtcdCluster + - urn:alm:descriptor:text + - description: The full AWS S3 path where the backup is saved. + displayName: S3 Path + path: s3.path + x-descriptors: + - urn:alm:descriptor:aws:s3:path + - description: The name of the secret object that stores the AWS credential + and config files. + displayName: AWS Secret + path: s3.awsSecret + x-descriptors: + - urn:alm:descriptor:io.kubernetes:Secret + statusDescriptors: + - description: Indicates if the restore was successful. + displayName: Succeeded + path: succeeded + x-descriptors: + - urn:alm:descriptor:text + - description: Indicates the reason for any restore related failures. + displayName: Reason + path: reason + x-descriptors: + - urn:alm:descriptor:io.kubernetes.phase:reason + version: v1beta2 + description: "The etcd Operater creates and maintains highly-available etcd clusters\ + \ on Kubernetes, allowing engineers to easily deploy and manage etcd clusters\ + \ for their applications.\n\netcd is a distributed key value store that provides\ + \ a reliable way to store data across a cluster of machines. It\xE2\u20AC\u2122\ + s open-source and available on GitHub. etcd gracefully handles leader elections\ + \ during network partitions and will tolerate machine failure, including the leader.\n\ + \n\n### Reading and writing to etcd\n\nCommunicate with etcd though its command\ + \ line utility `etcdctl` via port forwarding:\n\n $ kubectl --namespace default\ + \ port-forward service/example-client 2379:2379\n $ etcdctl --endpoints http://127.0.0.1:2379\ + \ get /\n\nOr directly to the API using the automatically generated Kubernetes\ + \ Service:\n\n $ etcdctl --endpoints http://example-client.default.svc:2379\ + \ get /\n\nBe sure to secure your etcd cluster (see Common Configurations) before\ + \ exposing it outside of the namespace or cluster.\n\n\n### Supported Features\n\ + \n* **High availability** - Multiple instances of etcd are networked together\ + \ and secured. Individual failures or networking issues are transparently handled\ + \ to keep your cluster up and running.\n\n* **Automated updates** - Rolling out\ + \ a new etcd version works like all Kubernetes rolling updates. Simply declare\ + \ the desired version, and the etcd service starts a safe rolling update to the\ + \ new version automatically.\n\n* **Backups included** - Create etcd backups and\ + \ restore them through the etcd Operator.\n\n### Common Configurations\n\n* **Configure\ + \ TLS** - Specify [static TLS certs](https://github.com/coreos/etcd-operator/blob/master/doc/user/cluster_tls.md)\ + \ as Kubernetes secrets.\n\n* **Set Node Selector and Affinity** - [Spread your\ + \ etcd Pods](https://github.com/coreos/etcd-operator/blob/master/doc/user/spec_examples.md#three-member-cluster-with-node-selector-and-anti-affinity-across-nodes)\ + \ across Nodes and availability zones.\n\n* **Set Resource Limits** - [Set the\ + \ Kubernetes limit and request](https://github.com/coreos/etcd-operator/blob/master/doc/user/spec_examples.md#three-member-cluster-with-resource-requirement)\ + \ values for your etcd Pods.\n\n* **Customize Storage** - [Set a custom StorageClass](https://github.com/coreos/etcd-operator/blob/master/doc/user/spec_examples.md#custom-persistentvolumeclaim-definition)\ + \ that you would like to use.\n" + displayName: etcd + install: + spec: + deployments: + - name: etcd-operator + spec: + replicas: 1 + selector: + matchLabels: + name: etcd-operator-alm-owned + template: + metadata: + labels: + name: etcd-operator-alm-owned + name: etcd-operator-alm-owned + spec: + containers: + - command: + - etcd-operator + - --create-crd=false + env: + - name: MY_POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + image: quay.io/coreos/etcd-operator@sha256:66a37fd61a06a43969854ee6d3e21087a98b93838e284a6086b13917f96b0d9b + name: etcd-operator + - command: + - etcd-backup-operator + - --create-crd=false + env: + - name: MY_POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + image: quay.io/coreos/etcd-operator@sha256:66a37fd61a06a43969854ee6d3e21087a98b93838e284a6086b13917f96b0d9b + name: etcd-backup-operator + - command: + - etcd-restore-operator + - --create-crd=false + env: + - name: MY_POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + image: quay.io/coreos/etcd-operator@sha256:66a37fd61a06a43969854ee6d3e21087a98b93838e284a6086b13917f96b0d9b + name: etcd-restore-operator + serviceAccountName: etcd-operator + permissions: + - rules: + - apiGroups: + - etcd.database.coreos.com + resources: + - etcdclusters + - etcdbackups + - etcdrestores + verbs: + - '*' + - apiGroups: + - '' + resources: + - pods + - services + - endpoints + - persistentvolumeclaims + - events + verbs: + - '*' + - apiGroups: + - apps + resources: + - deployments + verbs: + - '*' + - apiGroups: + - '' + resources: + - secrets + verbs: + - get + serviceAccountName: etcd-operator + strategy: deployment + installModes: + - supported: true + type: OwnNamespace + - supported: true + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: false + type: AllNamespaces + keywords: + - etcd + - key value + - database + - coreos + - open source + labels: + alm-owner-etcd: etcdoperator + operated-by: etcdoperator + links: + - name: Blog + url: https://coreos.com/etcd + - name: Documentation + url: https://coreos.com/operators/etcd/docs/latest/ + - name: etcd Operator Source Code + url: https://github.com/coreos/etcd-operator + maintainers: + - email: etcd-dev@googlegroups.com + name: etcd Community + maturity: alpha + provider: + name: CNCF + replaces: etcdoperator.v0.9.2 + selector: + matchLabels: + alm-owner-etcd: etcdoperator + operated-by: etcdoperator + version: 0.9.4 diff --git a/pkg/validation/internal/testdata/valid_bundle_no_icon/etcdrestores.etcd.database.coreos.com.crd.yaml b/pkg/validation/internal/testdata/valid_bundle_no_icon/etcdrestores.etcd.database.coreos.com.crd.yaml new file mode 100644 index 000000000..5b851cd12 --- /dev/null +++ b/pkg/validation/internal/testdata/valid_bundle_no_icon/etcdrestores.etcd.database.coreos.com.crd.yaml @@ -0,0 +1,13 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: etcdrestores.etcd.database.coreos.com +spec: + group: etcd.database.coreos.com + names: + kind: EtcdRestore + listKind: EtcdRestoreList + plural: etcdrestores + singular: etcdrestore + scope: Namespaced + version: v1beta2