@@ -17,7 +17,6 @@ limitations under the License.
17
17
package webhook
18
18
19
19
import (
20
- "context"
21
20
"errors"
22
21
"fmt"
23
22
"net/http"
@@ -30,19 +29,24 @@ import (
30
29
"k8s.io/api/admissionregistration/v1beta1"
31
30
admissionregistration "k8s.io/api/admissionregistration/v1beta1"
32
31
corev1 "k8s.io/api/core/v1"
33
- apierrors "k8s.io/apimachinery/pkg/api/errors"
34
32
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
35
33
"k8s.io/apimachinery/pkg/runtime"
36
34
"sigs.k8s.io/controller-runtime/pkg/client"
35
+ "sigs.k8s.io/controller-runtime/pkg/client/config"
37
36
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
38
37
"sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert"
39
- "sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator"
40
38
"sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer"
41
39
"sigs.k8s.io/controller-runtime/pkg/webhook/types"
42
40
)
43
41
44
42
// setDefault does defaulting for the Server.
45
43
func (s * Server ) setDefault () {
44
+ s .setServerDefault ()
45
+ s .setBootstrappingDefault ()
46
+ }
47
+
48
+ // setServerDefault does defaulting for the ServerOptions.
49
+ func (s * Server ) setServerDefault () {
46
50
if len (s .Name ) == 0 {
47
51
s .Name = "default-k8s-webhook-server"
48
52
}
@@ -61,10 +65,22 @@ func (s *Server) setDefault() {
61
65
if s .KVMap == nil {
62
66
s .KVMap = map [string ]interface {}{}
63
67
}
64
- s .setBootstrappingDefault ()
68
+
69
+ if s .Client == nil {
70
+ cfg , err := config .GetConfig ()
71
+ if err != nil {
72
+ s .err = err
73
+ return
74
+ }
75
+ s .Client , err = client .New (cfg , client.Options {})
76
+ if err != nil {
77
+ s .err = err
78
+ return
79
+ }
80
+ }
65
81
}
66
82
67
- // setBootstrappingDefault does defaulting for the Server bootstrapping.
83
+ // setBootstrappingDefault does defaulting for the Server bootstrapping.
68
84
func (s * Server ) setBootstrappingDefault () {
69
85
if len (s .MutatingWebhookConfigName ) == 0 {
70
86
s .MutatingWebhookConfigName = "mutating-webhook-configuration"
@@ -81,147 +97,71 @@ func (s *Server) setBootstrappingDefault() {
81
97
if s .Service != nil && s .Port != 443 {
82
98
s .Port = 443
83
99
}
84
- if s .certProvisioner == nil {
85
- s .certProvisioner = & cert.Provisioner {}
100
+
101
+ var certWriter writer.CertWriter
102
+ var err error
103
+ if s .Secret != nil {
104
+ certWriter , err = writer .NewSecretCertWriter (
105
+ writer.SecretCertWriterOptions {
106
+ Secret : s .Secret ,
107
+ })
108
+ } else {
109
+ certWriter , err = writer .NewFSCertWriter (
110
+ writer.FSCertWriterOptions {
111
+ Path : s .CertDir ,
112
+ })
113
+ }
114
+ if err != nil {
115
+ s .err = err
116
+ return
86
117
}
87
- if s . CertGenerator == nil {
88
- s . CertGenerator = & generator. SelfSignedCertGenerator {}
118
+ s . certProvisioner = & cert. Provisioner {
119
+ CertWriter : certWriter ,
89
120
}
90
121
if s .Writer == nil {
91
122
s .Writer = os .Stdout
92
123
}
93
124
}
94
125
95
- // bootstrap returns the configuration of admissionWebhookConfiguration in yaml format if dryrun is true.
126
+ // bootstrap writes the configuration of admissionWebhookConfiguration in yaml format if dryrun is true.
96
127
// Otherwise, it creates the the admissionWebhookConfiguration objects and service if any.
97
- // It also provisions the certificate for your admission server.
128
+ // It also provisions the certificate for the admission server.
98
129
func (s * Server ) bootstrap (dryrun bool ) error {
99
130
// do defaulting if necessary
100
131
s .once .Do (s .setDefault )
132
+ if s .err != nil {
133
+ return s .err
134
+ }
101
135
102
136
var err error
103
- s .mutatingWebhookConfig , err = s .mutatingWHConfigs ()
137
+ s .webhookConfigurations , err = s .whConfigs ()
104
138
if err != nil {
105
139
return err
106
140
}
141
+ svc := s .service ()
142
+ objects := append (s .webhookConfigurations , svc )
107
143
108
- s . validatingWebhookConfig , err = s .validatingWHConfigs ()
144
+ cc , err : = s .getClientConfig ()
109
145
if err != nil {
110
146
return err
111
147
}
112
-
113
- cc , err := s .clientConfig ()
114
-
115
- s .certProvisioner = & cert.Provisioner {
116
- ClientConfig : cc ,
117
- CertGenerator : s .CertGenerator ,
118
- }
119
-
120
- if s .Secret != nil {
121
- s .certWriter , err = writer .NewSecretCertWriter (
122
- writer.SecretCertWriterOptions {
123
- Secret : s .Secret ,
124
- })
125
- } else {
126
- s .certWriter , err = writer .NewFSCertWriter (
127
- writer.FSCertWriterOptions {
128
- Path : s .CertDir ,
129
- })
130
- }
131
148
// Provision the cert by creating new one or refreshing existing one.
132
- s .certProvisioner .Provision (false )
133
- // Inject the cert into MutatingWebhookConfiguration, ValidatingWebhookConfiguration and ConversionWebhook
134
- s .certProvisioner .Inject (s .mutatingWebhookConfig , s .validatingWebhookConfig )
135
-
136
- svc := s .service ()
149
+ _ , err = s .certProvisioner .Provision (cert.Options {
150
+ ClientConfig : cc ,
151
+ Objects : s .webhookConfigurations ,
152
+ Dryrun : dryrun ,
153
+ })
154
+ if err != nil {
155
+ return err
156
+ }
137
157
138
158
if dryrun {
139
159
// TODO: print here
140
160
// if dryrun, return the AdmissionWebhookConfiguration in yaml format.
141
- s .genYamlConfig ([]runtime.Object {s .mutatingWebhookConfig , s .validatingWebhookConfig , svc })
142
- return nil
143
- }
144
-
145
- if s .Client == nil {
146
- return errors .New ("client needs to be set for bootstrapping" )
147
- }
148
-
149
- objects := []runtime.Object {s .mutatingWebhookConfig , s .validatingWebhookConfig , svc }
150
-
151
- for _ , obj := range objects {
152
- err = createOrReplace (s .Client , obj )
153
- if err != nil {
154
- return err
155
- }
161
+ return s .genYamlConfig (objects )
156
162
}
157
- return nil
158
- }
159
-
160
- type mutateFn func (current , desired runtime.Object ) error
161
-
162
- var serviceFn = func (current , desired runtime.Object ) error {
163
- typedC := current .(* corev1.Service )
164
- typedD := desired .(* corev1.Service )
165
- typedC .Spec .Selector = typedD .Spec .Selector
166
- return nil
167
- }
168
-
169
- var mutatingWebhookConfigFn = func (current , desired runtime.Object ) error {
170
- typedC := current .(* admissionregistration.MutatingWebhookConfiguration )
171
- typedD := desired .(* admissionregistration.MutatingWebhookConfiguration )
172
- typedC .Webhooks = typedD .Webhooks
173
- return nil
174
- }
175
163
176
- var validatingWebhookConfigFn = func (current , desired runtime.Object ) error {
177
- typedC := current .(* admissionregistration.ValidatingWebhookConfiguration )
178
- typedD := desired .(* admissionregistration.ValidatingWebhookConfiguration )
179
- typedC .Webhooks = typedD .Webhooks
180
- return nil
181
- }
182
-
183
- // createOrReplace creates the object if it doesn't exist;
184
- // otherwise, it will replace it.
185
- // When replacing, it knows how to preserve existing fields in the object GET from the APIServer.
186
- func createOrReplace (c client.Client , obj runtime.Object ) error {
187
- switch obj .(type ) {
188
- case * admissionregistration.MutatingWebhookConfiguration :
189
- return createOrReplaceHelper (c , obj , mutatingWebhookConfigFn )
190
- case * admissionregistration.ValidatingWebhookConfiguration :
191
- return createOrReplaceHelper (c , obj , validatingWebhookConfigFn )
192
- case * corev1.Service :
193
- return createOrReplaceHelper (c , obj , serviceFn )
194
- }
195
- return nil
196
- }
197
-
198
- // createOrReplaceHelper creates the object if it doesn't exist;
199
- // otherwise, it will replace it.
200
- // When replacing, fn should know how to preserve existing fields in the object GET from the APIServer.
201
- // TODO: use the helper in #98 when it merges.
202
- func createOrReplaceHelper (c client.Client , obj runtime.Object , fn mutateFn ) error {
203
- if obj == nil {
204
- return nil
205
- }
206
- err := c .Create (context .Background (), obj )
207
- if apierrors .IsAlreadyExists (err ) {
208
- // TODO: retry mutiple times with backoff if necessary.
209
- existing := obj .DeepCopyObject ()
210
- objectKey , err := client .ObjectKeyFromObject (obj )
211
- if err != nil {
212
- return err
213
- }
214
- err = c .Get (context .Background (), objectKey , existing )
215
- if err != nil {
216
- return err
217
- }
218
- err = fn (existing , obj )
219
- if err != nil {
220
- return err
221
- }
222
- return c .Update (context .Background (), existing )
223
- }
224
- return err
164
+ return batchCreateOrReplace (s .Client , objects ... )
225
165
}
226
166
227
167
// genYamlConfig generates yaml config for admissionWebhookConfiguration
@@ -243,7 +183,7 @@ func (s *Server) genYamlConfig(objs []runtime.Object) error {
243
183
return nil
244
184
}
245
185
246
- func (s * Server ) clientConfig () (* admissionregistration.WebhookClientConfig , error ) {
186
+ func (s * Server ) getClientConfig () (* admissionregistration.WebhookClientConfig , error ) {
247
187
if s .Host != nil && s .Service != nil {
248
188
return nil , errors .New ("URL and Service can't be set at the same time" )
249
189
}
@@ -268,14 +208,17 @@ func (s *Server) clientConfig() (*admissionregistration.WebhookClientConfig, err
268
208
return cc , nil
269
209
}
270
210
271
- func (s * Server ) clientConfigWithPath (path string ) (* admissionregistration.WebhookClientConfig , error ) {
272
- cc , err := s .clientConfig ()
211
+ // getClientConfigWithPath constructs a WebhookClientConfig based on the server options.
212
+ // It will use path to the set the path in WebhookClientConfig.
213
+ func (s * Server ) getClientConfigWithPath (path string ) (* admissionregistration.WebhookClientConfig , error ) {
214
+ cc , err := s .getClientConfig ()
273
215
if err != nil {
274
216
return nil , err
275
217
}
276
218
return cc , setPath (cc , path )
277
219
}
278
220
221
+ // setPath sets the path in the WebhookClientConfig.
279
222
func setPath (cc * admissionregistration.WebhookClientConfig , path string ) error {
280
223
if cc .URL != nil {
281
224
u , err := url .Parse (* cc .URL )
@@ -294,6 +237,25 @@ func setPath(cc *admissionregistration.WebhookClientConfig, path string) error {
294
237
295
238
// whConfigs creates a mutatingWebhookConfiguration and(or) a validatingWebhookConfiguration based on registry.
296
239
// For the same type of webhook configuration, it generates a webhook entry per endpoint.
240
+ func (s * Server ) whConfigs () ([]runtime.Object , error ) {
241
+ objs := []runtime.Object {}
242
+ mutatingWH , err := s .mutatingWHConfigs ()
243
+ if err != nil {
244
+ return nil , err
245
+ }
246
+ if mutatingWH != nil {
247
+ objs = append (objs , mutatingWH )
248
+ }
249
+ validatingWH , err := s .validatingWHConfigs ()
250
+ if err != nil {
251
+ return nil , err
252
+ }
253
+ if validatingWH != nil {
254
+ objs = append (objs , validatingWH )
255
+ }
256
+ return objs , nil
257
+ }
258
+
297
259
func (s * Server ) mutatingWHConfigs () (runtime.Object , error ) {
298
260
mutatingWebhooks := []v1beta1.Webhook {}
299
261
for path , webhook := range s .registry {
@@ -306,11 +268,6 @@ func (s *Server) mutatingWHConfigs() (runtime.Object, error) {
306
268
if err != nil {
307
269
return nil , err
308
270
}
309
- cc , err := s .clientConfigWithPath (path )
310
- if err != nil {
311
- return nil , err
312
- }
313
- wh .ClientConfig = * cc
314
271
mutatingWebhooks = append (mutatingWebhooks , * wh )
315
272
}
316
273
@@ -342,11 +299,6 @@ func (s *Server) validatingWHConfigs() (runtime.Object, error) {
342
299
if err != nil {
343
300
return nil , err
344
301
}
345
- cc , err := s .clientConfigWithPath (path )
346
- if err != nil {
347
- return nil , err
348
- }
349
- wh .ClientConfig = * cc
350
302
validatingWebhooks = append (validatingWebhooks , * wh )
351
303
}
352
304
@@ -377,11 +329,7 @@ func (s *Server) admissionWebhook(path string, wh *admission.Webhook) (*admissio
377
329
CABundle : []byte {},
378
330
},
379
331
}
380
- cc , err := s .clientConfig ()
381
- if err != nil {
382
- return nil , err
383
- }
384
- err = setPath (cc , path )
332
+ cc , err := s .getClientConfigWithPath (path )
385
333
if err != nil {
386
334
return nil , err
387
335
}
0 commit comments