Skip to content

Commit c762fbf

Browse files
committed
Add componentconfig package testing files
1 parent be4683e commit c762fbf

File tree

8 files changed

+565
-0
lines changed

8 files changed

+565
-0
lines changed

staging/src/k8s.io/component-base/config/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ filegroup(
2424
name = "all-srcs",
2525
srcs = [
2626
":package-srcs",
27+
"//staging/src/k8s.io/component-base/config/testing:all-srcs",
2728
"//staging/src/k8s.io/component-base/config/v1alpha1:all-srcs",
2829
"//staging/src/k8s.io/component-base/config/validation:all-srcs",
2930
],
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
2+
3+
go_library(
4+
name = "go_default_library",
5+
srcs = [
6+
"apigroup.go",
7+
"defaulting.go",
8+
"helpers.go",
9+
"roundtrip.go",
10+
],
11+
importmap = "k8s.io/kubernetes/vendor/k8s.io/component-base/config/testing",
12+
importpath = "k8s.io/component-base/config/testing",
13+
visibility = ["//visibility:public"],
14+
deps = [
15+
"//staging/src/k8s.io/apimachinery/pkg/api/apitesting/naming:go_default_library",
16+
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
17+
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
18+
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
19+
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
20+
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
21+
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
22+
"//vendor/github.com/google/go-cmp/cmp:go_default_library",
23+
],
24+
)
25+
26+
filegroup(
27+
name = "package-srcs",
28+
srcs = glob(["**"]),
29+
tags = ["automanaged"],
30+
visibility = ["//visibility:private"],
31+
)
32+
33+
filegroup(
34+
name = "all-srcs",
35+
srcs = [":package-srcs"],
36+
tags = ["automanaged"],
37+
visibility = ["//visibility:public"],
38+
)
39+
40+
go_test(
41+
name = "go_default_test",
42+
srcs = ["apigroup_test.go"],
43+
embed = [":go_default_library"],
44+
)
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
/*
2+
Copyright 2019 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package testing
18+
19+
import (
20+
"fmt"
21+
"reflect"
22+
"regexp"
23+
"strings"
24+
25+
apinamingtest "k8s.io/apimachinery/pkg/api/apitesting/naming"
26+
"k8s.io/apimachinery/pkg/runtime"
27+
"k8s.io/apimachinery/pkg/runtime/schema"
28+
"k8s.io/apimachinery/pkg/util/errors"
29+
"k8s.io/apimachinery/pkg/util/sets"
30+
)
31+
32+
// APIVersionRegexp is the regular expression that matches with valid apiversion
33+
var APIVersionRegexp = regexp.MustCompile(`^v\d+((alpha|beta){1}\d+)?$`)
34+
35+
// ComponentConfigPackage is used in APIGroup Testing
36+
type ComponentConfigPackage struct {
37+
ComponentName string
38+
GroupName string
39+
SchemeGroupVersion schema.GroupVersion
40+
AddToScheme func(*runtime.Scheme) error
41+
SkipTests sets.String
42+
AllowedTags map[reflect.Type]bool
43+
AllowedNonstandardJSONNames map[reflect.Type]string
44+
}
45+
46+
type testingFunc func(*runtime.Scheme, *ComponentConfigPackage) error
47+
48+
const (
49+
verifyTagNaming = "verifyTagNaming"
50+
verifyGroupNameSuffix = "verifyGroupNameSuffix"
51+
verifyGroupNameMatch = "verifyGroupNameMatch"
52+
verifyCorrectGroupName = "verifyCorrectGroupName"
53+
verifyComponentConfigKindExists = "verifyComponentConfigKindExists"
54+
verifyExternalAPIVersion = "verifyExternalAPIVersion"
55+
verifyInternalAPIVersion = "verifyInternalAPIVersion"
56+
)
57+
58+
var testingFuncs = map[string]testingFunc{
59+
verifyTagNaming: verifyTagNamingFunc,
60+
verifyGroupNameSuffix: verifyGroupNameSuffixFunc,
61+
verifyGroupNameMatch: verifyGroupNameMatchFunc,
62+
verifyCorrectGroupName: verifyCorrectGroupNameFunc,
63+
verifyComponentConfigKindExists: verifyComponentConfigKindExistsFunc,
64+
}
65+
66+
// VerifyExternalTypePackage tests if external component config package is defined correctly
67+
// Test tag naming (json name should match Go name)
68+
// Test that GroupName has the k8s.io suffix
69+
// Test that GroupName == SchemeGroupVersion.GroupName
70+
// Test that the API version follows the right pattern and isn't internal
71+
// Test that addKnownTypes and AddToScheme registers at least one type and doesn't error
72+
// Test that the GroupName is named correctly (based on ComponentName), and there is a {Component}Configuration kind in the scheme
73+
func VerifyExternalTypePackage(pkginfo *ComponentConfigPackage) error {
74+
scheme, err := setup(pkginfo)
75+
if err != nil {
76+
return fmt.Errorf("test setup error: %v", err)
77+
}
78+
extraFns := map[string]testingFunc{
79+
verifyExternalAPIVersion: verifyExternalAPIVersionFunc,
80+
}
81+
return runFuncs(scheme, pkginfo, extraFns)
82+
}
83+
84+
// VerifyInternalTypePackage tests if internal component config package is defined correctly
85+
// Test tag naming (no tags allowed)
86+
// Test that GroupName has the k8s.io suffix
87+
// Test that GroupName == SchemeGroupVersion.GroupName
88+
// API version should be internal
89+
// Test that addKnownTypes and AddToScheme registers at least one type and doesn't error
90+
// Test that the GroupName is named correctly (based on ComponentName), and there is a {Component}Configuration kind in the scheme
91+
func VerifyInternalTypePackage(pkginfo *ComponentConfigPackage) error {
92+
scheme, err := setup(pkginfo)
93+
if err != nil {
94+
return fmt.Errorf("test setup error: %v", err)
95+
}
96+
extraFns := map[string]testingFunc{
97+
verifyInternalAPIVersion: verifyInternalAPIVersionFunc,
98+
}
99+
return runFuncs(scheme, pkginfo, extraFns)
100+
}
101+
102+
func setup(pkginfo *ComponentConfigPackage) (*runtime.Scheme, error) {
103+
if len(pkginfo.ComponentName) == 0 ||
104+
len(pkginfo.GroupName) == 0 ||
105+
pkginfo.SchemeGroupVersion.Empty() ||
106+
pkginfo.AddToScheme == nil {
107+
return nil, fmt.Errorf("invalid argument: not all parameters were passed correctly to the function")
108+
}
109+
110+
scheme := runtime.NewScheme()
111+
if err := pkginfo.AddToScheme(scheme); err != nil {
112+
return nil, fmt.Errorf("AddToScheme must not return an error: %v", err)
113+
}
114+
if len(scheme.AllKnownTypes()) == 0 {
115+
return nil, fmt.Errorf("AddToScheme doesn't register any type")
116+
}
117+
return scheme, nil
118+
}
119+
120+
func runFuncs(scheme *runtime.Scheme, pkginfo *ComponentConfigPackage, extraFns map[string]testingFunc) error {
121+
verifyFns := []testingFunc{}
122+
for name, fn := range testingFuncs {
123+
if pkginfo.SkipTests.Has(name) {
124+
continue
125+
}
126+
verifyFns = append(verifyFns, fn)
127+
}
128+
for name, fn := range extraFns {
129+
if pkginfo.SkipTests.Has(name) {
130+
continue
131+
}
132+
verifyFns = append(verifyFns, fn)
133+
}
134+
errs := []error{}
135+
for _, fn := range verifyFns {
136+
if err := fn(scheme, pkginfo); err != nil {
137+
errs = append(errs, err)
138+
}
139+
}
140+
return errors.NewAggregate(errs)
141+
}
142+
143+
func verifyTagNamingFunc(scheme *runtime.Scheme, pkginfo *ComponentConfigPackage) error {
144+
return apinamingtest.VerifyTagNaming(scheme, pkginfo.AllowedTags, pkginfo.AllowedNonstandardJSONNames)
145+
}
146+
147+
func verifyGroupNameSuffixFunc(scheme *runtime.Scheme, _ *ComponentConfigPackage) error {
148+
return apinamingtest.VerifyGroupNames(scheme, sets.NewString())
149+
}
150+
151+
func verifyGroupNameMatchFunc(_ *runtime.Scheme, pkginfo *ComponentConfigPackage) error {
152+
if pkginfo.GroupName != pkginfo.SchemeGroupVersion.Group {
153+
return fmt.Errorf("GroupName must be equal to SchemeGroupVersion.Group, GroupName: %v,SchemeGroupVersion.Group: %v",
154+
pkginfo.GroupName, pkginfo.SchemeGroupVersion.Group)
155+
}
156+
return nil
157+
}
158+
159+
func verifyCorrectGroupNameFunc(_ *runtime.Scheme, pkginfo *ComponentConfigPackage) error {
160+
desiredGroupName := fmt.Sprintf("%s.config.k8s.io", lowercaseWithoutDashes(pkginfo.ComponentName))
161+
if pkginfo.SchemeGroupVersion.Group != desiredGroupName {
162+
return fmt.Errorf("got GroupName %q, want %q", pkginfo.SchemeGroupVersion.Group, desiredGroupName)
163+
164+
}
165+
return nil
166+
}
167+
168+
func verifyComponentConfigKindExistsFunc(scheme *runtime.Scheme, pkginfo *ComponentConfigPackage) error {
169+
expectedKind := fmt.Sprintf("%sConfiguration", dashesToCapitalCase(pkginfo.ComponentName))
170+
expectedGVK := pkginfo.SchemeGroupVersion.WithKind(expectedKind)
171+
if !scheme.Recognizes(expectedGVK) {
172+
registeredKinds := sets.NewString()
173+
for gvk := range scheme.AllKnownTypes() {
174+
registeredKinds.Insert(gvk.Kind)
175+
}
176+
return fmt.Errorf("Kind %s not registered in the scheme, registered kinds are %v", expectedKind, registeredKinds.List())
177+
}
178+
return nil
179+
}
180+
181+
func verifyExternalAPIVersionFunc(_ *runtime.Scheme, pkginfo *ComponentConfigPackage) error {
182+
if !APIVersionRegexp.MatchString(pkginfo.SchemeGroupVersion.Version) {
183+
return fmt.Errorf("invalid API version %q, must match %q", pkginfo.SchemeGroupVersion.Version, APIVersionRegexp.String())
184+
}
185+
return nil
186+
}
187+
188+
func verifyInternalAPIVersionFunc(_ *runtime.Scheme, pkginfo *ComponentConfigPackage) error {
189+
if pkginfo.SchemeGroupVersion.Version != runtime.APIVersionInternal {
190+
return fmt.Errorf("internal API version must be %q, got %q",
191+
runtime.APIVersionInternal, pkginfo.SchemeGroupVersion.Version)
192+
}
193+
return nil
194+
}
195+
196+
func lowercaseWithoutDashes(str string) string {
197+
return strings.Replace(strings.ToLower(str), "-", "", -1)
198+
}
199+
200+
func dashesToCapitalCase(str string) string {
201+
segments := strings.Split(str, "-")
202+
result := ""
203+
for _, segment := range segments {
204+
result += strings.Title(segment)
205+
}
206+
return result
207+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
Copyright 2019 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package testing
18+
19+
import (
20+
"testing"
21+
)
22+
23+
func TestAPIVersionRegexp(t *testing.T) {
24+
testCases := []struct {
25+
name string
26+
apiversion string
27+
expected bool
28+
}{
29+
{
30+
name: "v1",
31+
apiversion: "v1",
32+
expected: true,
33+
},
34+
{
35+
name: "v1alpha1",
36+
apiversion: "v1alpha1",
37+
expected: true,
38+
},
39+
{
40+
name: "v1beta1",
41+
apiversion: "v1beta1",
42+
expected: true,
43+
},
44+
{
45+
name: "doesn't start with v",
46+
apiversion: "beta1",
47+
expected: false,
48+
},
49+
{
50+
name: "doesn't end with digit",
51+
apiversion: "v1alpha",
52+
expected: false,
53+
},
54+
{
55+
name: "doesn't have digit after v",
56+
apiversion: "valpha1",
57+
expected: false,
58+
},
59+
{
60+
name: "both alpha beta",
61+
apiversion: "v1alpha1beta1",
62+
expected: false,
63+
},
64+
}
65+
for _, tc := range testCases {
66+
t.Run(tc.name, func(t *testing.T) {
67+
actual := APIVersionRegexp.MatchString(tc.apiversion)
68+
if actual != tc.expected {
69+
t.Errorf("APIVersionRegexp expected %v, got %v", tc.expected, actual)
70+
}
71+
})
72+
}
73+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
Copyright 2019 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package testing
18+
19+
import (
20+
"fmt"
21+
"path/filepath"
22+
"testing"
23+
24+
"k8s.io/apimachinery/pkg/runtime"
25+
"k8s.io/apimachinery/pkg/runtime/serializer"
26+
)
27+
28+
// DefaultingTest run defaulting tests for given scheme
29+
func DefaultingTest(t *testing.T, scheme *runtime.Scheme, codecs serializer.CodecFactory) {
30+
tc := GetDefaultingTestCases(scheme)
31+
RunTestsOnYAMLData(t, scheme, tc, codecs)
32+
}
33+
34+
// GetDefaultingTestCases returns defaulting testcases for given scheme
35+
func GetDefaultingTestCases(scheme *runtime.Scheme) []TestCase {
36+
cases := []TestCase{}
37+
for gvk := range scheme.AllKnownTypes() {
38+
beforeDir := fmt.Sprintf("testdata/%s/before", gvk.Kind)
39+
afterDir := fmt.Sprintf("testdata/%s/after", gvk.Kind)
40+
filename := fmt.Sprintf("%s.yaml", gvk.Version)
41+
42+
cases = append(cases, TestCase{
43+
name: fmt.Sprintf("default_%s", gvk.Version),
44+
in: filepath.Join(beforeDir, filename),
45+
inGVK: gvk,
46+
out: filepath.Join(afterDir, filename),
47+
outGV: gvk.GroupVersion(),
48+
})
49+
}
50+
return cases
51+
}

0 commit comments

Comments
 (0)