67
67
{
68
68
Name : crewv1 .GroupVersion .Version ,
69
69
Served : true ,
70
- // At creation v1 will be the storage version.
71
- // During the test v2 will become the storage version .
70
+ // v1 will be the storage version.
71
+ // Reconciler and index will use v2 so we can validate the conversion webhook works .
72
72
Storage : true ,
73
73
Schema : & apiextensionsv1.CustomResourceValidation {
74
74
OpenAPIV3Schema : & apiextensionsv1.JSONSchemaProps {
@@ -91,23 +91,22 @@ var (
91
91
}
92
92
)
93
93
94
- var _ = Describe ("manger.Manager" , func () {
94
+ var _ = Describe ("manger.Manager Start " , func () {
95
95
// This test ensure the Manager starts without running into any deadlocks as it can be very tricky
96
96
// to start health probes, webhooks, caches (including informers) and reconcilers in the right order.
97
97
//
98
98
// To verify this we set up a test environment in the following state:
99
99
// * Ensure Informer sync requires a functioning conversion webhook (and thus readiness probe)
100
100
// * Driver CRD is deployed with v1 as storage version
101
101
// * A Driver CR is created and stored in the v1 version
102
- // * The CRD is updated to make v2 the storage version
103
- // * This ensures every Driver list call goes through conversion.
104
102
// * Setup manager:
105
103
// * Set up health probes
106
- // * Set up a Driver reconciler to verify reconciliation works
107
- // * Set up a conversion webhook which only works if readiness probe succeeds (just like a Kubernetes service)
104
+ // * Set up a Driver v2 reconciler to verify reconciliation works
105
+ // * Set up a conversion webhook which only works if readiness probe succeeds (just like via a Kubernetes service)
108
106
// * Add an index on v2 Driver to ensure we start and wait for an informer during cache.Start (as part of manager.Start)
109
107
// * Note: cache.Start would fail if the conversion webhook doesn't work (which in turn depends on the readiness probe)
110
- Describe ("Start should start all components without deadlock" , func () {
108
+ // * Note: Adding the index for v2 ensures the Driver list call during Informer sync goes through conversion.
109
+ It ("should start all components without deadlock" , func () {
111
110
ctx := ctrl .SetupSignalHandler ()
112
111
113
112
// Set up schema.
@@ -123,6 +122,7 @@ var _ = Describe("manger.Manager", func() {
123
122
CRDs : []* apiextensionsv1.CustomResourceDefinition {driverCRD },
124
123
},
125
124
}
125
+ // Note: The test env configures a conversion webhook on driverCRD during Start.
126
126
cfg , err := env .Start ()
127
127
Expect (err ).NotTo (HaveOccurred ())
128
128
Expect (cfg ).NotTo (BeNil ())
@@ -139,12 +139,6 @@ var _ = Describe("manger.Manager", func() {
139
139
driverV1 .SetNamespace (metav1 .NamespaceDefault )
140
140
Expect (c .Create (ctx , driverV1 )).To (Succeed ())
141
141
142
- // Update driver CRD to make v2 the storage version.
143
- driverCRDV2Storage := driverCRD .DeepCopy ()
144
- driverCRDV2Storage .Spec .Versions [0 ].Storage = false
145
- driverCRDV2Storage .Spec .Versions [0 ].Storage = true
146
- Expect (c .Patch (ctx , driverCRDV2Storage , client .MergeFrom (driverCRD ))).To (Succeed ())
147
-
148
142
// Set up Manager.
149
143
ctrl .SetLogger (zap .New ())
150
144
mgr , err := manager .New (env .Config , manager.Options {
@@ -164,17 +158,18 @@ var _ = Describe("manger.Manager", func() {
164
158
Expect (mgr .AddReadyzCheck ("webhook" , mgr .GetWebhookServer ().StartedChecker ())).To (Succeed ())
165
159
Expect (mgr .AddHealthzCheck ("webhook" , mgr .GetWebhookServer ().StartedChecker ())).To (Succeed ())
166
160
167
- // Set up Driver reconciler.
161
+ // Set up Driver reconciler (using v2) .
168
162
driverReconciler := & DriverReconciler {
169
163
Client : mgr .GetClient (),
170
164
}
171
- Expect (ctrl .NewControllerManagedBy (mgr ).For (& crewv1 .Driver {}).Complete (driverReconciler )).To (Succeed ())
165
+ Expect (ctrl .NewControllerManagedBy (mgr ).For (& crewv2 .Driver {}).Complete (driverReconciler )).To (Succeed ())
172
166
173
167
// Set up a conversion webhook.
174
168
conversionWebhook := createConversionWebhook (mgr )
175
169
mgr .GetWebhookServer ().Register ("/convert" , conversionWebhook )
176
170
177
- // Add an index on v2 Driver.
171
+ // Add an index on Driver (using v2).
172
+ // Note: This triggers the creation of an Informer for Driver v2.
178
173
Expect (mgr .GetCache ().IndexField (ctx , & crewv2.Driver {}, "name" , func (object client.Object ) []string {
179
174
return []string {object .GetName ()}
180
175
})).To (Succeed ())
@@ -187,6 +182,9 @@ var _ = Describe("manger.Manager", func() {
187
182
}()
188
183
189
184
// Verify manager.Start successfully started health probes, webhooks, caches (including informers) and reconcilers.
185
+ // Notes:
186
+ // * The cache will only start successfully if the informer for v2 Driver is synced.
187
+ // * The informer for v2 Driver will only sync if a list on v2 Driver succeeds (which requires a working conversion webhook)
190
188
<- mgr .Elected ()
191
189
192
190
// Verify the reconciler reconciles.
@@ -197,7 +195,8 @@ var _ = Describe("manger.Manager", func() {
197
195
// Verify conversion webhook was called.
198
196
Expect (atomic .LoadUint64 (& conversionWebhook .ConversionCount )).Should (BeNumerically (">" , 0 ))
199
197
200
- // Verify the conversion webhook works.
198
+ // Verify the conversion webhook works by getting the Driver as v1 and v2.
199
+ Expect (c .Get (ctx , client .ObjectKeyFromObject (driverV1 ), driverV1 )).To (Succeed ())
201
200
driverV2 := & unstructured.Unstructured {}
202
201
driverV2 .SetGroupVersionKind (crewv2 .GroupVersion .WithKind ("Driver" ))
203
202
driverV2 .SetName ("driver1" )
@@ -234,6 +233,10 @@ func (r *DriverReconciler) Reconcile(ctx context.Context, req reconcile.Request)
234
233
return reconcile.Result {}, nil
235
234
}
236
235
236
+ // ConversionWebhook is just a shim around the conversion handler from
237
+ // the webhook package. We use it to simulate the behavior of a conversion
238
+ // webhook in a real cluster, i.e. the conversion webhook only works after the
239
+ // controller Pod is ready (the readiness probe is up).
237
240
type ConversionWebhook struct {
238
241
httpClient http.Client
239
242
conversionHandler http.Handler
0 commit comments