1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package resourcefixture
16
17 import (
18 "fmt"
19 "io/ioutil"
20 "path"
21 "path/filepath"
22 "strings"
23 "testing"
24
25 testcontroller "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/controller"
26 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/util/repo"
27
28 "github.com/ghodss/yaml"
29 "github.com/golang-collections/go-datastructures/queue"
30 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
31 "k8s.io/apimachinery/pkg/runtime/schema"
32 )
33
34 type TestType string
35
36 const (
37 Unknown TestType = "unknown"
38 Basic TestType = "basic"
39 ContainerAnnotations TestType = "containerannotations"
40 Directives TestType = "directives"
41 ExternalRef TestType = "externalref"
42 SensitiveField TestType = "sensitivefield"
43 IAMExternalOnlyRef TestType = "iamexternalonlyref"
44 IAMMemberReferences TestType = "iammemberreferences"
45 ResourceID TestType = "resourceid"
46 StateAbsentInSpec TestType = "stateabsentinspec"
47 ResourceOverrides TestType = "resourceoverrides"
48 ReconcileIntervalAnnotation TestType = "reconcileintervalannotations"
49 )
50
51 type ResourceFixture struct {
52 GVK schema.GroupVersionKind
53 Name string
54 Create []byte
55 Update []byte
56 Dependencies []byte
57 Type TestType
58 }
59
60
61
62
63
64 func Load(t *testing.T) []ResourceFixture {
65 return LoadWithFilter(t, nil, nil)
66 }
67
68 type LightFilter func(name string, testType TestType) bool
69 type HeavyFilter func(fixture ResourceFixture) bool
70
71
72
73
74
75
76
77 func LoadWithFilter(t *testing.T, lightFilterFunc LightFilter, heavyFilterFunc HeavyFilter) []ResourceFixture {
78 t.Helper()
79 allCases := make([]ResourceFixture, 0)
80 q := queue.New(1)
81 rootDir := getTestDataPath(t)
82 q.Put(rootDir)
83 for !q.Empty() {
84 items, err := q.Get(1)
85 if err != nil {
86 t.Fatalf("error retreiving an item from queue: %v", err)
87 }
88 dir := items[0].(string)
89 fileInfos, err := ioutil.ReadDir(dir)
90 if err != nil {
91 t.Fatalf("error reading directory '%v': %v", dir, err)
92 }
93 testToFileName := make(map[string]string)
94 for _, fi := range fileInfos {
95 if fi.IsDir() {
96 q.Put(path.Join(dir, fi.Name()))
97 continue
98 }
99 if !strings.HasSuffix(fi.Name(), ".yaml") {
100 continue
101 }
102 fileNameNoExt := strings.TrimSuffix(fi.Name(), ".yaml")
103 if value, ok := testToFileName[fileNameNoExt]; ok {
104 t.Fatalf("error, conflicting files for test '%v' in '%v': {%v, %v}", fileNameNoExt, dir, value, fi.Name())
105 }
106 testToFileName[fileNameNoExt] = fi.Name()
107 }
108
109 if createFile, ok := testToFileName["create"]; ok {
110 updateFile := testToFileName["update"]
111 depFile := testToFileName["dependencies"]
112 name := path.Base(dir)
113 testType := parseTestTypeFromPath(t, dir)
114 if lightFilterFunc != nil && !lightFilterFunc(name, testType) {
115 continue
116 }
117 rf := loadResourceFixture(t, name, testType, dir, createFile, updateFile, depFile)
118 if heavyFilterFunc != nil && !heavyFilterFunc(rf) {
119 continue
120 }
121 allCases = append(allCases, rf)
122 }
123 }
124 return allCases
125 }
126
127 func loadResourceFixture(t *testing.T, testName string, testType TestType, dir, createFile, updateFile, depFile string) ResourceFixture {
128 t.Helper()
129 createConfig := testcontroller.ReadFileToBytes(t, path.Join(dir, createFile))
130 gvk, err := readGroupVersionKind(t, createConfig)
131 if err != nil {
132 t.Fatalf("unable to determine GroupVersionKind for test case named %v: %v", testName, err)
133 }
134
135 rf := ResourceFixture{
136 Name: testName,
137 GVK: gvk,
138 Create: createConfig,
139 Type: testType,
140 }
141
142 if updateFile != "" {
143 rf.Update = testcontroller.ReadFileToBytes(t, path.Join(dir, updateFile))
144 }
145 if depFile != "" {
146 rf.Dependencies = testcontroller.ReadFileToBytes(t, path.Join(dir, depFile))
147 }
148 return rf
149 }
150
151 func readGroupVersionKind(t *testing.T, config []byte) (schema.GroupVersionKind, error) {
152 t.Helper()
153 u := &unstructured.Unstructured{}
154 err := yaml.Unmarshal(config, u)
155 if err != nil {
156 return schema.GroupVersionKind{}, fmt.Errorf("error unmarshalling bytes to CRD: %v", err)
157 }
158 return u.GroupVersionKind(), nil
159 }
160
161 func parseTestTypeFromPath(t *testing.T, path string) TestType {
162 t.Helper()
163 switch parseTestDataSubdirFromPath(t, path) {
164 case "basic":
165 return Basic
166 case "containerannotations":
167 return ContainerAnnotations
168 case "directives":
169 return Directives
170 case "externalref":
171 return ExternalRef
172 case "sensitivefield":
173 return SensitiveField
174 case "iamexternalonlyref":
175 return IAMExternalOnlyRef
176 case "iammemberreferences":
177 return IAMMemberReferences
178 case "resourceid":
179 return ResourceID
180 case "stateabsentinspec":
181 return StateAbsentInSpec
182 case "resourceoverrides":
183 return ResourceOverrides
184 case "reconcileintervalannotations":
185 return ReconcileIntervalAnnotation
186 default:
187 t.Fatalf("failed to parse test type for path %v", path)
188 return Unknown
189 }
190 }
191
192 func parseTestDataSubdirFromPath(t *testing.T, path string) string {
193 t.Helper()
194 testDataPath := getTestDataPath(t)
195 pathWithoutTestDataPath := strings.TrimPrefix(path, testDataPath+"/")
196 pathTokens := strings.Split(pathWithoutTestDataPath, "/")
197 return pathTokens[0]
198 }
199
200 func getTestDataPath(t *testing.T) string {
201 t.Helper()
202 packagePath := repo.GetCallerPackagePathOrTestFatal(t)
203 return filepath.Join(packagePath, "testdata")
204 }
205
View as plain text