-
Notifications
You must be signed in to change notification settings - Fork 75
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Separate Category and Capability Validation (#304)
This commit removes the default set of category and capability validation as a part of the operatorhubio validator. As we have a mechanism for custom category validation, and there is significantly more churn on that specific validation, this commit separates the default operatorhubio validator from a distinct default categories validator. This allows users that want to continue to use the default set of categories to still do so, and if there are custom categories they would like to include they are free to use the dynamic categories validation option instead. This commit also does the same separation for capability validation. There is no implementation of custom capability validation (as there is less churn and no explicit need for that yet) -- adding custom capability validation should be trivial in a future commit. This commit accomplishes this by deprecating the existing validator and creating a v2 version of the operatorhubio validator. Additionally, this commit adds 'Observability' to the category list.
- Loading branch information
1 parent
071829b
commit 73a5934
Showing
11 changed files
with
430 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package internal | ||
|
||
import ( | ||
"github.com/operator-framework/api/pkg/manifests" | ||
"github.com/operator-framework/api/pkg/operators/v1alpha1" | ||
"github.com/operator-framework/api/pkg/validation/errors" | ||
interfaces "github.com/operator-framework/api/pkg/validation/interfaces" | ||
) | ||
|
||
var OperatorHubV2Validator interfaces.Validator = interfaces.ValidatorFunc(validateOperatorHubV2) | ||
|
||
func validateOperatorHubV2(objs ...interface{}) (results []errors.ManifestResult) { | ||
// Obtain the k8s version if informed via the objects an optional | ||
k8sVersion := "" | ||
for _, obj := range objs { | ||
switch obj.(type) { | ||
case map[string]string: | ||
k8sVersion = obj.(map[string]string)[k8sVersionKey] | ||
if len(k8sVersion) > 0 { | ||
break | ||
} | ||
} | ||
} | ||
|
||
for _, obj := range objs { | ||
switch v := obj.(type) { | ||
case *manifests.Bundle: | ||
results = append(results, validateBundleOperatorHubV2(v, k8sVersion)) | ||
} | ||
} | ||
|
||
return results | ||
} | ||
|
||
func validateBundleOperatorHubV2(bundle *manifests.Bundle, k8sVersion string) errors.ManifestResult { | ||
result := errors.ManifestResult{Name: bundle.Name} | ||
|
||
if bundle == nil { | ||
result.Add(errors.ErrInvalidBundle("Bundle is nil", nil)) | ||
return result | ||
} | ||
|
||
if bundle.CSV == nil { | ||
result.Add(errors.ErrInvalidBundle("Bundle csv is nil", bundle.Name)) | ||
return result | ||
} | ||
|
||
csvChecksResult := validateHubCSVSpecV2(*bundle.CSV) | ||
for _, err := range csvChecksResult.errs { | ||
result.Add(errors.ErrInvalidCSV(err.Error(), bundle.CSV.GetName())) | ||
} | ||
for _, warn := range csvChecksResult.warns { | ||
result.Add(errors.WarnInvalidCSV(warn.Error(), bundle.CSV.GetName())) | ||
} | ||
|
||
errs, warns := validateDeprecatedAPIS(bundle, k8sVersion) | ||
for _, err := range errs { | ||
result.Add(errors.ErrFailedValidation(err.Error(), bundle.CSV.GetName())) | ||
} | ||
for _, warn := range warns { | ||
result.Add(errors.WarnFailedValidation(warn.Error(), bundle.CSV.GetName())) | ||
} | ||
|
||
return result | ||
} | ||
|
||
// validateHubCSVSpec will check the CSV against the criteria to publish an | ||
// operator bundle in the OperatorHub.io | ||
func validateHubCSVSpecV2(csv v1alpha1.ClusterServiceVersion) CSVChecks { | ||
checks := CSVChecks{csv: csv, errs: []error{}, warns: []error{}} | ||
|
||
checks = checkSpecProviderName(checks) | ||
checks = checkSpecMaintainers(checks) | ||
checks = checkSpecLinks(checks) | ||
checks = checkSpecVersion(checks) | ||
checks = checkSpecIcon(checks) | ||
checks = checkSpecMinKubeVersion(checks) | ||
|
||
return checks | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package internal | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/operator-framework/api/pkg/manifests" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestValidateBundleOperatorHubV2(t *testing.T) { | ||
var table = []struct { | ||
description string | ||
directory string | ||
hasError bool | ||
errStrings []string | ||
}{ | ||
{ | ||
description: "registryv1 bundle/valid bundle", | ||
directory: "./testdata/valid_bundle", | ||
hasError: false, | ||
}, | ||
{ | ||
description: "registryv1 bundle/invald bundle operatorhubio", | ||
directory: "./testdata/invalid_bundle_operatorhub", | ||
hasError: true, | ||
errStrings: []string{ | ||
`Error: Value : (etcdoperator.v0.9.4) csv.Spec.Provider.Name not specified`, | ||
`Error: Value : (etcdoperator.v0.9.4) csv.Spec.Maintainers elements should contain both name and email`, | ||
`Error: Value : (etcdoperator.v0.9.4) csv.Spec.Maintainers email invalidemail is invalid: mail: missing '@' or angle-addr`, | ||
`Error: Value : (etcdoperator.v0.9.4) csv.Spec.Links elements should contain both name and url`, | ||
`Error: Value : (etcdoperator.v0.9.4) csv.Spec.Links url https//coreos.com/operators/etcd/docs/latest/ is invalid: parse "https//coreos.com/operators/etcd/docs/latest/": invalid URI for request`, | ||
`Error: Value : (etcdoperator.v0.9.4) csv.Spec.Icon should only have one element`, | ||
`Error: Value : (etcdoperator.v0.9.4) csv.Spec.Version must be set`, | ||
}, | ||
}, | ||
} | ||
|
||
for _, tt := range table { | ||
// Validate the bundle object | ||
bundle, err := manifests.GetBundleFromDir(tt.directory) | ||
require.NoError(t, err) | ||
|
||
results := OperatorHubV2Validator.Validate(bundle) | ||
|
||
if len(results) > 0 { | ||
require.Equal(t, results[0].HasError(), tt.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) | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package internal | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/operator-framework/api/pkg/manifests" | ||
"github.com/operator-framework/api/pkg/validation/errors" | ||
interfaces "github.com/operator-framework/api/pkg/validation/interfaces" | ||
) | ||
|
||
var StandardCapabilitiesValidator interfaces.Validator = interfaces.ValidatorFunc(validateCapabilities) | ||
|
||
var validCapabilities = map[string]struct{}{ | ||
"Basic Install": {}, | ||
"Seamless Upgrades": {}, | ||
"Full Lifecycle": {}, | ||
"Deep Insights": {}, | ||
"Auto Pilot": {}, | ||
} | ||
|
||
func validateCapabilities(objs ...interface{}) (results []errors.ManifestResult) { | ||
for _, obj := range objs { | ||
switch v := obj.(type) { | ||
case *manifests.Bundle: | ||
results = append(results, validateCapabilitiesBundle(v)) | ||
} | ||
} | ||
|
||
return results | ||
} | ||
|
||
func validateCapabilitiesBundle(bundle *manifests.Bundle) errors.ManifestResult { | ||
result := errors.ManifestResult{Name: bundle.Name} | ||
csvCategoryCheck := CSVChecks{csv: *bundle.CSV, errs: []error{}, warns: []error{}} | ||
|
||
csvChecksResult := checkCapabilities(csvCategoryCheck) | ||
for _, err := range csvChecksResult.errs { | ||
result.Add(errors.ErrInvalidCSV(err.Error(), bundle.CSV.GetName())) | ||
} | ||
for _, warn := range csvChecksResult.warns { | ||
result.Add(errors.WarnInvalidCSV(warn.Error(), bundle.CSV.GetName())) | ||
} | ||
|
||
return result | ||
} | ||
|
||
// checkAnnotations will validate the values informed via annotations such as; capabilities and categories | ||
func checkCapabilities(checks CSVChecks) CSVChecks { | ||
if checks.csv.GetAnnotations() == nil { | ||
checks.csv.SetAnnotations(make(map[string]string)) | ||
} | ||
|
||
if capability, ok := checks.csv.ObjectMeta.Annotations["capabilities"]; ok { | ||
if _, ok := validCapabilities[capability]; !ok { | ||
checks.errs = append(checks.errs, fmt.Errorf("csv.Metadata.Annotations.Capabilities %q is not a valid capabilities level", capability)) | ||
} | ||
} | ||
return checks | ||
} |
Oops, something went wrong.