Skip to content

Commit 1c4cba2

Browse files
✨ chnange: move check bundle name to the Good Practices validator (#238)
1 parent cb9dc0e commit 1c4cba2

File tree

4 files changed

+111
-116
lines changed

4 files changed

+111
-116
lines changed

pkg/validation/internal/good_practices.go

+37-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package internal
22

33
import (
4-
goerrors "errors"
54
"fmt"
5+
"regexp"
66
"strings"
77

8+
goerrors "errors"
9+
"github.com/blang/semver/v4"
10+
811
"github.com/operator-framework/api/pkg/manifests"
912
"github.com/operator-framework/api/pkg/operators/v1alpha1"
1013
operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
@@ -30,6 +33,9 @@ import (
3033
// b) "An Operator shouldn't deploy or manage other operators (such patterns are known as meta or super operators or include CRDs in its Operands). It's the Operator Lifecycle Manager's job to manage the deployment and lifecycle of operators. For further information check Dependency Resolution: https://olm.operatorframework.io/docs/concepts/olm-architecture/dependency-resolution/"
3134
//
3235
// WARNING: if you create CRD's via the reconciliations or via the Operands then, OLM cannot handle CRDs migration and update, validation.
36+
// - The bundle name (CSV.metadata.name) does not follow the naming convention: <operator-name>.v<semver> e.g. memcached-operator.v0.0.1
37+
//
38+
// NOTE: The bundle name must be 63 characters or less because it will be used as k8s ownerref label which only allows max of 63 characters.
3339
var GoodPracticesValidator interfaces.Validator = interfaces.ValidatorFunc(goodPracticesValidator)
3440

3541
func goodPracticesValidator(objs ...interface{}) (results []errors.ManifestResult) {
@@ -60,6 +66,8 @@ func validateGoodPracticesFrom(bundle *manifests.Bundle) errors.ManifestResult {
6066
warns = append(warns, validateCrdDescriptions(bundle.CSV.Spec.CustomResourceDefinitions)...)
6167
warns = append(warns, validateHubChannels(bundle))
6268
warns = append(warns, validateRBACForCRDsWith(bundle.CSV))
69+
warns = append(warns, checkBundleName(bundle.CSV)...)
70+
6371
for _, err := range errs {
6472
if err != nil {
6573
result.Add(errors.ErrFailedValidation(err.Error(), bundle.CSV.GetName()))
@@ -96,6 +104,34 @@ func validateResourceRequests(csv *operatorsv1alpha1.ClusterServiceVersion) (err
96104
return errs, warns
97105
}
98106

107+
// checkBundleName will validate the operator bundle name informed via CSV.metadata.name.
108+
// The motivation for the following check is to ensure that operators authors knows that operator bundles names should
109+
// follow a name and versioning convention
110+
func checkBundleName(csv *operatorsv1alpha1.ClusterServiceVersion) []error {
111+
var warns []error
112+
// Check if is following the semver
113+
re := regexp.MustCompile("([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?(?:\\+[0-9A-Za-z-]+)?$")
114+
match := re.FindStringSubmatch(csv.Name)
115+
116+
if len(match) > 0 {
117+
if _, err := semver.Parse(match[0]); err != nil {
118+
warns = append(warns, fmt.Errorf("csv.metadata.Name %v is not following the versioning "+
119+
"convention (MAJOR.MINOR.PATCH e.g 0.0.1): https://semver.org/", csv.Name))
120+
}
121+
} else {
122+
warns = append(warns, fmt.Errorf("csv.metadata.Name %v is not following the versioning "+
123+
"convention (MAJOR.MINOR.PATCH e.g 0.0.1): https://semver.org/", csv.Name))
124+
}
125+
126+
// Check if its following the name convention
127+
if len(strings.Split(csv.Name, ".v")) != 2 {
128+
warns = append(warns, fmt.Errorf("csv.metadata.Name %v is not following the recommended "+
129+
"naming convention: <operator-name>.v<semver> e.g. memcached-operator.v0.0.1", csv.Name))
130+
}
131+
132+
return warns
133+
}
134+
99135
// validateHubChannels will check the channels. The motivation for the following check is to ensure that operators
100136
// authors knows if their operator bundles are or not respecting the Naming Convention Rules.
101137
// However, the operator authors still able to choose the names as please them.

pkg/validation/internal/good_practices_test.go

+74
Original file line numberDiff line numberDiff line change
@@ -242,3 +242,77 @@ func TestValidateRBACForCRDsWith(t *testing.T) {
242242
})
243243
}
244244
}
245+
246+
func TestCheckBundleName(t *testing.T) {
247+
type args struct {
248+
bundleName string
249+
}
250+
tests := []struct {
251+
name string
252+
args args
253+
wantWarning bool
254+
errStrings []string
255+
warnStrings []string
256+
}{
257+
{
258+
name: "should work with valid bundle name",
259+
args: args{bundleName: "memcached-operator.v0.9.2"},
260+
},
261+
{
262+
name: "should return a warning when the bundle name is not following the convention",
263+
args: args{bundleName: "memcached-operator0.9.2"},
264+
wantWarning: true,
265+
warnStrings: []string{"csv.metadata.Name memcached-operator0.9.2 is not following the recommended naming " +
266+
"convention: <operator-name>.v<semver> e.g. memcached-operator.v0.0.1"},
267+
},
268+
{
269+
name: "should return a warning when the bundle name version is not following semver",
270+
args: args{bundleName: "memcached-operator.v1"},
271+
wantWarning: true,
272+
warnStrings: []string{"csv.metadata.Name memcached-operator.v1 is not following the versioning convention " +
273+
"(MAJOR.MINOR.PATCH e.g 0.0.1): https://semver.org/"},
274+
},
275+
{
276+
name: "should return a warning when the bundle name version is not following semver",
277+
args: args{bundleName: "memcached-operator.v1"},
278+
wantWarning: true,
279+
warnStrings: []string{"csv.metadata.Name memcached-operator.v1 is not following the " +
280+
"versioning convention (MAJOR.MINOR.PATCH e.g 0.0.1): https://semver.org/"},
281+
},
282+
{
283+
name: "should return a warning when the bundle name version is not following semver",
284+
args: args{bundleName: "memcached-operator.v1--1.0"},
285+
wantWarning: true,
286+
warnStrings: []string{"csv.metadata.Name memcached-operator.v1--1.0 is not following the " +
287+
"versioning convention (MAJOR.MINOR.PATCH e.g 0.0.1): https://semver.org/"},
288+
},
289+
{
290+
name: "should return a warning when the bundle name version is not following semver",
291+
args: args{bundleName: "memcached-operator.v1.3"},
292+
wantWarning: true,
293+
warnStrings: []string{"csv.metadata.Name memcached-operator.v1.3 is not following the " +
294+
"versioning convention (MAJOR.MINOR.PATCH e.g 0.0.1): https://semver.org/"},
295+
},
296+
{
297+
name: "should not warning for patch releases",
298+
args: args{bundleName: "memcached-operator.v0.9.2+alpha"},
299+
},
300+
}
301+
for _, tt := range tests {
302+
t.Run(tt.name, func(t *testing.T) {
303+
304+
csv := operatorsv1alpha1.ClusterServiceVersion{}
305+
csv.Name = tt.args.bundleName
306+
result := checkBundleName(&csv)
307+
308+
require.Equal(t, tt.wantWarning, len(result) > 0)
309+
if tt.wantWarning {
310+
require.Equal(t, len(tt.warnStrings), len(result))
311+
for _, w := range result {
312+
wString := w.Error()
313+
require.Contains(t, tt.warnStrings, wString)
314+
}
315+
}
316+
})
317+
}
318+
}

pkg/validation/internal/operatorhub.go

-34
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"net/url"
99
"os"
1010
"path/filepath"
11-
"regexp"
1211
"strings"
1312

1413
"github.com/blang/semver/v4"
@@ -111,10 +110,6 @@ import (
111110
// - If informed ONLY, check if the value csv.Spec.MinKubeVersion is parsable according to semver (https://semver.org/)
112111
// Also, this validator will raise warnings when:
113112
//
114-
// - The bundle name (CSV.metadata.name) does not follow the naming convention: <operator-name>.v<semver> e.g. memcached-operator.v0.0.1
115-
//
116-
// NOTE: The bundle name must be 63 characters or less because it will be used as k8s ownerref label which only allows max of 63 characters.
117-
//
118113
// - The channel names seems are not following the convention https://olm.operatorframework.io/docs/best-practices/channel-naming/
119114
//
120115
// - The usage of the removed APIs on Kubernetes 1.22 is found. More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-22
@@ -230,7 +225,6 @@ func validateHubCSVSpec(csv v1alpha1.ClusterServiceVersion) CSVChecks {
230225
checks = checkSpecVersion(checks)
231226
checks = checkSpecIcon(checks)
232227
checks = checkSpecMinKubeVersion(checks)
233-
checks = checkBundleName(checks)
234228

235229
return checks
236230
}
@@ -241,34 +235,6 @@ type CSVChecks struct {
241235
warns []error
242236
}
243237

244-
// checkBundleName will validate the operator bundle name informed via CSV.metadata.name.
245-
// The motivation for the following check is to ensure that operators authors knows that operator bundles names should
246-
// follow a name and versioning convention
247-
func checkBundleName(checks CSVChecks) CSVChecks {
248-
249-
// Check if is following the semver
250-
re := regexp.MustCompile("([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?(?:\\+[0-9A-Za-z-]+)?$")
251-
match := re.FindStringSubmatch(checks.csv.Name)
252-
253-
if len(match) > 0 {
254-
if _, err := semver.Parse(match[0]); err != nil {
255-
checks.warns = append(checks.warns, fmt.Errorf("csv.metadata.Name %v is not following the versioning "+
256-
"convention (MAJOR.MINOR.PATCH e.g 0.0.1): https://semver.org/", checks.csv.Name))
257-
}
258-
} else {
259-
checks.warns = append(checks.warns, fmt.Errorf("csv.metadata.Name %v is not following the versioning "+
260-
"convention (MAJOR.MINOR.PATCH e.g 0.0.1): https://semver.org/", checks.csv.Name))
261-
}
262-
263-
// Check if its following the name convention
264-
if len(strings.Split(checks.csv.Name, ".v")) < 2 {
265-
checks.warns = append(checks.errs, fmt.Errorf("csv.metadata.Name %v is not following the recommended "+
266-
"naming convention: <operator-name>.v<semver> e.g. memcached-operator.v0.0.1", checks.csv.Name))
267-
}
268-
269-
return checks
270-
}
271-
272238
// checkSpecMinKubeVersion will validate the spec minKubeVersion informed via CSV.spec.minKubeVersion
273239
func checkSpecMinKubeVersion(checks CSVChecks) CSVChecks {
274240
if len(strings.TrimSpace(checks.csv.Spec.MinKubeVersion)) == 0 {

pkg/validation/internal/operatorhub_test.go

-81
Original file line numberDiff line numberDiff line change
@@ -204,87 +204,6 @@ func TestCheckSpecIcon(t *testing.T) {
204204
}
205205
}
206206

207-
func TestCheckBundleName(t *testing.T) {
208-
type args struct {
209-
bundleName string
210-
}
211-
tests := []struct {
212-
name string
213-
args args
214-
wantError bool
215-
wantWarning bool
216-
errStrings []string
217-
warnStrings []string
218-
}{
219-
{
220-
name: "should work with valid bundle name",
221-
args: args{bundleName: "memcached-operator.v0.9.2"},
222-
},
223-
{
224-
name: "should return a warning when the bundle name is not following the convention",
225-
args: args{bundleName: "memcached-operator0.9.2"},
226-
wantWarning: true,
227-
warnStrings: []string{"csv.metadata.Name memcached-operator0.9.2 is not following the recommended naming " +
228-
"convention: <operator-name>.v<semver> e.g. memcached-operator.v0.0.1"},
229-
},
230-
{
231-
name: "should return a warning when the bundle name version is not following semver",
232-
args: args{bundleName: "memcached-operator.v1"},
233-
wantWarning: true,
234-
warnStrings: []string{"csv.metadata.Name memcached-operator.v1 is not following the versioning convention " +
235-
"(MAJOR.MINOR.PATCH e.g 0.0.1): https://semver.org/"},
236-
},
237-
{
238-
name: "should return a warning when the bundle name version is not following semver",
239-
args: args{bundleName: "memcached-operator.v1"},
240-
wantWarning: true,
241-
warnStrings: []string{"csv.metadata.Name memcached-operator.v1 is not following the " +
242-
"versioning convention (MAJOR.MINOR.PATCH e.g 0.0.1): https://semver.org/"},
243-
},
244-
{
245-
name: "should return a warning when the bundle name version is not following semver",
246-
args: args{bundleName: "memcached-operator.v1--1.0"},
247-
wantWarning: true,
248-
warnStrings: []string{"csv.metadata.Name memcached-operator.v1--1.0 is not following the " +
249-
"versioning convention (MAJOR.MINOR.PATCH e.g 0.0.1): https://semver.org/"},
250-
},
251-
{
252-
name: "should return a warning when the bundle name version is not following semver",
253-
args: args{bundleName: "memcached-operator.v1.3"},
254-
wantWarning: true,
255-
warnStrings: []string{"csv.metadata.Name memcached-operator.v1.3 is not following the " +
256-
"versioning convention (MAJOR.MINOR.PATCH e.g 0.0.1): https://semver.org/"},
257-
},
258-
}
259-
for _, tt := range tests {
260-
t.Run(tt.name, func(t *testing.T) {
261-
262-
csv := v1alpha1.ClusterServiceVersion{}
263-
csv.Name = tt.args.bundleName
264-
checks := CSVChecks{csv: csv, errs: []error{}, warns: []error{}}
265-
result := checkBundleName(checks)
266-
267-
require.Equal(t, tt.wantWarning, len(result.warns) > 0)
268-
if tt.wantWarning {
269-
require.Equal(t, len(tt.warnStrings), len(result.warns))
270-
for _, w := range result.warns {
271-
wString := w.Error()
272-
require.Contains(t, tt.warnStrings, wString)
273-
}
274-
}
275-
276-
require.Equal(t, tt.wantError, len(result.errs) > 0)
277-
if tt.wantError {
278-
require.Equal(t, len(tt.errStrings), len(result.errs))
279-
for _, err := range result.errs {
280-
errString := err.Error()
281-
require.Contains(t, tt.errStrings, errString)
282-
}
283-
}
284-
})
285-
}
286-
}
287-
288207
func TestCheckSpecMinKubeVersion(t *testing.T) {
289208
type args struct {
290209
minKubeVersion string

0 commit comments

Comments
 (0)