@@ -17,11 +17,12 @@ limitations under the License.
17
17
package webhook
18
18
19
19
import (
20
- "bytes"
21
20
"context"
22
21
"errors"
22
+ "fmt"
23
23
"net/http"
24
24
"net/url"
25
+ "os"
25
26
"path"
26
27
27
28
"github.com/ghodss/yaml"
@@ -35,14 +36,15 @@ import (
35
36
"sigs.k8s.io/controller-runtime/pkg/client"
36
37
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
37
38
"sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert"
39
+ "sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator"
38
40
"sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer"
39
41
"sigs.k8s.io/controller-runtime/pkg/webhook/types"
40
42
)
41
43
42
44
// setDefault does defaulting for the Server.
43
45
func (s * Server ) setDefault () {
44
46
if len (s .Name ) == 0 {
45
- s .Name = "default-admission -server"
47
+ s .Name = "default-k8s-webhook -server"
46
48
}
47
49
if s .registry == nil {
48
50
s .registry = map [string ]Webhook {}
@@ -54,7 +56,10 @@ func (s *Server) setDefault() {
54
56
s .Port = 443
55
57
}
56
58
if len (s .CertDir ) == 0 {
57
- s .CertDir = path .Join ("admission-server" , "cert" )
59
+ s .CertDir = path .Join ("k8s-webhook-server" , "cert" )
60
+ }
61
+ if s .KVMap == nil {
62
+ s .KVMap = map [string ]interface {}{}
58
63
}
59
64
s .setBootstrappingDefault ()
60
65
}
@@ -76,89 +81,125 @@ func (s *Server) setBootstrappingDefault() {
76
81
if s .Service != nil && s .Port != 443 {
77
82
s .Port = 443
78
83
}
79
- if s .CertProvisioner == nil {
80
- s .CertProvisioner = & cert.Provisioner {
81
- Client : s .Client ,
82
- }
84
+ if s .certProvisioner == nil {
85
+ s .certProvisioner = & cert.Provisioner {}
86
+ }
87
+ if s .CertGenerator == nil {
88
+ s .CertGenerator = & generator.SelfSignedCertGenerator {}
89
+ }
90
+ if s .Writer == nil {
91
+ s .Writer = os .Stdout
83
92
}
84
93
}
85
94
86
95
// bootstrap returns the configuration of admissionWebhookConfiguration in yaml format if dryrun is true.
87
96
// Otherwise, it creates the the admissionWebhookConfiguration objects and service if any.
88
97
// It also provisions the certificate for your admission server.
89
- func (s * Server ) bootstrap (dryrun bool ) ([]byte , error ) {
98
+ func (s * Server ) bootstrap (dryrun bool ) error {
99
+ // do defaulting if necessary
90
100
s .once .Do (s .setDefault )
91
101
92
102
var err error
93
103
s .mutatingWebhookConfig , err = s .mutatingWHConfigs ()
94
104
if err != nil {
95
- return nil , err
96
- }
97
- err = s .CertProvisioner .Sync (s .mutatingWebhookConfig )
98
- if err != nil {
99
- return nil , err
105
+ return err
100
106
}
101
107
102
108
s .validatingWebhookConfig , err = s .validatingWHConfigs ()
103
109
if err != nil {
104
- return nil , err
110
+ return err
105
111
}
106
- err = s .CertProvisioner .Sync (s .validatingWebhookConfig )
107
- if err != nil {
108
- return nil , err
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
+ })
109
130
}
131
+ // 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 )
110
135
111
136
svc := s .service ()
112
137
113
- // if dryrun, return the AdmissionWebhookConfiguration in yaml format.
114
138
if dryrun {
115
- return s .genYamlConfig ([]runtime.Object {s .mutatingWebhookConfig , s .validatingWebhookConfig , svc })
139
+ // TODO: print here
140
+ // if dryrun, return the AdmissionWebhookConfiguration in yaml format.
141
+ s .genYamlConfig ([]runtime.Object {s .mutatingWebhookConfig , s .validatingWebhookConfig , svc })
142
+ return nil
116
143
}
117
144
118
145
if s .Client == nil {
119
- return nil , errors .New ("client needs to be set for bootstrapping" )
146
+ return errors .New ("client needs to be set for bootstrapping" )
120
147
}
121
148
122
- err = createOrReplace (s .Client , svc , func (current , desired runtime.Object ) error {
123
- typedC := current .(* corev1.Service )
124
- typedD := desired .(* corev1.Service )
125
- typedC .Spec .Selector = typedD .Spec .Selector
126
- return nil
127
- })
128
- if err != nil {
129
- return nil , err
130
- }
149
+ objects := []runtime.Object {s .mutatingWebhookConfig , s .validatingWebhookConfig , svc }
131
150
132
- err = createOrReplace (s .Client , s .mutatingWebhookConfig , func (current , desired runtime.Object ) error {
133
- typedC := current .(* admissionregistration.MutatingWebhookConfiguration )
134
- typedD := desired .(* admissionregistration.MutatingWebhookConfiguration )
135
- typedC .Webhooks = typedD .Webhooks
136
- return nil
137
- })
138
- if err != nil {
139
- return nil , err
151
+ for _ , obj := range objects {
152
+ err = createOrReplace (s .Client , obj )
153
+ if err != nil {
154
+ return err
155
+ }
140
156
}
157
+ return nil
158
+ }
141
159
142
- err = createOrReplace (s .Client , s .validatingWebhookConfig , func (current , desired runtime.Object ) error {
143
- typedC := current .(* admissionregistration.ValidatingWebhookConfiguration )
144
- typedD := desired .(* admissionregistration.ValidatingWebhookConfiguration )
145
- typedC .Webhooks = typedD .Webhooks
146
- return nil
147
- })
148
- if err != nil {
149
- return nil , err
150
- }
160
+ type mutateFn func (current , desired runtime.Object ) error
151
161
152
- return nil , nil
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
153
167
}
154
168
155
- type mutateFn func (current , desired runtime.Object ) error
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
+
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
+ }
156
182
157
183
// createOrReplace creates the object if it doesn't exist;
158
184
// otherwise, it will replace it.
159
- // When replacing, fn knows how to preserve existing fields in the object GET from the APIServer.
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.
160
201
// TODO: use the helper in #98 when it merges.
161
- func createOrReplace (c client.Client , obj runtime.Object , fn mutateFn ) error {
202
+ func createOrReplaceHelper (c client.Client , obj runtime.Object , fn mutateFn ) error {
162
203
if obj == nil {
163
204
return nil
164
205
}
@@ -183,59 +224,31 @@ func createOrReplace(c client.Client, obj runtime.Object, fn mutateFn) error {
183
224
return err
184
225
}
185
226
186
- // genYamlConfig generates yaml config for runtime.Object
187
- func (s * Server ) genYamlConfig (objs []runtime.Object ) ([]byte , error ) {
188
- var buf bytes.Buffer
189
- firstObject := true
190
- for _ , awc := range objs {
191
- if ! firstObject {
192
- _ , err := buf .WriteString ("---" )
193
- if err != nil {
194
- return nil , err
195
- }
196
- }
197
- firstObject = false
198
- b , err := yaml .Marshal (awc )
227
+ // genYamlConfig generates yaml config for admissionWebhookConfiguration
228
+ func (s * Server ) genYamlConfig (objs []runtime.Object ) error {
229
+ for _ , obj := range objs {
230
+ _ , err := s .Writer .Write ([]byte ("---" ))
199
231
if err != nil {
200
- return nil , err
232
+ return err
201
233
}
202
- _ , err = buf . Write ( b )
234
+ b , err := yaml . Marshal ( obj )
203
235
if err != nil {
204
- return nil , err
205
- }
206
- }
207
- return buf .Bytes (), nil
208
- }
209
-
210
- func (s * Server ) annotations (t types.WebhookType ) map [string ]string {
211
- anno := map [string ]string {}
212
- for _ , webhook := range s .registry {
213
- if webhook .GetType () != t {
214
- continue
236
+ return err
215
237
}
216
- if s .Secret != nil {
217
- key := writer .SecretCertProvisionAnnotationKeyPrefix + webhook .GetName ()
218
- anno [key ] = s .Secret .String ()
219
- } else {
220
- key := writer .FSCertProvisionAnnotationKeyPrefix + webhook .GetName ()
221
- anno [key ] = s .CertDir
238
+ _ , err = s .Writer .Write (b )
239
+ if err != nil {
240
+ return err
222
241
}
223
242
}
224
- return anno
225
- }
226
-
227
- type webhookClientConfig struct {
228
- * admissionregistration.WebhookClientConfig
243
+ return nil
229
244
}
230
245
231
- func (s * Server ) clientConfig () (* webhookClientConfig , error ) {
246
+ func (s * Server ) clientConfig () (* admissionregistration. WebhookClientConfig , error ) {
232
247
if s .Host != nil && s .Service != nil {
233
248
return nil , errors .New ("URL and Service can't be set at the same time" )
234
249
}
235
- cc := & webhookClientConfig {
236
- WebhookClientConfig : & admissionregistration.WebhookClientConfig {
237
- CABundle : []byte {},
238
- },
250
+ cc := & admissionregistration.WebhookClientConfig {
251
+ CABundle : []byte {},
239
252
}
240
253
if s .Host != nil {
241
254
u := url.URL {
@@ -255,18 +268,26 @@ func (s *Server) clientConfig() (*webhookClientConfig, error) {
255
268
return cc , nil
256
269
}
257
270
258
- func (w * webhookClientConfig ) withPath (path string ) error {
259
- if w .URL != nil {
260
- u , err := url .Parse (* w .URL )
271
+ func (s * Server ) clientConfigWithPath (path string ) (* admissionregistration.WebhookClientConfig , error ) {
272
+ cc , err := s .clientConfig ()
273
+ if err != nil {
274
+ return nil , err
275
+ }
276
+ return cc , setPath (cc , path )
277
+ }
278
+
279
+ func setPath (cc * admissionregistration.WebhookClientConfig , path string ) error {
280
+ if cc .URL != nil {
281
+ u , err := url .Parse (* cc .URL )
261
282
if err != nil {
262
283
return err
263
284
}
264
285
u .Path = path
265
286
urlString := u .String ()
266
- w .URL = & urlString
287
+ cc .URL = & urlString
267
288
}
268
- if w .Service != nil {
269
- w .Service .Path = & path
289
+ if cc .Service != nil {
290
+ cc .Service .Path = & path
270
291
}
271
292
return nil
272
293
}
@@ -285,19 +306,22 @@ func (s *Server) mutatingWHConfigs() (runtime.Object, error) {
285
306
if err != nil {
286
307
return nil , err
287
308
}
309
+ cc , err := s .clientConfigWithPath (path )
310
+ if err != nil {
311
+ return nil , err
312
+ }
313
+ wh .ClientConfig = * cc
288
314
mutatingWebhooks = append (mutatingWebhooks , * wh )
289
-
290
315
}
291
316
292
317
if len (mutatingWebhooks ) > 0 {
293
318
return & admissionregistration.MutatingWebhookConfiguration {
294
319
TypeMeta : metav1.TypeMeta {
295
- APIVersion : " admissionregistration.k8s.io/ v1beta1" ,
320
+ APIVersion : fmt . Sprintf ( "%s/%s" , admissionregistration .GroupName , " v1beta1") ,
296
321
Kind : "MutatingWebhookConfiguration" ,
297
322
},
298
323
ObjectMeta : metav1.ObjectMeta {
299
- Name : s .MutatingWebhookConfigName ,
300
- Annotations : s .annotations (types .WebhookTypeMutating ),
324
+ Name : s .MutatingWebhookConfigName ,
301
325
},
302
326
Webhooks : mutatingWebhooks ,
303
327
}, nil
@@ -318,18 +342,22 @@ func (s *Server) validatingWHConfigs() (runtime.Object, error) {
318
342
if err != nil {
319
343
return nil , err
320
344
}
345
+ cc , err := s .clientConfigWithPath (path )
346
+ if err != nil {
347
+ return nil , err
348
+ }
349
+ wh .ClientConfig = * cc
321
350
validatingWebhooks = append (validatingWebhooks , * wh )
322
351
}
323
352
324
353
if len (validatingWebhooks ) > 0 {
325
354
return & admissionregistration.ValidatingWebhookConfiguration {
326
355
TypeMeta : metav1.TypeMeta {
327
- APIVersion : " admissionregistration.k8s.io/ v1beta1" ,
356
+ APIVersion : fmt . Sprintf ( "%s/%s" , admissionregistration .GroupName , " v1beta1") ,
328
357
Kind : "ValidatingWebhookConfiguration" ,
329
358
},
330
359
ObjectMeta : metav1.ObjectMeta {
331
- Name : s .ValidatingWebhookConfigName ,
332
- Annotations : s .annotations (types .WebhookTypeValidating ),
360
+ Name : s .ValidatingWebhookConfigName ,
333
361
},
334
362
Webhooks : validatingWebhooks ,
335
363
}, nil
@@ -353,11 +381,11 @@ func (s *Server) admissionWebhook(path string, wh *admission.Webhook) (*admissio
353
381
if err != nil {
354
382
return nil , err
355
383
}
356
- err = cc . withPath ( path )
384
+ err = setPath ( cc , path )
357
385
if err != nil {
358
386
return nil , err
359
387
}
360
- webhook .ClientConfig = * cc . WebhookClientConfig
388
+ webhook .ClientConfig = * cc
361
389
return webhook , nil
362
390
}
363
391
0 commit comments