@@ -27,14 +27,15 @@ import (
27
27
"strings"
28
28
29
29
"k8s.io/apimachinery/pkg/api/meta"
30
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
30
31
"k8s.io/apimachinery/pkg/runtime"
31
32
"k8s.io/apimachinery/pkg/runtime/serializer/json"
32
33
"k8s.io/apimachinery/pkg/types"
33
34
"k8s.io/apimachinery/pkg/util/diff"
34
35
"k8s.io/client-go/rest"
35
36
"sigs.k8s.io/controller-runtime/pkg/client"
37
+ "sigs.k8s.io/controller-runtime/pkg/envtest"
36
38
"sigs.k8s.io/controller-runtime/pkg/manager"
37
- "sigs.k8s.io/controller-runtime/pkg/scheme"
38
39
"sigs.k8s.io/kubebuilder-declarative-pattern/mockkubeapiserver"
39
40
"sigs.k8s.io/kubebuilder-declarative-pattern/pkg/patterns/addon"
40
41
"sigs.k8s.io/kubebuilder-declarative-pattern/pkg/patterns/addon/pkg/loaders"
@@ -61,37 +62,59 @@ type T interface {
61
62
TempDir () string
62
63
}
63
64
64
- func NewValidator (t T , b * scheme.Builder ) * validator {
65
+ type AddToSchemeFunc func (s * runtime.Scheme ) error
66
+
67
+ func NewValidator (t T , env * envtest.Environment , addToSchemeFuncs ... AddToSchemeFunc ) * validator {
68
+ ctx := context .TODO ()
69
+ ctx , cancel := context .WithCancel (ctx )
70
+
65
71
v := & validator {T : t , scheme : runtime .NewScheme ()}
66
- if err := b .AddToScheme (v .scheme ); err != nil {
67
- t .Fatalf ("error from AddToScheme: %v" , err )
72
+ for _ , addToSchemeFunc := range addToSchemeFuncs {
73
+ if err := addToSchemeFunc (v .scheme ); err != nil {
74
+ t .Fatalf ("error from AddToScheme: %v" , err )
75
+ }
68
76
}
69
77
70
78
v .T .Helper ()
71
79
addon .Init ()
72
80
v .findChannelsPath ()
73
81
74
- k8s , err := mockkubeapiserver .NewMockKubeAPIServer (":0" )
75
- if err != nil {
76
- t .Fatalf ("error building mock kube-apiserver: %v" , err )
77
- }
82
+ useEnvtest := true
83
+ var restConfig * rest.Config
84
+ if useEnvtest {
85
+ rc , err := env .Start ()
86
+ if err != nil {
87
+ t .Fatalf ("failed to start envtest kube-apiserver: %v" , err )
88
+ }
89
+ restConfig = rc
90
+ t .Cleanup (func () {
91
+ if err := env .Stop (); err != nil {
92
+ t .Errorf ("error stopping envtest: %v" , err )
93
+ }
94
+ })
78
95
79
- addr , err := k8s . StartServing ()
80
- if err != nil {
81
- t . Errorf ( "error starting mock kube-apiserver: %v" , err )
82
- }
83
- v . k8s = k8s
96
+ } else {
97
+ k8s , err := mockkubeapiserver . NewMockKubeAPIServer ( ":0" )
98
+ if err != nil {
99
+ t . Fatalf ( "error building mock kube-apiserver: %v" , err )
100
+ }
84
101
85
- t . Cleanup ( func () {
86
- if err := k8s . Stop (); err != nil {
87
- t .Errorf ("error stopping mock kube-apiserver: %v" , err )
102
+ addr , err := k8s . StartServing ()
103
+ if err != nil {
104
+ t .Errorf ("error starting mock kube-apiserver: %v" , err )
88
105
}
89
- })
106
+ v . k8s = k8s
90
107
91
- restConfig := & rest.Config {
92
- Host : addr .String (),
93
- }
108
+ t .Cleanup (func () {
109
+ if err := k8s .Stop (); err != nil {
110
+ t .Errorf ("error stopping mock kube-apiserver: %v" , err )
111
+ }
112
+ })
94
113
114
+ restConfig = & rest.Config {
115
+ Host : addr .String (),
116
+ }
117
+ }
95
118
mgr , err := manager .New (restConfig , manager.Options {
96
119
Scheme : v .scheme ,
97
120
})
@@ -100,6 +123,25 @@ func NewValidator(t T, b *scheme.Builder) *validator {
100
123
}
101
124
v .client = mgr .GetClient ()
102
125
v .mgr = mgr
126
+
127
+ managerError := make (chan error )
128
+ go func () {
129
+ err := v .mgr .Start (ctx )
130
+ managerError <- err
131
+ }()
132
+
133
+ // Wait for the manager to start
134
+ if ! v .mgr .GetCache ().WaitForCacheSync (ctx ) {
135
+ t .Fatalf ("error waiting for cache sync" )
136
+ }
137
+
138
+ t .Cleanup (func () {
139
+ // Cancel the context so the manager exits
140
+ cancel ()
141
+ // Wait for manager to exit
142
+ <- managerError
143
+ })
144
+
103
145
return v
104
146
}
105
147
@@ -186,7 +228,13 @@ func (v *validator) Client() client.Client {
186
228
return v .client
187
229
}
188
230
189
- func (v * validator ) Validate (r declarative.Reconciler ) {
231
+ type ValidateOptions struct {
232
+ RewriteObjects func (o * unstructured.Unstructured )
233
+ }
234
+
235
+ func (v * validator ) Validate (r * declarative.Reconciler , options ValidateOptions ) {
236
+ ctx := context .TODO ()
237
+
190
238
t := v .T
191
239
t .Helper ()
192
240
@@ -205,8 +253,6 @@ func (v *validator) Validate(r declarative.Reconciler) {
205
253
t .Fatalf ("error reading dir %s: %v" , basedir , err )
206
254
}
207
255
208
- ctx := context .TODO ()
209
-
210
256
for _ , f := range files {
211
257
p := filepath .Join (basedir , f .Name ())
212
258
t .Logf ("Filepath: %s" , p )
@@ -217,7 +263,7 @@ func (v *validator) Validate(r declarative.Reconciler) {
217
263
}
218
264
219
265
if strings .HasSuffix (p , "~" ) {
220
- // Ignore editor temp files (for sanity )
266
+ // Ignore editor temp files (this makes development easier )
221
267
t .Logf ("ignoring editor temp file %s" , p )
222
268
continue
223
269
}
@@ -258,6 +304,7 @@ func (v *validator) Validate(r declarative.Reconciler) {
258
304
if err := v .client .Create (ctx , obj ); err != nil {
259
305
t .Errorf ("error creating resource in %s: %v" , p , err )
260
306
}
307
+ t .Logf ("created object %v %v/%v" , obj .GetObjectKind ().GroupVersionKind ().Kind , obj .GetNamespace (), obj .GetName ())
261
308
objectsToCleanup = append (objectsToCleanup , obj )
262
309
}
263
310
}
@@ -291,6 +338,15 @@ func (v *validator) Validate(r declarative.Reconciler) {
291
338
continue
292
339
}
293
340
341
+ {
342
+ obj := cr .(client.Object )
343
+ if err := v .client .Create (ctx , obj ); err != nil {
344
+ t .Errorf ("error creating resource in %s: %v" , p , err )
345
+ }
346
+ t .Logf ("created object %v %v/%v" , obj .GetObjectKind ().GroupVersionKind ().Kind , obj .GetNamespace (), obj .GetName ())
347
+ objectsToCleanup = append (objectsToCleanup , obj )
348
+ }
349
+
294
350
namespace , err := metadataAccessor .Namespace (cr )
295
351
if err != nil {
296
352
t .Errorf ("error getting namespace in %s: %v" , p , err )
@@ -324,6 +380,9 @@ func (v *validator) Validate(r declarative.Reconciler) {
324
380
b .WriteString ("---\n " )
325
381
}
326
382
u := o .UnstructuredObject ()
383
+ if options .RewriteObjects != nil {
384
+ options .RewriteObjects (u )
385
+ }
327
386
if err := yamlizer .Encode (u , & b ); err != nil {
328
387
t .Fatalf ("error encoding to yaml: %v" , err )
329
388
}
@@ -336,15 +395,19 @@ func (v *validator) Validate(r declarative.Reconciler) {
336
395
{
337
396
b , err := os .ReadFile (expectedPath )
338
397
if err != nil {
339
- t .Errorf ("error reading file %s: %v" , expectedPath , err )
340
- continue
398
+ if os .IsNotExist (err ) && ShouldWriteGoldenOutput (t ) {
399
+ // We'll create the file below
400
+ } else {
401
+ t .Errorf ("error reading file %s: %v" , expectedPath , err )
402
+ continue
403
+ }
341
404
}
342
405
expectedYAML = string (b )
343
406
}
344
407
345
408
if actualYAML != expectedYAML {
346
- if os . Getenv ( "HACK_AUTOFIX_EXPECTED_OUTPUT" ) != "" {
347
- t .Logf ("HACK_AUTOFIX_EXPECTED_OUTPUT is set; replacing expected output in %s" , expectedPath )
409
+ if ShouldWriteGoldenOutput ( t ) {
410
+ t .Logf ("WRITE_GOLDEN_OUTPUT is set; replacing expected output in %s" , expectedPath )
348
411
if err := os .WriteFile (expectedPath , []byte (actualYAML ), 0644 ); err != nil {
349
412
t .Fatalf ("error writing expected output to %s: %v" , expectedPath , err )
350
413
}
@@ -357,15 +420,27 @@ func (v *validator) Validate(r declarative.Reconciler) {
357
420
}
358
421
359
422
t .Errorf ("unexpected diff between actual and expected YAML. See previous output for details." )
360
- t .Logf (`To regenerate the output based on this result, rerun this test with HACK_AUTOFIX_EXPECTED_OUTPUT ="true"` )
423
+ t .Logf (`To regenerate the output based on this result, rerun this test with WRITE_GOLDEN_OUTPUT ="true"` )
361
424
}
362
425
363
426
for _ , objectToCleanup := range objectsToCleanup {
364
427
if err := v .client .Delete (ctx , objectToCleanup ); err != nil {
365
428
t .Errorf ("error deleting object: %v" , err )
366
429
}
367
430
}
431
+
432
+ }
433
+ }
434
+
435
+ func ShouldWriteGoldenOutput (t T ) bool {
436
+ if os .Getenv ("HACK_AUTOFIX_EXPECTED_OUTPUT" ) != "" {
437
+ t .Logf ("HACK_AUTOFIX_EXPECTED_OUTPUT is set, please switch to use WRITE_GOLDEN_OUTPUT. This may be an test failure in future versions." )
438
+ return true
439
+ }
440
+ if os .Getenv ("WRITE_GOLDEN_OUTPUT" ) != "" {
441
+ return true
368
442
}
443
+ return false
369
444
}
370
445
371
446
func diffFiles (t T , expectedPath , actual string ) error {
0 commit comments