Skip to content

Commit c2f9dae

Browse files
authored
Merge pull request kubernetes-sigs#300 from mengqiy/decouple
⚠️ split webhook server and manifest generation
2 parents d25df1b + 29adcfc commit c2f9dae

36 files changed

+98
-5247
lines changed

Gopkg.lock

Lines changed: 1 addition & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

example/main.go

Lines changed: 9 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,15 @@ import (
2020
"flag"
2121
"os"
2222

23-
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
2423
appsv1 "k8s.io/api/apps/v1"
2524
corev1 "k8s.io/api/core/v1"
26-
apitypes "k8s.io/apimachinery/pkg/types"
2725
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
2826
"sigs.k8s.io/controller-runtime/pkg/client/config"
2927
"sigs.k8s.io/controller-runtime/pkg/controller"
3028
"sigs.k8s.io/controller-runtime/pkg/handler"
31-
"sigs.k8s.io/controller-runtime/pkg/manager"
3229
logf "sigs.k8s.io/controller-runtime/pkg/log"
3330
"sigs.k8s.io/controller-runtime/pkg/log/zap"
31+
"sigs.k8s.io/controller-runtime/pkg/manager"
3432
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
3533
"sigs.k8s.io/controller-runtime/pkg/source"
3634
"sigs.k8s.io/controller-runtime/pkg/webhook"
@@ -81,52 +79,22 @@ func main() {
8179

8280
// Setup webhooks
8381
entryLog.Info("setting up webhooks")
84-
mutatingWebhook, err := builder.NewWebhookBuilder().
85-
Name("mutating.k8s.io").
82+
mutatingWebhook := builder.NewWebhookBuilder().
83+
Path("/mutate-pods").
8684
Mutating().
87-
Operations(admissionregistrationv1beta1.Create, admissionregistrationv1beta1.Update).
88-
WithManager(mgr).
89-
ForType(&corev1.Pod{}).
9085
Handlers(&podAnnotator{}).
9186
Build()
92-
if err != nil {
93-
entryLog.Error(err, "unable to setup mutating webhook")
94-
os.Exit(1)
95-
}
9687

97-
validatingWebhook, err := builder.NewWebhookBuilder().
98-
Name("validating.k8s.io").
88+
validatingWebhook := builder.NewWebhookBuilder().
89+
Path("/validate-pods").
9990
Validating().
100-
Operations(admissionregistrationv1beta1.Create, admissionregistrationv1beta1.Update).
101-
WithManager(mgr).
102-
ForType(&corev1.Pod{}).
10391
Handlers(&podValidator{}).
10492
Build()
105-
if err != nil {
106-
entryLog.Error(err, "unable to setup validating webhook")
107-
os.Exit(1)
108-
}
10993

11094
entryLog.Info("setting up webhook server")
111-
as, err := webhook.NewServer("foo-admission-server", mgr, webhook.ServerOptions{
112-
Port: 9876,
113-
CertDir: "/tmp/cert",
114-
DisableWebhookConfigInstaller: &disableWebhookConfigInstaller,
115-
BootstrapOptions: &webhook.BootstrapOptions{
116-
Secret: &apitypes.NamespacedName{
117-
Namespace: "default",
118-
Name: "foo-admission-server-secret",
119-
},
120-
121-
Service: &webhook.Service{
122-
Namespace: "default",
123-
Name: "foo-admission-server-service",
124-
// Selectors should select the pods that runs this webhook server.
125-
Selectors: map[string]string{
126-
"app": "foo-admission-server",
127-
},
128-
},
129-
},
95+
as, err := webhook.NewServer(mgr, webhook.ServerOptions{
96+
Port: 9876,
97+
CertDir: "/tmp/cert",
13098
})
13199
if err != nil {
132100
entryLog.Error(err, "unable to create a new webhook server")
@@ -136,7 +104,7 @@ func main() {
136104
entryLog.Info("registering webhooks to the webhook server")
137105
err = as.Register(mutatingWebhook, validatingWebhook)
138106
if err != nil {
139-
entryLog.Error(err, "unable to register webhooks in the admission server")
107+
entryLog.Error(err, "unable to setup the admission server")
140108
os.Exit(1)
141109
}
142110

pkg/webhook/admission/builder/builder.go

Lines changed: 9 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,6 @@ limitations under the License.
1717
package builder
1818

1919
import (
20-
"errors"
21-
"fmt"
22-
23-
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
24-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25-
"k8s.io/apimachinery/pkg/runtime"
26-
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
27-
"sigs.k8s.io/controller-runtime/pkg/manager"
2820
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
2921
"sigs.k8s.io/controller-runtime/pkg/webhook/types"
3022
)
@@ -46,26 +38,6 @@ type WebhookBuilder struct {
4638
// t specifies the type of the webhook.
4739
// Currently, Mutating and Validating are supported.
4840
t *types.WebhookType
49-
50-
// operations define the operations this webhook cares.
51-
// only one of operations and Rules can be set.
52-
operations []admissionregistrationv1beta1.OperationType
53-
// apiType represents the resource that this webhook cares.
54-
// Only one of apiType and Rules can be set.
55-
apiType runtime.Object
56-
// rules contain a list of admissionregistrationv1beta1.RuleWithOperations
57-
// It overrides operations and apiType.
58-
rules []admissionregistrationv1beta1.RuleWithOperations
59-
60-
// failurePolicy maps to the FailurePolicy in the admissionregistrationv1beta1.Webhook
61-
failurePolicy *admissionregistrationv1beta1.FailurePolicyType
62-
63-
// namespaceSelector maps to the NamespaceSelector in the admissionregistrationv1beta1.Webhook
64-
namespaceSelector *metav1.LabelSelector
65-
66-
// manager is the manager for the webhook.
67-
// It is used for provisioning various dependencies for the webhook. e.g. RESTMapper.
68-
manager manager.Manager
6941
}
7042

7143
// NewWebhookBuilder creates an empty WebhookBuilder.
@@ -98,126 +70,32 @@ func (b *WebhookBuilder) Validating() *WebhookBuilder {
9870

9971
// Path sets the path for the webhook.
10072
// Path needs to be unique among different webhooks.
101-
// This is optional. If not set, it will be built from the type and resource name.
73+
// This is required. If not set, it will be built from the type and resource name.
10274
// For example, a webhook that mutates pods has a default path of "/mutate-pods"
10375
// If the defaulting logic can't find a unique path for it, user need to set it manually.
10476
func (b *WebhookBuilder) Path(path string) *WebhookBuilder {
10577
b.path = path
10678
return b
10779
}
10880

109-
// Operations sets the operations that this webhook cares.
110-
// It will be overridden by Rules if Rules are not empty.
111-
// This is optional
112-
func (b *WebhookBuilder) Operations(ops ...admissionregistrationv1beta1.OperationType) *WebhookBuilder {
113-
b.operations = ops
114-
return b
115-
}
116-
117-
// ForType sets the type of resources that the webhook will operate.
118-
// It will be overridden by Rules if Rules are not empty.
119-
func (b *WebhookBuilder) ForType(obj runtime.Object) *WebhookBuilder {
120-
b.apiType = obj
121-
return b
122-
}
123-
124-
// Rules sets the RuleWithOperations for the webhook.
125-
// It overrides ForType and Operations.
126-
// This is optional and for advanced user.
127-
func (b *WebhookBuilder) Rules(rules ...admissionregistrationv1beta1.RuleWithOperations) *WebhookBuilder {
128-
b.rules = rules
129-
return b
130-
}
131-
132-
// FailurePolicy sets the FailurePolicy of the webhook.
133-
// If not set, it will be defaulted by the server.
134-
// This is optional
135-
func (b *WebhookBuilder) FailurePolicy(policy admissionregistrationv1beta1.FailurePolicyType) *WebhookBuilder {
136-
b.failurePolicy = &policy
137-
return b
138-
}
139-
140-
// NamespaceSelector sets the NamespaceSelector for the webhook.
141-
// This is optional
142-
func (b *WebhookBuilder) NamespaceSelector(namespaceSelector *metav1.LabelSelector) *WebhookBuilder {
143-
b.namespaceSelector = namespaceSelector
144-
return b
145-
}
146-
147-
// WithManager set the manager for the webhook for provisioning various dependencies. e.g. client etc.
148-
func (b *WebhookBuilder) WithManager(mgr manager.Manager) *WebhookBuilder {
149-
b.manager = mgr
150-
return b
151-
}
152-
15381
// Handlers sets the handlers of the webhook.
15482
func (b *WebhookBuilder) Handlers(handlers ...admission.Handler) *WebhookBuilder {
15583
b.handlers = handlers
15684
return b
15785
}
15886

159-
func (b *WebhookBuilder) validate() error {
160-
if b.t == nil {
161-
return errors.New("webhook type cannot be nil")
162-
}
163-
if b.rules == nil && b.apiType == nil {
164-
return fmt.Errorf("ForType should be set")
165-
}
166-
if b.rules != nil && b.apiType != nil {
167-
return fmt.Errorf("at most one of ForType and Rules can be set")
168-
}
169-
return nil
170-
}
171-
17287
// Build creates the Webhook based on the options provided.
173-
func (b *WebhookBuilder) Build() (*admission.Webhook, error) {
174-
err := b.validate()
175-
if err != nil {
176-
return nil, err
88+
func (b *WebhookBuilder) Build() *admission.Webhook {
89+
if b.t == nil {
90+
b.Mutating()
17791
}
17892

17993
w := &admission.Webhook{
180-
Name: b.name,
181-
Type: *b.t,
182-
Path: b.path,
183-
FailurePolicy: b.failurePolicy,
184-
NamespaceSelector: b.namespaceSelector,
185-
Handlers: b.handlers,
186-
}
187-
188-
if b.rules != nil {
189-
w.Rules = b.rules
190-
} else {
191-
if b.manager == nil {
192-
return nil, errors.New("manager should be set using WithManager")
193-
}
194-
gvk, err := apiutil.GVKForObject(b.apiType, b.manager.GetScheme())
195-
if err != nil {
196-
return nil, err
197-
}
198-
mapper := b.manager.GetRESTMapper()
199-
mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
200-
if err != nil {
201-
return nil, err
202-
}
203-
204-
if b.operations == nil {
205-
b.operations = []admissionregistrationv1beta1.OperationType{
206-
admissionregistrationv1beta1.Create,
207-
admissionregistrationv1beta1.Update,
208-
}
209-
}
210-
w.Rules = []admissionregistrationv1beta1.RuleWithOperations{
211-
{
212-
Operations: b.operations,
213-
Rule: admissionregistrationv1beta1.Rule{
214-
APIGroups: []string{gvk.Group},
215-
APIVersions: []string{gvk.Version},
216-
Resources: []string{mapping.Resource.Resource},
217-
},
218-
},
219-
}
94+
Name: b.name,
95+
Type: *b.t,
96+
Path: b.path,
97+
Handlers: b.handlers,
22098
}
22199

222-
return w, nil
100+
return w
223101
}

pkg/webhook/admission/builder/doc.go

Lines changed: 4 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -19,27 +19,19 @@ Package builder provides methods to build admission webhooks.
1919
2020
The following are 2 examples for building mutating webhook and validating webhook.
2121
22-
webhook1, err := NewWebhookBuilder().
22+
webhook1 := NewWebhookBuilder().
2323
Mutating().
24-
Operations(admissionregistrationv1beta1.Create).
24+
Path("/mutatepods").
2525
ForType(&corev1.Pod{}).
26-
WithManager(mgr).
2726
Handlers(mutatingHandler11, mutatingHandler12).
2827
Build()
29-
if err != nil {
30-
// handle error
31-
}
3228
33-
webhook2, err := NewWebhookBuilder().
29+
webhook2 := NewWebhookBuilder().
3430
Validating().
35-
Operations(admissionregistrationv1beta1.Create, admissionregistrationv1beta1.Update).
31+
Path("/validatepods").
3632
ForType(&appsv1.Deployment{}).
37-
WithManager(mgr).
3833
Handlers(validatingHandler21).
3934
Build()
40-
if err != nil {
41-
// handle error
42-
}
4335
4436
Note: To build a webhook for a CRD, you need to ensure the manager uses the scheme that understands your CRD.
4537
This is necessary, because if the scheme doesn't understand your CRD types, the decoder won't be able to decode
@@ -62,46 +54,5 @@ The following snippet shows how to register CRD types with manager's scheme.
6254
if err != nil {
6355
// handle error
6456
}
65-
66-
There are more options for configuring a webhook. e.g. Name, Path, FailurePolicy, NamespaceSelector.
67-
Here is another example:
68-
69-
webhook3, err := NewWebhookBuilder().
70-
Name("foo.example.com").
71-
Path("/mutatepods").
72-
Mutating().
73-
Operations(admissionregistrationv1beta1.Create).
74-
ForType(&corev1.Pod{}).
75-
FailurePolicy(admissionregistrationv1beta1.Fail).
76-
WithManager(mgr).
77-
Handlers(mutatingHandler31, mutatingHandler32).
78-
Build()
79-
if err != nil {
80-
// handle error
81-
}
82-
83-
For most users, we recommend to use Operations and ForType instead of Rules to construct a webhook,
84-
since it is more intuitive and easier to pass the target operations to Operations method and
85-
a empty target object to ForType method than passing a complex RuleWithOperations struct to Rules method.
86-
87-
Rules may be useful for some more advanced use cases like subresources, wildcard resources etc.
88-
Here is an example:
89-
90-
webhook4, err := NewWebhookBuilder().
91-
Validating().
92-
Rules(admissionregistrationv1beta1.RuleWithOperations{
93-
Operations: []admissionregistrationv1beta1.OperationType{admissionregistrationv1beta1.Create},
94-
Rule: admissionregistrationv1beta1.Rule{
95-
APIGroups: []string{"apps", "batch"},
96-
APIVersions: []string{"v1"},
97-
Resources: []string{"*"},
98-
},
99-
}).
100-
WithManager(mgr).
101-
Handlers(validatingHandler41).
102-
Build()
103-
if err != nil {
104-
// handle error
105-
}
10657
*/
10758
package builder

0 commit comments

Comments
 (0)