Skip to content

Commit 04a8e80

Browse files
committed
Restructure webhook code to use Defaulting interfaces and initialization
1 parent 1f19998 commit 04a8e80

File tree

4 files changed

+116
-53
lines changed

4 files changed

+116
-53
lines changed

cmd/main.go

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

1919
import (
20-
"context"
2120
"crypto/tls"
2221
"encoding/json"
2322
"flag"
@@ -26,8 +25,6 @@ import (
2625
"os"
2726
"path/filepath"
2827
"sigs.k8s.io/controller-runtime/pkg/certwatcher"
29-
"sigs.k8s.io/controller-runtime/pkg/client"
30-
3128
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
3229
// to ensure that exec-entrypoint and run can make use of them.
3330
_ "k8s.io/client-go/plugin/pkg/client/auth"
@@ -146,27 +143,10 @@ func main() {
146143
setupLog.Info("setting up pod config injector webhook")
147144

148145
webhookServer := webhook.NewServer(webhook.Options{
149-
TLSOpts: webhookTLSOpts, Port: webhookPort,
150-
})
151-
cfg := ctrl.GetConfigOrDie()
152-
client, err := client.New(cfg, client.Options{})
153-
if err != nil {
154-
setupLog.Error(err, "error creating client")
155-
os.Exit(1)
156-
}
157-
158-
webhookServer.Register("/mutate-v1-pod", &webhook.Admission{
159-
Handler: &webhook2.PodConfigInjector{
160-
Client: client,
161-
Log: ctrl.Log.WithName("webhooks").WithName("pod-config-injector"),
162-
DefaultContainerSelector: &containerSelector,
163-
},
146+
TLSOpts: webhookTLSOpts,
147+
Port: webhookPort,
164148
})
165149

166-
if err := webhookServer.Start(context.Background()); err != nil {
167-
setupLog.Error(err, "error starting webhook server")
168-
os.Exit(1)
169-
}
170150
// Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server.
171151
// More info:
172152
// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/metrics/server
@@ -232,6 +212,11 @@ func main() {
232212
setupLog.Error(err, "unable to create controller", "controller", "KconfigBinding")
233213
os.Exit(1)
234214
}
215+
216+
if err = webhook2.SetupPodConfigInjectorWithManager(mgr, &containerSelector); err != nil {
217+
setupLog.Error(err, "unable to setup pod config injector", "webhook", "Pod")
218+
os.Exit(1)
219+
}
235220
// +kubebuilder:scaffold:builder
236221

237222
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {

config/webhook/manifests.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ metadata:
55
name: mutating-webhook-configuration
66
webhooks:
77
- admissionReviewVersions:
8-
- '[v1]'
8+
- v1
99
clientConfig:
1010
service:
1111
name: webhook-service

internal/webhook/pod_injector_webhook.go

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,56 +17,63 @@ package webhook
1717

1818
import (
1919
"context"
20-
"encoding/json"
2120
"fmt"
22-
"net/http"
21+
"k8s.io/apimachinery/pkg/runtime"
22+
ctrl "sigs.k8s.io/controller-runtime"
23+
logf "sigs.k8s.io/controller-runtime/pkg/log"
24+
"sigs.k8s.io/controller-runtime/pkg/webhook"
2325
"sort"
2426
"strings"
2527

2628
"github.com/att-cloudnative-labs/kconfig-controller/api/v1beta1"
27-
"github.com/go-logr/logr"
2829
v1 "k8s.io/api/core/v1"
2930
v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
3031
"k8s.io/apimachinery/pkg/labels"
3132
"sigs.k8s.io/controller-runtime/pkg/client"
32-
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
3333
)
3434

35-
const (
36-
InjectConfigAnnotation = "kconfigcontroller.atteg.com/inject"
37-
ExclusiveEnvConfigAnnotation = "kconfigcontroller.atteg.com/exclusive-env"
38-
)
35+
var podConfigInjectorLog = logf.Log.WithName("pod-config-injector")
36+
37+
func SetupPodConfigInjectorWithManager(mgr ctrl.Manager, sel *v12.LabelSelector) error {
38+
return ctrl.NewWebhookManagedBy(mgr).For(&v1.Pod{}).
39+
WithDefaulter(
40+
&PodConfigInjector{
41+
Client: mgr.GetClient(),
42+
DefaultContainerSelector: sel,
43+
},
44+
).
45+
Complete()
46+
}
3947

40-
// +kubebuilder:webhook:path=/mutate-v1-pod,admissionReviewVersions=[v1],sideEffects=None,mutating=true,failurePolicy=ignore,groups="",resources=pods,verbs=create,versions=v1,name=config-injector.kconfigcontroller.aeg.cloud
48+
// +kubebuilder:webhook:path=/mutate-v1-pod,mutating=true,failurePolicy=ignore,sideEffects=None,groups="",resources=pods,verbs=create,versions=v1,name=config-injector.kconfigcontroller.aeg.cloud,admissionReviewVersions=v1
4149

4250
type PodConfigInjector struct {
4351
Client client.Client
44-
decoder admission.Decoder
45-
Log logr.Logger
4652
DefaultContainerSelector *v12.LabelSelector
4753
}
4854

49-
func (r *PodConfigInjector) InjectDecoder(d *admission.Decoder) error {
50-
r.decoder = *d
51-
return nil
52-
}
55+
const (
56+
InjectConfigAnnotation = "kconfigcontroller.atteg.com/inject"
57+
ExclusiveEnvConfigAnnotation = "kconfigcontroller.atteg.com/exclusive-env"
58+
)
5359

54-
func (r *PodConfigInjector) Handle(ctx context.Context, req admission.Request) admission.Response {
55-
pod := &v1.Pod{}
60+
var _ webhook.CustomDefaulter = &PodConfigInjector{}
5661

57-
err := r.decoder.Decode(req, pod)
58-
if err != nil {
59-
return admission.Errored(http.StatusBadRequest, err)
62+
func (r *PodConfigInjector) Default(ctx context.Context, obj runtime.Object) error {
63+
pod, ok := obj.(*v1.Pod)
64+
if !ok {
65+
return fmt.Errorf("expected an Pod object but got %T", obj)
6066
}
6167

6268
// only inject into pods with proper annotation
6369
if pod.Annotations == nil || strings.ToLower(pod.Annotations[InjectConfigAnnotation]) != "true" {
64-
return admission.Allowed("kconfig inject not indicated")
70+
podConfigInjectorLog.Info(fmt.Sprintf("skipping %s - not annotated", pod.Name))
71+
return nil
6572
}
6673
// get bindings that select this rs
6774
kcbs := v1beta1.KconfigBindingList{}
68-
if err := r.Client.List(ctx, &kcbs, client.InNamespace(req.Namespace)); err != nil {
69-
return admission.Errored(http.StatusInternalServerError, fmt.Errorf("could not get kconfigbininglist: %s", err.Error()))
75+
if err := r.Client.List(ctx, &kcbs, client.InNamespace(pod.Namespace)); err != nil {
76+
return fmt.Errorf("could not get kconfigbininglist: %s", err.Error())
7077
}
7178

7279
// cleanup old pod env configs
@@ -78,7 +85,7 @@ func (r *PodConfigInjector) Handle(ctx context.Context, req admission.Request) a
7885
for _, kcb := range kcbs.Items {
7986
ls, err := v12.LabelSelectorAsSelector(&kcb.Spec.Selector)
8087
if err != nil {
81-
r.Log.Error(err, fmt.Sprintf("couldn't get selector of kcb: %s", err.Error()))
88+
podConfigInjectorLog.Error(err, fmt.Sprintf("couldn't get selector of kcb: %s", err.Error()))
8289
continue
8390
}
8491

@@ -98,7 +105,7 @@ func (r *PodConfigInjector) Handle(ctx context.Context, req admission.Request) a
98105
}
99106
selector, err := v12.LabelSelectorAsSelector(labelSelector)
100107
if err != nil {
101-
r.Log.Error(err, fmt.Sprintf("error reading kcb containerSelector: %s", err.Error()))
108+
podConfigInjectorLog.Error(err, fmt.Sprintf("error reading kcb containerSelector: %s", err.Error()))
102109
continue
103110
}
104111
if selector.Matches(labelsForContainer) {
@@ -109,11 +116,7 @@ func (r *PodConfigInjector) Handle(ctx context.Context, req admission.Request) a
109116
}
110117
}
111118
}
112-
marshaledPod, err := json.Marshal(pod)
113-
if err != nil {
114-
return admission.Errored(http.StatusInternalServerError, fmt.Errorf("could not marshal pod json: %s", err.Error()))
115-
}
116-
return admission.PatchResponseFromRaw(req.Object.Raw, marshaledPod)
119+
return nil
117120
}
118121

119122
// ByLevel sort function for sorting array of KconfigBindingSpecs by their level
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
{
2+
"kind": "AdmissionReview",
3+
"apiVersion": "admission.k8s.io/v1",
4+
"request": {
5+
"uid": "12345678-1234-1234-1234-1234567890ab",
6+
"kind": {
7+
"group": "",
8+
"version": "v1",
9+
"kind": "Pod"
10+
},
11+
"resource": {
12+
"group": "",
13+
"version": "v1",
14+
"resource": "pods"
15+
},
16+
"subResource": "",
17+
"requestKind": {
18+
"group": "",
19+
"version": "v1",
20+
"kind": "Pod"
21+
},
22+
"requestResource": {
23+
"group": "",
24+
"version": "v1",
25+
"resource": "pods"
26+
},
27+
"requestSubResource": "",
28+
"name": "example-pod",
29+
"namespace": "sandbox-george",
30+
"operation": "CREATE",
31+
"userInfo": {
32+
"username": "system:serviceaccount:kube-system:default",
33+
"uid": "abcd1234-5678-90ef-ghij-klmnopqrstuv",
34+
"groups": [
35+
"system:serviceaccounts",
36+
"system:serviceaccounts:kube-system",
37+
"system:authenticated"
38+
]
39+
},
40+
"object": {
41+
"apiVersion": "v1",
42+
"kind": "Pod",
43+
"metadata": {
44+
"name": "example-pod",
45+
"namespace": "sandbox-george",
46+
"labels": {
47+
"app": "okpipeline"
48+
},
49+
"annotations": {
50+
"kconfigcontroller.atteg.com/inject": "true"
51+
}
52+
},
53+
"spec": {
54+
"containers": [
55+
{
56+
"name": "okpipeline",
57+
"image": "nginx:1.19",
58+
"ports": [
59+
{
60+
"containerPort": 80
61+
}
62+
]
63+
}
64+
]
65+
}
66+
},
67+
"oldObject": null,
68+
"dryRun": false,
69+
"options": {
70+
"apiVersion": "meta.k8s.io/v1",
71+
"kind": "CreateOptions"
72+
}
73+
}
74+
}
75+

0 commit comments

Comments
 (0)