Skip to content

Commit e3ce169

Browse files
author
Mengqi Yu
committed
address comments
1 parent 02b41e1 commit e3ce169

File tree

11 files changed

+270
-263
lines changed

11 files changed

+270
-263
lines changed

pkg/webhook/bootstrap.go

Lines changed: 85 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ limitations under the License.
1717
package webhook
1818

1919
import (
20-
"context"
2120
"errors"
2221
"fmt"
2322
"net/http"
@@ -30,19 +29,24 @@ import (
3029
"k8s.io/api/admissionregistration/v1beta1"
3130
admissionregistration "k8s.io/api/admissionregistration/v1beta1"
3231
corev1 "k8s.io/api/core/v1"
33-
apierrors "k8s.io/apimachinery/pkg/api/errors"
3432
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3533
"k8s.io/apimachinery/pkg/runtime"
3634
"sigs.k8s.io/controller-runtime/pkg/client"
35+
"sigs.k8s.io/controller-runtime/pkg/client/config"
3736
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
3837
"sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert"
39-
"sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator"
4038
"sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer"
4139
"sigs.k8s.io/controller-runtime/pkg/webhook/types"
4240
)
4341

4442
// setDefault does defaulting for the Server.
4543
func (s *Server) setDefault() {
44+
s.setServerDefault()
45+
s.setBootstrappingDefault()
46+
}
47+
48+
// setServerDefault does defaulting for the ServerOptions.
49+
func (s *Server) setServerDefault() {
4650
if len(s.Name) == 0 {
4751
s.Name = "default-k8s-webhook-server"
4852
}
@@ -61,10 +65,22 @@ func (s *Server) setDefault() {
6165
if s.KVMap == nil {
6266
s.KVMap = map[string]interface{}{}
6367
}
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+
}
6581
}
6682

67-
// setBootstrappingDefault does defaulting for the Server bootstrapping.
83+
// setBootstrappingDefault does defaulting for the Server bootstrapping.
6884
func (s *Server) setBootstrappingDefault() {
6985
if len(s.MutatingWebhookConfigName) == 0 {
7086
s.MutatingWebhookConfigName = "mutating-webhook-configuration"
@@ -81,147 +97,71 @@ func (s *Server) setBootstrappingDefault() {
8197
if s.Service != nil && s.Port != 443 {
8298
s.Port = 443
8399
}
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
86117
}
87-
if s.CertGenerator == nil {
88-
s.CertGenerator = &generator.SelfSignedCertGenerator{}
118+
s.certProvisioner = &cert.Provisioner{
119+
CertWriter: certWriter,
89120
}
90121
if s.Writer == nil {
91122
s.Writer = os.Stdout
92123
}
93124
}
94125

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.
96127
// 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.
98129
func (s *Server) bootstrap(dryrun bool) error {
99130
// do defaulting if necessary
100131
s.once.Do(s.setDefault)
132+
if s.err != nil {
133+
return s.err
134+
}
101135

102136
var err error
103-
s.mutatingWebhookConfig, err = s.mutatingWHConfigs()
137+
s.webhookConfigurations, err = s.whConfigs()
104138
if err != nil {
105139
return err
106140
}
141+
svc := s.service()
142+
objects := append(s.webhookConfigurations, svc)
107143

108-
s.validatingWebhookConfig, err = s.validatingWHConfigs()
144+
cc, err := s.getClientConfig()
109145
if err != nil {
110146
return err
111147
}
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-
}
131148
// 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+
}
137157

138158
if dryrun {
139159
// TODO: print here
140160
// 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)
156162
}
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-
}
175163

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...)
225165
}
226166

227167
// genYamlConfig generates yaml config for admissionWebhookConfiguration
@@ -243,7 +183,7 @@ func (s *Server) genYamlConfig(objs []runtime.Object) error {
243183
return nil
244184
}
245185

246-
func (s *Server) clientConfig() (*admissionregistration.WebhookClientConfig, error) {
186+
func (s *Server) getClientConfig() (*admissionregistration.WebhookClientConfig, error) {
247187
if s.Host != nil && s.Service != nil {
248188
return nil, errors.New("URL and Service can't be set at the same time")
249189
}
@@ -268,14 +208,17 @@ func (s *Server) clientConfig() (*admissionregistration.WebhookClientConfig, err
268208
return cc, nil
269209
}
270210

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()
273215
if err != nil {
274216
return nil, err
275217
}
276218
return cc, setPath(cc, path)
277219
}
278220

221+
// setPath sets the path in the WebhookClientConfig.
279222
func setPath(cc *admissionregistration.WebhookClientConfig, path string) error {
280223
if cc.URL != nil {
281224
u, err := url.Parse(*cc.URL)
@@ -294,6 +237,25 @@ func setPath(cc *admissionregistration.WebhookClientConfig, path string) error {
294237

295238
// whConfigs creates a mutatingWebhookConfiguration and(or) a validatingWebhookConfiguration based on registry.
296239
// 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+
297259
func (s *Server) mutatingWHConfigs() (runtime.Object, error) {
298260
mutatingWebhooks := []v1beta1.Webhook{}
299261
for path, webhook := range s.registry {
@@ -306,11 +268,6 @@ func (s *Server) mutatingWHConfigs() (runtime.Object, error) {
306268
if err != nil {
307269
return nil, err
308270
}
309-
cc, err := s.clientConfigWithPath(path)
310-
if err != nil {
311-
return nil, err
312-
}
313-
wh.ClientConfig = *cc
314271
mutatingWebhooks = append(mutatingWebhooks, *wh)
315272
}
316273

@@ -342,11 +299,6 @@ func (s *Server) validatingWHConfigs() (runtime.Object, error) {
342299
if err != nil {
343300
return nil, err
344301
}
345-
cc, err := s.clientConfigWithPath(path)
346-
if err != nil {
347-
return nil, err
348-
}
349-
wh.ClientConfig = *cc
350302
validatingWebhooks = append(validatingWebhooks, *wh)
351303
}
352304

@@ -377,11 +329,7 @@ func (s *Server) admissionWebhook(path string, wh *admission.Webhook) (*admissio
377329
CABundle: []byte{},
378330
},
379331
}
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)
385333
if err != nil {
386334
return nil, err
387335
}

pkg/webhook/doc.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ Start the server by starting the manager
6464
if err != nil {
6565
// handle error
6666
}
67-
6867
*/
6968
package webhook
7069

pkg/webhook/internal/cert/doc.go

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,18 @@ limitations under the License.
1717
/*
1818
Package cert provides functions to manage certificates for webhookClientConfiguration.
1919
20-
Create a Provisioner with the webhookClientConfig cc you want to manage.
20+
Create a Provisioner with a CertWriter.
2121
2222
provisioner := Provisioner{
2323
CertWriter: admission.NewSecretCertWriter(admission.SecretCertWriterOptions{...}),
24-
ClientConfig: cc,
2524
}
2625
2726
Provision the certificates for the webhookClientConfig
2827
29-
err := provisioner.Provision(false)
30-
if err != nil {
31-
// handle error
32-
}
33-
34-
Inject the webhookClientConfig into objects
35-
36-
// mutatingWebhookConfiguration is an instance of admissionregistritionv1beta1.MutatingWebhookConfiguration
37-
// validatingWebhookConfiguration is an instance of admissionregistritionv1beta1.ValidatingWebhookConfiguration
38-
err = provisioner.Inject(mutatingWebhookConfiguration, validatingWebhookConfiguration)
28+
err := provisioner.Provision(Options{
29+
ClientConfig: webhookClientConfig,
30+
Objects: []runtime.Object{mutatingWebhookConfiguration, validatingWebhookConfiguration}
31+
})
3932
if err != nil {
4033
// handle error
4134
}

pkg/webhook/internal/cert/generator/doc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ limitations under the License.
1717
/*
1818
Package generator provides an interface and implementation to provision certificates.
1919
20-
Create an instance of CertGenerator.
20+
Create an instance of certGenerator.
2121
2222
cg := SelfSignedCertGenerator{}
2323

0 commit comments

Comments
 (0)