@@ -91,6 +91,7 @@ var _ = SIGDescribe("AdmissionWebhook", func() {
91
91
92
92
// Make sure the namespace created for the test is labeled to be selected by the webhooks
93
93
labelNamespace (f , f .Namespace .Name )
94
+ createWebhookConfigurationReadyNamespace (f )
94
95
95
96
ginkgo .By ("Setting up server cert" )
96
97
context = setupServerCert (namespaceName , serviceName )
@@ -150,7 +151,7 @@ var _ = SIGDescribe("AdmissionWebhook", func() {
150
151
defer validatingWebhookCleanup ()
151
152
mutatingWebhookCleanup := registerMutatingWebhookForWebhookConfigurations (f , f .UniqueName + "blocking" , context , servicePort )
152
153
defer mutatingWebhookCleanup ()
153
- testWebhooksForWebhookConfigurations (f , f .UniqueName , servicePort )
154
+ testWebhooksForWebhookConfigurations (f , f .UniqueName , context , servicePort )
154
155
})
155
156
156
157
ginkgo .It ("Should mutate custom resource" , func () {
@@ -683,12 +684,15 @@ func registerWebhook(f *framework.Framework, configName string, context *certCon
683
684
// Server cannot talk to this webhook, so it always fails.
684
685
// Because this webhook is configured fail-open, request should be admitted after the call fails.
685
686
failOpenHook ,
687
+
688
+ // Register a webhook that can be probed by marker requests to detect when the configuration is ready.
689
+ newValidatingIsReadyWebhookFixture (f , context , servicePort ),
686
690
},
687
691
})
688
692
framework .ExpectNoError (err , "registering webhook config %s with namespace %s" , configName , namespace )
689
693
690
- // The webhook configuration is honored in 10s.
691
- time . Sleep ( 10 * time . Second )
694
+ err = waitWebhookConfigurationReady ( f )
695
+ framework . ExpectNoError ( err , "waiting for webhook configuration to be ready" )
692
696
693
697
return func () {
694
698
client .AdmissionregistrationV1 ().ValidatingWebhookConfigurations ().Delete (configName , nil )
@@ -733,12 +737,14 @@ func registerWebhookForAttachingPod(f *framework.Framework, configName string, c
733
737
MatchLabels : map [string ]string {f .UniqueName : "true" },
734
738
},
735
739
},
740
+ // Register a webhook that can be probed by marker requests to detect when the configuration is ready.
741
+ newValidatingIsReadyWebhookFixture (f , context , servicePort ),
736
742
},
737
743
})
738
744
framework .ExpectNoError (err , "registering webhook config %s with namespace %s" , configName , namespace )
739
745
740
- // The webhook configuration is honored in 10s.
741
- time . Sleep ( 10 * time . Second )
746
+ err = waitWebhookConfigurationReady ( f )
747
+ framework . ExpectNoError ( err , "waiting for webhook configuration to be ready" )
742
748
743
749
return func () {
744
750
client .AdmissionregistrationV1 ().ValidatingWebhookConfigurations ().Delete (configName , nil )
@@ -758,12 +764,14 @@ func registerMutatingWebhookForConfigMap(f *framework.Framework, configName stri
758
764
Webhooks : []admissionregistrationv1.MutatingWebhook {
759
765
newMutateConfigMapWebhookFixture (f , context , 1 , servicePort ),
760
766
newMutateConfigMapWebhookFixture (f , context , 2 , servicePort ),
767
+ // Register a webhook that can be probed by marker requests to detect when the configuration is ready.
768
+ newMutatingIsReadyWebhookFixture (f , context , servicePort ),
761
769
},
762
770
})
763
771
framework .ExpectNoError (err , "registering mutating webhook config %s with namespace %s" , configName , namespace )
764
772
765
- // The webhook configuration is honored in 10s.
766
- time . Sleep ( 10 * time . Second )
773
+ err = waitWebhookConfigurationReady ( f )
774
+ framework . ExpectNoError ( err , "waiting for webhook configuration to be ready" )
767
775
return func () { client .AdmissionregistrationV1 ().MutatingWebhookConfigurations ().Delete (configName , nil ) }
768
776
}
769
777
@@ -821,12 +829,14 @@ func registerMutatingWebhookForPod(f *framework.Framework, configName string, co
821
829
MatchLabels : map [string ]string {f .UniqueName : "true" },
822
830
},
823
831
},
832
+ // Register a webhook that can be probed by marker requests to detect when the configuration is ready.
833
+ newMutatingIsReadyWebhookFixture (f , context , servicePort ),
824
834
},
825
835
})
826
836
framework .ExpectNoError (err , "registering mutating webhook config %s with namespace %s" , configName , namespace )
827
837
828
- // The webhook configuration is honored in 10s.
829
- time . Sleep ( 10 * time . Second )
838
+ err = waitWebhookConfigurationReady ( f )
839
+ framework . ExpectNoError ( err , "waiting for webhook configuration to be ready" )
830
840
831
841
return func () { client .AdmissionregistrationV1 ().MutatingWebhookConfigurations ().Delete (configName , nil ) }
832
842
}
@@ -1067,12 +1077,14 @@ func registerFailClosedWebhook(f *framework.Framework, configName string, contex
1067
1077
// Server cannot talk to this webhook, so it always fails.
1068
1078
// Because this webhook is configured fail-closed, request should be rejected after the call fails.
1069
1079
hook ,
1080
+ // Register a webhook that can be probed by marker requests to detect when the configuration is ready.
1081
+ newValidatingIsReadyWebhookFixture (f , context , servicePort ),
1070
1082
},
1071
1083
})
1072
1084
framework .ExpectNoError (err , "registering webhook config %s with namespace %s" , configName , namespace )
1073
1085
1074
- // The webhook configuration is honored in 10s.
1075
- time . Sleep ( 10 * time . Second )
1086
+ err = waitWebhookConfigurationReady ( f )
1087
+ framework . ExpectNoError ( err , "waiting for webhook configuration to be ready" )
1076
1088
return func () {
1077
1089
f .ClientSet .AdmissionregistrationV1 ().ValidatingWebhookConfigurations ().Delete (configName , nil )
1078
1090
}
@@ -1151,12 +1163,14 @@ func registerValidatingWebhookForWebhookConfigurations(f *framework.Framework, c
1151
1163
MatchLabels : map [string ]string {f .UniqueName : "true" },
1152
1164
},
1153
1165
},
1166
+ // Register a webhook that can be probed by marker requests to detect when the configuration is ready.
1167
+ newValidatingIsReadyWebhookFixture (f , context , servicePort ),
1154
1168
},
1155
1169
})
1156
1170
framework .ExpectNoError (err , "registering webhook config %s with namespace %s" , configName , namespace )
1157
1171
1158
- // The webhook configuration is honored in 10s.
1159
- time . Sleep ( 10 * time . Second )
1172
+ err = waitWebhookConfigurationReady ( f )
1173
+ framework . ExpectNoError ( err , "waiting for webhook configuration to be ready" )
1160
1174
return func () {
1161
1175
err := client .AdmissionregistrationV1 ().ValidatingWebhookConfigurations ().Delete (configName , nil )
1162
1176
framework .ExpectNoError (err , "deleting webhook config %s with namespace %s" , configName , namespace )
@@ -1210,12 +1224,14 @@ func registerMutatingWebhookForWebhookConfigurations(f *framework.Framework, con
1210
1224
MatchLabels : map [string ]string {f .UniqueName : "true" },
1211
1225
},
1212
1226
},
1227
+ // Register a webhook that can be probed by marker requests to detect when the configuration is ready.
1228
+ newMutatingIsReadyWebhookFixture (f , context , servicePort ),
1213
1229
},
1214
1230
})
1215
1231
framework .ExpectNoError (err , "registering webhook config %s with namespace %s" , configName , namespace )
1216
1232
1217
- // The webhook configuration is honored in 10s.
1218
- time . Sleep ( 10 * time . Second )
1233
+ err = waitWebhookConfigurationReady ( f )
1234
+ framework . ExpectNoError ( err , "waiting for webhook configuration to be ready" )
1219
1235
return func () {
1220
1236
err := client .AdmissionregistrationV1 ().MutatingWebhookConfigurations ().Delete (configName , nil )
1221
1237
framework .ExpectNoError (err , "deleting webhook config %s with namespace %s" , configName , namespace )
@@ -1225,7 +1241,7 @@ func registerMutatingWebhookForWebhookConfigurations(f *framework.Framework, con
1225
1241
// This test assumes that the deletion-rejecting webhook defined in
1226
1242
// registerValidatingWebhookForWebhookConfigurations and the webhook-config-mutating
1227
1243
// webhook defined in registerMutatingWebhookForWebhookConfigurations already exist.
1228
- func testWebhooksForWebhookConfigurations (f * framework.Framework , configName string , servicePort int32 ) {
1244
+ func testWebhooksForWebhookConfigurations (f * framework.Framework , configName string , context * certContext , servicePort int32 ) {
1229
1245
var err error
1230
1246
client := f .ClientSet
1231
1247
ginkgo .By ("Creating a dummy validating-webhook-configuration object" )
@@ -1271,15 +1287,17 @@ func testWebhooksForWebhookConfigurations(f *framework.Framework, configName str
1271
1287
MatchLabels : map [string ]string {f .UniqueName : "true" },
1272
1288
},
1273
1289
},
1290
+ // Register a webhook that can be probed by marker requests to detect when the configuration is ready.
1291
+ newValidatingIsReadyWebhookFixture (f , context , servicePort ),
1274
1292
},
1275
1293
})
1276
1294
framework .ExpectNoError (err , "registering webhook config %s with namespace %s" , configName , namespace )
1277
1295
if mutatedValidatingWebhookConfiguration .ObjectMeta .Labels != nil && mutatedValidatingWebhookConfiguration .ObjectMeta .Labels [addedLabelKey ] == addedLabelValue {
1278
1296
e2elog .Failf ("expected %s not to be mutated by mutating webhooks but it was" , configName )
1279
1297
}
1280
1298
1281
- // The webhook configuration is honored in 10s.
1282
- time . Sleep ( 10 * time . Second )
1299
+ err = waitWebhookConfigurationReady ( f )
1300
+ framework . ExpectNoError ( err , "waiting for webhook configuration to be ready" )
1283
1301
1284
1302
ginkgo .By ("Deleting the validating-webhook-configuration, which should be possible to remove" )
1285
1303
@@ -1325,15 +1343,17 @@ func testWebhooksForWebhookConfigurations(f *framework.Framework, configName str
1325
1343
MatchLabels : map [string ]string {f .UniqueName : "true" },
1326
1344
},
1327
1345
},
1346
+ // Register a webhook that can be probed by marker requests to detect when the configuration is ready.
1347
+ newMutatingIsReadyWebhookFixture (f , context , servicePort ),
1328
1348
},
1329
1349
})
1330
1350
framework .ExpectNoError (err , "registering webhook config %s with namespace %s" , configName , namespace )
1331
1351
if mutatedMutatingWebhookConfiguration .ObjectMeta .Labels != nil && mutatedMutatingWebhookConfiguration .ObjectMeta .Labels [addedLabelKey ] == addedLabelValue {
1332
1352
e2elog .Failf ("expected %s not to be mutated by mutating webhooks but it was" , configName )
1333
1353
}
1334
1354
1335
- // The webhook configuration is honored in 10s.
1336
- time . Sleep ( 10 * time . Second )
1355
+ err = waitWebhookConfigurationReady ( f )
1356
+ framework . ExpectNoError ( err , "waiting for webhook configuration to be ready" )
1337
1357
1338
1358
ginkgo .By ("Deleting the mutating-webhook-configuration, which should be possible to remove" )
1339
1359
@@ -1542,12 +1562,14 @@ func registerWebhookForCustomResource(f *framework.Framework, configName string,
1542
1562
MatchLabels : map [string ]string {f .UniqueName : "true" },
1543
1563
},
1544
1564
},
1565
+ // Register a webhook that can be probed by marker requests to detect when the configuration is ready.
1566
+ newValidatingIsReadyWebhookFixture (f , context , servicePort ),
1545
1567
},
1546
1568
})
1547
1569
framework .ExpectNoError (err , "registering custom resource webhook config %s with namespace %s" , configName , namespace )
1548
1570
1549
- // The webhook configuration is honored in 10s.
1550
- time . Sleep ( 10 * time . Second )
1571
+ err = waitWebhookConfigurationReady ( f )
1572
+ framework . ExpectNoError ( err , "waiting for webhook configuration to be ready" )
1551
1573
return func () {
1552
1574
client .AdmissionregistrationV1 ().ValidatingWebhookConfigurations ().Delete (configName , nil )
1553
1575
}
@@ -1617,12 +1639,14 @@ func registerMutatingWebhookForCustomResource(f *framework.Framework, configName
1617
1639
MatchLabels : map [string ]string {f .UniqueName : "true" },
1618
1640
},
1619
1641
},
1642
+ // Register a webhook that can be probed by marker requests to detect when the configuration is ready.
1643
+ newMutatingIsReadyWebhookFixture (f , context , servicePort ),
1620
1644
},
1621
1645
})
1622
1646
framework .ExpectNoError (err , "registering custom resource webhook config %s with namespace %s" , configName , namespace )
1623
1647
1624
- // The webhook configuration is honored in 10s.
1625
- time . Sleep ( 10 * time . Second )
1648
+ err = waitWebhookConfigurationReady ( f )
1649
+ framework . ExpectNoError ( err , "waiting for webhook configuration to be ready" )
1626
1650
1627
1651
return func () { client .AdmissionregistrationV1 ().MutatingWebhookConfigurations ().Delete (configName , nil ) }
1628
1652
}
@@ -1819,12 +1843,14 @@ func registerValidatingWebhookForCRD(f *framework.Framework, configName string,
1819
1843
MatchLabels : map [string ]string {f .UniqueName : "true" },
1820
1844
},
1821
1845
},
1846
+ // Register a webhook that can be probed by marker requests to detect when the configuration is ready.
1847
+ newValidatingIsReadyWebhookFixture (f , context , servicePort ),
1822
1848
},
1823
1849
})
1824
1850
framework .ExpectNoError (err , "registering crd webhook config %s with namespace %s" , configName , namespace )
1825
1851
1826
- // The webhook configuration is honored in 10s.
1827
- time . Sleep ( 10 * time . Second )
1852
+ err = waitWebhookConfigurationReady ( f )
1853
+ framework . ExpectNoError ( err , "waiting for webhook configuration to be ready" )
1828
1854
return func () {
1829
1855
client .AdmissionregistrationV1 ().ValidatingWebhookConfigurations ().Delete (configName , nil )
1830
1856
}
@@ -1943,12 +1969,14 @@ func registerSlowWebhook(f *framework.Framework, configName string, context *cer
1943
1969
SideEffects : & sideEffectsNone ,
1944
1970
AdmissionReviewVersions : []string {"v1" , "v1beta1" },
1945
1971
},
1972
+ // Register a webhook that can be probed by marker requests to detect when the configuration is ready.
1973
+ newValidatingIsReadyWebhookFixture (f , context , servicePort ),
1946
1974
},
1947
1975
})
1948
1976
framework .ExpectNoError (err , "registering slow webhook config %s with namespace %s" , configName , namespace )
1949
1977
1950
- // The webhook configuration is honored in 10s.
1951
- time . Sleep ( 10 * time . Second )
1978
+ err = waitWebhookConfigurationReady ( f )
1979
+ framework . ExpectNoError ( err , "waiting for webhook configuration to be ready" )
1952
1980
1953
1981
return func () {
1954
1982
client .AdmissionregistrationV1 ().ValidatingWebhookConfigurations ().Delete (configName , nil )
@@ -2148,3 +2176,117 @@ func newMutateConfigMapWebhookFixture(f *framework.Framework, context *certConte
2148
2176
},
2149
2177
}
2150
2178
}
2179
+
2180
+ // createWebhookConfigurationReadyNamespace creates a separate namespace for webhook configuration ready markers to
2181
+ // prevent cross-talk with webhook configurations being tested.
2182
+ func createWebhookConfigurationReadyNamespace (f * framework.Framework ) {
2183
+ ns , err := f .ClientSet .CoreV1 ().Namespaces ().Create (& v1.Namespace {
2184
+ ObjectMeta : metav1.ObjectMeta {
2185
+ Name : f .Namespace .Name + "-markers" ,
2186
+ Labels : map [string ]string {f .UniqueName + "-markers" : "true" },
2187
+ },
2188
+ })
2189
+ framework .ExpectNoError (err , "creating namespace for webhook configuration ready markers" )
2190
+ f .AddNamespacesToDelete (ns )
2191
+ }
2192
+
2193
+ // waitWebhookConfigurationReady sends "marker" requests until a webhook configuration is ready.
2194
+ // A webhook created with newValidatingIsReadyWebhookFixture or newMutatingIsReadyWebhookFixture should first be added to
2195
+ // the webhook configuration.
2196
+ func waitWebhookConfigurationReady (f * framework.Framework ) error {
2197
+ cmClient := f .ClientSet .CoreV1 ().ConfigMaps (f .Namespace .Name + "-markers" )
2198
+ return wait .PollImmediate (100 * time .Millisecond , 30 * time .Second , func () (bool , error ) {
2199
+ marker := & v1.ConfigMap {
2200
+ ObjectMeta : metav1.ObjectMeta {
2201
+ Name : string (uuid .NewUUID ()),
2202
+ Labels : map [string ]string {
2203
+ f .UniqueName : "true" ,
2204
+ },
2205
+ },
2206
+ }
2207
+ _ , err := cmClient .Create (marker )
2208
+ if err != nil {
2209
+ // The always-deny webhook does not provide a reason, so check for the error string we expect
2210
+ if strings .Contains (err .Error (), "denied" ) {
2211
+ return true , nil
2212
+ }
2213
+ return false , err
2214
+ }
2215
+ // best effort cleanup of markers that are no longer needed
2216
+ _ = cmClient .Delete (marker .GetName (), nil )
2217
+ framework .Logf ("Waiting for webhook configuration to be ready..." )
2218
+ return false , nil
2219
+ })
2220
+ }
2221
+
2222
+ // newValidatingIsReadyWebhookFixture creates a validating webhook that can be added to a webhook configuration and then probed
2223
+ // with "marker" requests via waitWebhookConfigurationReady to wait for a webhook configuration to be ready.
2224
+ func newValidatingIsReadyWebhookFixture (f * framework.Framework , context * certContext , servicePort int32 ) admissionregistrationv1.ValidatingWebhook {
2225
+ sideEffectsNone := admissionregistrationv1 .SideEffectClassNone
2226
+ return admissionregistrationv1.ValidatingWebhook {
2227
+ Name : "validating-is-webhook-configuration-ready.k8s.io" ,
2228
+ Rules : []admissionregistrationv1.RuleWithOperations {{
2229
+ Operations : []admissionregistrationv1.OperationType {admissionregistrationv1 .Create },
2230
+ Rule : admissionregistrationv1.Rule {
2231
+ APIGroups : []string {"" },
2232
+ APIVersions : []string {"v1" },
2233
+ Resources : []string {"configmaps" },
2234
+ },
2235
+ }},
2236
+ ClientConfig : admissionregistrationv1.WebhookClientConfig {
2237
+ Service : & admissionregistrationv1.ServiceReference {
2238
+ Namespace : f .Namespace .Name ,
2239
+ Name : serviceName ,
2240
+ Path : strPtr ("/always-deny" ),
2241
+ Port : pointer .Int32Ptr (servicePort ),
2242
+ },
2243
+ CABundle : context .signingCert ,
2244
+ },
2245
+ SideEffects : & sideEffectsNone ,
2246
+ AdmissionReviewVersions : []string {"v1" , "v1beta1" },
2247
+ // Scope the webhook to just the markers namespace
2248
+ NamespaceSelector : & metav1.LabelSelector {
2249
+ MatchLabels : map [string ]string {f .UniqueName + "-markers" : "true" },
2250
+ },
2251
+ // appease createValidatingWebhookConfiguration isolation requirements
2252
+ ObjectSelector : & metav1.LabelSelector {
2253
+ MatchLabels : map [string ]string {f .UniqueName : "true" },
2254
+ },
2255
+ }
2256
+ }
2257
+
2258
+ // newMutatingIsReadyWebhookFixture creates a mutating webhook that can be added to a webhook configuration and then probed
2259
+ // with "marker" requests via waitWebhookConfigurationReady to wait for a webhook configuration to be ready.
2260
+ func newMutatingIsReadyWebhookFixture (f * framework.Framework , context * certContext , servicePort int32 ) admissionregistrationv1.MutatingWebhook {
2261
+ sideEffectsNone := admissionregistrationv1 .SideEffectClassNone
2262
+ return admissionregistrationv1.MutatingWebhook {
2263
+ Name : "mutating-is-webhook-configuration-ready.k8s.io" ,
2264
+ Rules : []admissionregistrationv1.RuleWithOperations {{
2265
+ Operations : []admissionregistrationv1.OperationType {admissionregistrationv1 .Create },
2266
+ Rule : admissionregistrationv1.Rule {
2267
+ APIGroups : []string {"" },
2268
+ APIVersions : []string {"v1" },
2269
+ Resources : []string {"configmaps" },
2270
+ },
2271
+ }},
2272
+ ClientConfig : admissionregistrationv1.WebhookClientConfig {
2273
+ Service : & admissionregistrationv1.ServiceReference {
2274
+ Namespace : f .Namespace .Name ,
2275
+ Name : serviceName ,
2276
+ Path : strPtr ("/always-deny" ),
2277
+ Port : pointer .Int32Ptr (servicePort ),
2278
+ },
2279
+ CABundle : context .signingCert ,
2280
+ },
2281
+ SideEffects : & sideEffectsNone ,
2282
+ AdmissionReviewVersions : []string {"v1" , "v1beta1" },
2283
+ // Scope the webhook to just the markers namespace
2284
+ NamespaceSelector : & metav1.LabelSelector {
2285
+ MatchLabels : map [string ]string {f .UniqueName + "-markers" : "true" },
2286
+ },
2287
+ // appease createMutatingWebhookConfiguration isolation requirements
2288
+ ObjectSelector : & metav1.LabelSelector {
2289
+ MatchLabels : map [string ]string {f .UniqueName : "true" },
2290
+ },
2291
+ }
2292
+ }
0 commit comments