Skip to content

Commit f5b0ea6

Browse files
authored
Merge pull request kubernetes-sigs#143 from mengqiy/admissionwebhook-1.11
Merge branch admissionwebhook-1.11 back to master
2 parents d364a6f + 64491d3 commit f5b0ea6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+4854
-2010
lines changed

Gopkg.lock

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

example/controller.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package main
18+
19+
import (
20+
"context"
21+
22+
"github.com/go-logr/logr"
23+
24+
appsv1 "k8s.io/api/apps/v1"
25+
"k8s.io/apimachinery/pkg/api/errors"
26+
"sigs.k8s.io/controller-runtime/pkg/client"
27+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
28+
)
29+
30+
// reconcileReplicaSet reconciles ReplicaSets
31+
type reconcileReplicaSet struct {
32+
// client can be used to retrieve objects from the APIServer.
33+
client client.Client
34+
log logr.Logger
35+
}
36+
37+
// Implement reconcile.Reconciler so the controller can reconcile objects
38+
var _ reconcile.Reconciler = &reconcileReplicaSet{}
39+
40+
func (r *reconcileReplicaSet) Reconcile(request reconcile.Request) (reconcile.Result, error) {
41+
// set up a convinient log object so we don't have to type request over and over again
42+
log := r.log.WithValues("request", request)
43+
44+
// Fetch the ReplicaSet from the cache
45+
rs := &appsv1.ReplicaSet{}
46+
err := r.client.Get(context.TODO(), request.NamespacedName, rs)
47+
if errors.IsNotFound(err) {
48+
log.Error(nil, "Could not find ReplicaSet")
49+
return reconcile.Result{}, nil
50+
}
51+
52+
if err != nil {
53+
log.Error(err, "Could not fetch ReplicaSet")
54+
return reconcile.Result{}, err
55+
}
56+
57+
// Print the ReplicaSet
58+
log.Info("Reconciling ReplicaSet", "container name", rs.Spec.Template.Spec.Containers[0].Name)
59+
60+
// Set the label if it is missing
61+
if rs.Labels == nil {
62+
rs.Labels = map[string]string{}
63+
}
64+
if rs.Labels["hello"] == "world" {
65+
return reconcile.Result{}, nil
66+
}
67+
68+
// Update the ReplicaSet
69+
rs.Labels["hello"] = "world"
70+
err = r.client.Update(context.TODO(), rs)
71+
if err != nil {
72+
log.Error(err, "Could not write ReplicaSet")
73+
return reconcile.Result{}, err
74+
}
75+
76+
return reconcile.Result{}, nil
77+
}

example/main.go

Lines changed: 55 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,26 @@ limitations under the License.
1717
package main
1818

1919
import (
20-
"context"
2120
"flag"
2221
"os"
2322

24-
"github.com/go-logr/logr"
23+
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
2524
appsv1 "k8s.io/api/apps/v1"
2625
corev1 "k8s.io/api/core/v1"
27-
"k8s.io/apimachinery/pkg/api/errors"
26+
apitypes "k8s.io/apimachinery/pkg/types"
2827
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
29-
"sigs.k8s.io/controller-runtime/pkg/client"
3028
"sigs.k8s.io/controller-runtime/pkg/client/config"
3129
"sigs.k8s.io/controller-runtime/pkg/controller"
3230
"sigs.k8s.io/controller-runtime/pkg/handler"
3331
"sigs.k8s.io/controller-runtime/pkg/manager"
34-
"sigs.k8s.io/controller-runtime/pkg/reconcile"
3532
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
3633
"sigs.k8s.io/controller-runtime/pkg/runtime/signals"
3734
"sigs.k8s.io/controller-runtime/pkg/source"
35+
"sigs.k8s.io/controller-runtime/pkg/webhook"
36+
"sigs.k8s.io/controller-runtime/pkg/webhook/admission/builder"
3837
)
3938

40-
var (
41-
log = logf.Log.WithName("example-controller")
42-
)
39+
var log = logf.Log.WithName("example-controller")
4340

4441
func main() {
4542
flag.Parse()
@@ -75,56 +72,64 @@ func main() {
7572
os.Exit(1)
7673
}
7774

78-
if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
79-
entryLog.Error(err, "unable to run manager")
75+
// Setup webhooks
76+
mutatingWebhook, err := builder.NewWebhookBuilder().
77+
Name("mutating.k8s.io").
78+
Mutating().
79+
Operations(admissionregistrationv1beta1.Create, admissionregistrationv1beta1.Update).
80+
WithManager(mgr).
81+
ForType(&corev1.Pod{}).
82+
Handlers(&podAnnotator{}).
83+
Build()
84+
if err != nil {
85+
entryLog.Error(err, "unable to setup mutating webhook")
8086
os.Exit(1)
8187
}
82-
}
83-
84-
// reconcileReplicaSet reconciles ReplicaSets
85-
type reconcileReplicaSet struct {
86-
client client.Client
87-
log logr.Logger
88-
}
89-
90-
// Implement reconcile.Reconciler so the controller can reconcile objects
91-
var _ reconcile.Reconciler = &reconcileReplicaSet{}
92-
93-
func (r *reconcileReplicaSet) Reconcile(request reconcile.Request) (reconcile.Result, error) {
94-
// set up a convinient log object so we don't have to type request over and over again
95-
log := r.log.WithValues("request", request)
96-
97-
// Fetch the ReplicaSet from the cache
98-
rs := &appsv1.ReplicaSet{}
99-
err := r.client.Get(context.TODO(), request.NamespacedName, rs)
100-
if errors.IsNotFound(err) {
101-
log.Error(nil, "Could not find ReplicaSet")
102-
return reconcile.Result{}, nil
103-
}
10488

89+
validatingWebhook, err := builder.NewWebhookBuilder().
90+
Name("validating.k8s.io").
91+
Validating().
92+
Operations(admissionregistrationv1beta1.Create, admissionregistrationv1beta1.Update).
93+
WithManager(mgr).
94+
ForType(&corev1.Pod{}).
95+
Handlers(&podValidator{}).
96+
Build()
10597
if err != nil {
106-
log.Error(err, "Could not fetch ReplicaSet")
107-
return reconcile.Result{}, err
98+
entryLog.Error(err, "unable to setup validating webhook")
99+
os.Exit(1)
108100
}
109101

110-
// Print the ReplicaSet
111-
log.Info("Reconciling ReplicaSet", "container name", rs.Spec.Template.Spec.Containers[0].Name)
112-
113-
// Set the label if it is missing
114-
if rs.Labels == nil {
115-
rs.Labels = map[string]string{}
116-
}
117-
if rs.Labels["hello"] == "world" {
118-
return reconcile.Result{}, nil
102+
as, err := webhook.NewServer("foo-admission-server", mgr, webhook.ServerOptions{
103+
Port: 9876,
104+
CertDir: "/tmp/cert",
105+
BootstrapOptions: &webhook.BootstrapOptions{
106+
Secret: &apitypes.NamespacedName{
107+
Namespace: "default",
108+
Name: "foo-admission-server-secret",
109+
},
110+
111+
Service: &webhook.Service{
112+
Namespace: "default",
113+
Name: "foo-admission-server-service",
114+
// Selectors should select the pods that runs this webhook server.
115+
Selectors: map[string]string{
116+
"app": "foo-admission-server",
117+
},
118+
},
119+
},
120+
})
121+
if err != nil {
122+
entryLog.Error(err, "unable to create a new webhook server")
123+
os.Exit(1)
119124
}
120-
121-
// Update the ReplicaSet
122-
rs.Labels["hello"] = "world"
123-
err = r.client.Update(context.TODO(), rs)
125+
err = as.Register(mutatingWebhook, validatingWebhook)
124126
if err != nil {
125-
log.Error(err, "Could not write ReplicaSet")
126-
return reconcile.Result{}, err
127+
entryLog.Error(err, "unable to register webhooks in the admission server")
128+
os.Exit(1)
127129
}
128130

129-
return reconcile.Result{}, nil
131+
if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
132+
entryLog.Error(err, "unable to run manager")
133+
os.Exit(1)
134+
}
130135
}

example/mutatingwebhook.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package main
18+
19+
import (
20+
"context"
21+
"net/http"
22+
23+
corev1 "k8s.io/api/core/v1"
24+
"sigs.k8s.io/controller-runtime/pkg/client"
25+
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
26+
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
27+
"sigs.k8s.io/controller-runtime/pkg/webhook/admission/types"
28+
)
29+
30+
// podAnnotator annotates Pods
31+
type podAnnotator struct {
32+
client client.Client
33+
decoder types.Decoder
34+
}
35+
36+
// Implement admission.Handler so the controller can handle admission request.
37+
var _ admission.Handler = &podAnnotator{}
38+
39+
// podAnnotator adds an annotation to every incoming pods.
40+
func (a *podAnnotator) Handle(ctx context.Context, req types.Request) types.Response {
41+
pod := &corev1.Pod{}
42+
43+
err := a.decoder.Decode(req, pod)
44+
if err != nil {
45+
return admission.ErrorResponse(http.StatusBadRequest, err)
46+
}
47+
copy := pod.DeepCopy()
48+
49+
err = a.mutatePodsFn(ctx, copy)
50+
if err != nil {
51+
return admission.ErrorResponse(http.StatusInternalServerError, err)
52+
}
53+
return admission.PatchResponse(pod, copy)
54+
}
55+
56+
// mutatePodsFn add an annotation to the given pod
57+
func (a *podAnnotator) mutatePodsFn(ctx context.Context, pod *corev1.Pod) error {
58+
if pod.Annotations == nil {
59+
pod.Annotations = map[string]string{}
60+
}
61+
pod.Annotations["example-mutating-admission-webhook"] = "foo"
62+
return nil
63+
}
64+
65+
// podValidator implements inject.Client.
66+
// A client will be automatically injected.
67+
var _ inject.Client = &podValidator{}
68+
69+
// InjectClient injects the client.
70+
func (v *podAnnotator) InjectClient(c client.Client) error {
71+
v.client = c
72+
return nil
73+
}
74+
75+
// podValidator implements inject.Decoder.
76+
// A decoder will be automatically injected.
77+
var _ inject.Decoder = &podValidator{}
78+
79+
// InjectDecoder injects the decoder.
80+
func (v *podAnnotator) InjectDecoder(d types.Decoder) error {
81+
v.decoder = d
82+
return nil
83+
}

0 commit comments

Comments
 (0)