diff --git a/pkg/validation/internal/csv.go b/pkg/validation/internal/csv.go index ea7145d07..74576b05e 100644 --- a/pkg/validation/internal/csv.go +++ b/pkg/validation/internal/csv.go @@ -1,6 +1,7 @@ package internal import ( + "encoding/json" "fmt" "io" "reflect" @@ -97,6 +98,12 @@ func validateExamplesAnnotations(csv *v1alpha1.ClusterServiceVersion) (errs []er } else { examplesString = olmExamples } + + if err := validateJSON(examplesString); err != nil { + errs = append(errs, errors.ErrInvalidParse("invalid example", err)) + return errs + } + us := []unstructured.Unstructured{} dec := yaml.NewYAMLOrJSONDecoder(strings.NewReader(examplesString), 8) if err := dec.Decode(&us); err != nil && err != io.EOF { @@ -115,6 +122,26 @@ func validateExamplesAnnotations(csv *v1alpha1.ClusterServiceVersion) (errs []er return errs } +func validateJSON(value string) error { + var js json.RawMessage + byteValue := []byte(value) + if err := json.Unmarshal(byteValue, &js); err != nil { + switch t := err.(type) { + case *json.SyntaxError: + jsn := string(byteValue[0:t.Offset]) + jsn += "<--(see the invalid character)" + return fmt.Errorf("invalid character at %v\n %s", t.Offset, jsn) + case *json.UnmarshalTypeError: + jsn := string(byteValue[0:t.Offset]) + jsn += "<--(see the invalid type)" + return fmt.Errorf("invalid value at %v\n %s", t.Offset, jsn) + default: + return err + } + } + return nil +} + func getProvidedAPIs(csv *v1alpha1.ClusterServiceVersion) (provided map[schema.GroupVersionKind]struct{}, errs []errors.Error) { provided = map[schema.GroupVersionKind]struct{}{} for _, owned := range csv.Spec.CustomResourceDefinitions.Owned { diff --git a/pkg/validation/internal/csv_test.go b/pkg/validation/internal/csv_test.go index 1c94a4f5e..f3b2a83e4 100644 --- a/pkg/validation/internal/csv_test.go +++ b/pkg/validation/internal/csv_test.go @@ -5,10 +5,9 @@ import ( "path/filepath" "testing" + "github.com/ghodss/yaml" operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" "github.com/operator-framework/api/pkg/validation/errors" - - "github.com/ghodss/yaml" ) func TestValidateCSV(t *testing.T) { @@ -71,6 +70,16 @@ func TestValidateCSV(t *testing.T) { }, filepath.Join("testdata", "badName.csv.yaml"), }, + { + validatorFuncTest{ + description: "should fail when alm-examples is pretty format and is invalid", + wantErr: true, + errors: []errors.Error{ + errors.ErrInvalidParse("invalid example", "invalid character at 176\n [{\"apiVersion\":\"local.storage.openshift.io/v1\",\"kind\":\"LocalVolume\",\"metadata\":{\"name\":\"example\"},\"spec\":{\"storageClassDevices\":[{\"devicePaths\":[\"/dev/disk/by-id/ata-crucial\",]<--(see the invalid character)"), + }, + }, + filepath.Join("testdata", "invalid.alm-examples.csv.yaml"), + }, } for _, c := range cases { b, err := ioutil.ReadFile(c.csvPath) diff --git a/pkg/validation/internal/testdata/invalid.alm-examples.csv.yaml b/pkg/validation/internal/testdata/invalid.alm-examples.csv.yaml new file mode 100644 index 000000000..1d6bfc13b --- /dev/null +++ b/pkg/validation/internal/testdata/invalid.alm-examples.csv.yaml @@ -0,0 +1,34 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: ClusterServiceVersion +metadata: + annotations: + alm-examples: '[{"apiVersion":"local.storage.openshift.io/v1","kind":"LocalVolume","metadata":{"name":"example"},"spec":{"storageClassDevices":[{"devicePaths":["/dev/disk/by-id/ata-crucial",],"fsType": "ext4", "storageClassName": "foobar", "volumeMode": "Filesystem" } ] } }, { "apiVersion": "local.storage.openshift.io/v1alpha1", "kind": "LocalVolumeSet", "metadata": { "name": "example-localvolumeset" }, "spec": { "deviceInclusionSpec": { "deviceMechanicalProperties": [ "Rotational", "NonRotational" ], "deviceTypes": [ "RawDisk" ], "maxSize": "100G", "minSize": "10G" }, "maxDeviceCount": 10, "nodeSelector": { "nodeSelectorTerms": [ { "matchExpressions": [ { "key": "kubernetes.io/hostname", "operator": "In", "values": [ "worker-0", "worker-1" ] } ] } ] }, "storageClassName": "example-storageclass", "volumeMode": "Block" } }, { "apiVersion": "local.storage.openshift.io/v1alpha1", "kind": "LocalVolumeDiscovery", "metadata": { "name": "auto-discover-devices" }, "spec": { "nodeSelector": { "nodeSelectorTerms": [ { "matchExpressions": [ { "key":"kubernetes.io/hostname","operator":"In","values":["worker-0","worker-1"]}]}]}}}]' + capabilities: Basic Install + name: test-operator.v0.0.1 + namespace: placeholder +spec: + displayName: test-operator + install: + strategy: deployment + installModes: + - supported: true + type: OwnNamespace + - supported: true + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces + keywords: + - test-operator + links: + - name: Test Operator + url: https://test-operator.domain + maintainers: + - email: your@email.com + name: Maintainer Name + maturity: alpha + provider: + name: Provider Name + url: https://your.domain + version: 0.0.1