Skip to content

Commit b49970c

Browse files
author
Mengqi Yu
committed
restructure certwriter and certprovisioner packages
1 parent ab29e82 commit b49970c

File tree

10 files changed

+473
-593
lines changed

10 files changed

+473
-593
lines changed

pkg/webhook/bootstrap.go

Lines changed: 135 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,12 @@ limitations under the License.
1717
package webhook
1818

1919
import (
20-
"bytes"
2120
"context"
2221
"errors"
22+
"fmt"
2323
"net/http"
2424
"net/url"
25+
"os"
2526
"path"
2627

2728
"github.com/ghodss/yaml"
@@ -35,14 +36,15 @@ import (
3536
"sigs.k8s.io/controller-runtime/pkg/client"
3637
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
3738
"sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert"
39+
"sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/generator"
3840
"sigs.k8s.io/controller-runtime/pkg/webhook/internal/cert/writer"
3941
"sigs.k8s.io/controller-runtime/pkg/webhook/types"
4042
)
4143

4244
// setDefault does defaulting for the Server.
4345
func (s *Server) setDefault() {
4446
if len(s.Name) == 0 {
45-
s.Name = "default-admission-server"
47+
s.Name = "default-k8s-webhook-server"
4648
}
4749
if s.registry == nil {
4850
s.registry = map[string]Webhook{}
@@ -54,7 +56,10 @@ func (s *Server) setDefault() {
5456
s.Port = 443
5557
}
5658
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{}{}
5863
}
5964
s.setBootstrappingDefault()
6065
}
@@ -76,89 +81,125 @@ func (s *Server) setBootstrappingDefault() {
7681
if s.Service != nil && s.Port != 443 {
7782
s.Port = 443
7883
}
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
8392
}
8493
}
8594

8695
// bootstrap returns the configuration of admissionWebhookConfiguration in yaml format if dryrun is true.
8796
// Otherwise, it creates the the admissionWebhookConfiguration objects and service if any.
8897
// 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
90100
s.once.Do(s.setDefault)
91101

92102
var err error
93103
s.mutatingWebhookConfig, err = s.mutatingWHConfigs()
94104
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
100106
}
101107

102108
s.validatingWebhookConfig, err = s.validatingWHConfigs()
103109
if err != nil {
104-
return nil, err
110+
return err
105111
}
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+
})
109130
}
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)
110135

111136
svc := s.service()
112137

113-
// if dryrun, return the AdmissionWebhookConfiguration in yaml format.
114138
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
116143
}
117144

118145
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")
120147
}
121148

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}
131150

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+
}
140156
}
157+
return nil
158+
}
141159

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
151161

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
153167
}
154168

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+
}
156182

157183
// createOrReplace creates the object if it doesn't exist;
158184
// 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.
160201
// 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 {
162203
if obj == nil {
163204
return nil
164205
}
@@ -183,59 +224,31 @@ func createOrReplace(c client.Client, obj runtime.Object, fn mutateFn) error {
183224
return err
184225
}
185226

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("---"))
199231
if err != nil {
200-
return nil, err
232+
return err
201233
}
202-
_, err = buf.Write(b)
234+
b, err := yaml.Marshal(obj)
203235
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
215237
}
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
222241
}
223242
}
224-
return anno
225-
}
226-
227-
type webhookClientConfig struct {
228-
*admissionregistration.WebhookClientConfig
243+
return nil
229244
}
230245

231-
func (s *Server) clientConfig() (*webhookClientConfig, error) {
246+
func (s *Server) clientConfig() (*admissionregistration.WebhookClientConfig, error) {
232247
if s.Host != nil && s.Service != nil {
233248
return nil, errors.New("URL and Service can't be set at the same time")
234249
}
235-
cc := &webhookClientConfig{
236-
WebhookClientConfig: &admissionregistration.WebhookClientConfig{
237-
CABundle: []byte{},
238-
},
250+
cc := &admissionregistration.WebhookClientConfig{
251+
CABundle: []byte{},
239252
}
240253
if s.Host != nil {
241254
u := url.URL{
@@ -255,18 +268,26 @@ func (s *Server) clientConfig() (*webhookClientConfig, error) {
255268
return cc, nil
256269
}
257270

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)
261282
if err != nil {
262283
return err
263284
}
264285
u.Path = path
265286
urlString := u.String()
266-
w.URL = &urlString
287+
cc.URL = &urlString
267288
}
268-
if w.Service != nil {
269-
w.Service.Path = &path
289+
if cc.Service != nil {
290+
cc.Service.Path = &path
270291
}
271292
return nil
272293
}
@@ -285,19 +306,22 @@ func (s *Server) mutatingWHConfigs() (runtime.Object, error) {
285306
if err != nil {
286307
return nil, err
287308
}
309+
cc, err := s.clientConfigWithPath(path)
310+
if err != nil {
311+
return nil, err
312+
}
313+
wh.ClientConfig = *cc
288314
mutatingWebhooks = append(mutatingWebhooks, *wh)
289-
290315
}
291316

292317
if len(mutatingWebhooks) > 0 {
293318
return &admissionregistration.MutatingWebhookConfiguration{
294319
TypeMeta: metav1.TypeMeta{
295-
APIVersion: "admissionregistration.k8s.io/v1beta1",
320+
APIVersion: fmt.Sprintf("%s/%s", admissionregistration.GroupName, "v1beta1"),
296321
Kind: "MutatingWebhookConfiguration",
297322
},
298323
ObjectMeta: metav1.ObjectMeta{
299-
Name: s.MutatingWebhookConfigName,
300-
Annotations: s.annotations(types.WebhookTypeMutating),
324+
Name: s.MutatingWebhookConfigName,
301325
},
302326
Webhooks: mutatingWebhooks,
303327
}, nil
@@ -318,18 +342,22 @@ func (s *Server) validatingWHConfigs() (runtime.Object, error) {
318342
if err != nil {
319343
return nil, err
320344
}
345+
cc, err := s.clientConfigWithPath(path)
346+
if err != nil {
347+
return nil, err
348+
}
349+
wh.ClientConfig = *cc
321350
validatingWebhooks = append(validatingWebhooks, *wh)
322351
}
323352

324353
if len(validatingWebhooks) > 0 {
325354
return &admissionregistration.ValidatingWebhookConfiguration{
326355
TypeMeta: metav1.TypeMeta{
327-
APIVersion: "admissionregistration.k8s.io/v1beta1",
356+
APIVersion: fmt.Sprintf("%s/%s", admissionregistration.GroupName, "v1beta1"),
328357
Kind: "ValidatingWebhookConfiguration",
329358
},
330359
ObjectMeta: metav1.ObjectMeta{
331-
Name: s.ValidatingWebhookConfigName,
332-
Annotations: s.annotations(types.WebhookTypeValidating),
360+
Name: s.ValidatingWebhookConfigName,
333361
},
334362
Webhooks: validatingWebhooks,
335363
}, nil
@@ -353,11 +381,11 @@ func (s *Server) admissionWebhook(path string, wh *admission.Webhook) (*admissio
353381
if err != nil {
354382
return nil, err
355383
}
356-
err = cc.withPath(path)
384+
err = setPath(cc, path)
357385
if err != nil {
358386
return nil, err
359387
}
360-
webhook.ClientConfig = *cc.WebhookClientConfig
388+
webhook.ClientConfig = *cc
361389
return webhook, nil
362390
}
363391

0 commit comments

Comments
 (0)