From 198277a9c9ec1dbc27e57806bf606d7f3d3a21b4 Mon Sep 17 00:00:00 2001 From: jessicagreben Date: Sat, 26 Jan 2019 18:15:15 -0800 Subject: [PATCH 1/6] add deploy admission webhook --- deploy/all.yaml | 21 +++++++++-- deploy/deploy.yaml | 22 +++++++++++ main.go | 42 +++------------------ pkg/kube/clientset.go | 3 ++ pkg/validator/deploy.go | 77 +++++++++++++++++++++++++++++++++++++++ pkg/validator/handlers.go | 33 +++++++++++++++++ pkg/validator/webhooks.go | 49 +++++++++++++++++++++++++ 7 files changed, 207 insertions(+), 40 deletions(-) create mode 100644 deploy/deploy.yaml create mode 100644 pkg/validator/deploy.go create mode 100644 pkg/validator/handlers.go create mode 100644 pkg/validator/webhooks.go diff --git a/deploy/all.yaml b/deploy/all.yaml index cf23d3bcc..3bec49dfb 100644 --- a/deploy/all.yaml +++ b/deploy/all.yaml @@ -1,4 +1,17 @@ --- +kind: Service +apiVersion: v1 +metadata: + name: fairwinds-dash +spec: + selector: + app: fairwinds + ports: + - name: dashboard + protocol: TCP + port: 80 + targetPort: 8080 +--- apiVersion: v1 kind: Namespace metadata: @@ -22,9 +35,9 @@ metadata: app: fairwinds rules: - apiGroups: - - '' - - 'apps/v1' - - 'admissionregistration.k8s.io' + - '' + - 'apps' + - 'admissionregistration.k8s.io' resources: - '*' verbs: @@ -110,6 +123,8 @@ spec: image: quay.io/reactiveops/fairwinds command: ["fairwinds", "--webhook"] imagePullPolicy: Always + ports: + - containerPort: 9876 resources: limits: cpu: 100m diff --git a/deploy/deploy.yaml b/deploy/deploy.yaml new file mode 100644 index 000000000..38c0df2fa --- /dev/null +++ b/deploy/deploy.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ng + labels: + app: nginx +spec: + replicas: 1 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.7.9 + ports: + - containerPort: 80 diff --git a/main.go b/main.go index a2371f823..f5f9e0e71 100644 --- a/main.go +++ b/main.go @@ -15,18 +15,13 @@ package main import ( - "encoding/json" "flag" glog "log" "net/http" "os" conf "github.com/reactiveops/fairwinds/pkg/config" - "github.com/reactiveops/fairwinds/pkg/kube" "github.com/reactiveops/fairwinds/pkg/validator" - admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" apitypes "k8s.io/apimachinery/pkg/types" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" "sigs.k8s.io/controller-runtime/pkg/client/config" @@ -34,7 +29,6 @@ import ( logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" "sigs.k8s.io/controller-runtime/pkg/runtime/signals" "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission/builder" ) // FairwindsName is used for Kubernetes resource naming @@ -66,27 +60,12 @@ func main() { } func startDashboardServer(c conf.Configuration) { - http.HandleFunc("/validate", func(w http.ResponseWriter, r *http.Request) { validateHandler(w, r, c) }) + http.HandleFunc("/validate", func(w http.ResponseWriter, r *http.Request) { validator.DeployHandler(w, r, c) }) + http.HandleFunc("/ping", validator.PingHandler) glog.Println("Starting Fairwinds dashboard webserver on port 8080.") glog.Fatal(http.ListenAndServe(":8080", nil)) } -func validateHandler(w http.ResponseWriter, r *http.Request, c conf.Configuration) { - var results []validator.Results - pods, err := kube.CoreV1API.Pods("").List(metav1.ListOptions{}) - if err != nil { - return - } - glog.Println("pods count:", len(pods.Items)) - for _, pod := range pods.Items { - result := validator.ValidatePods(c, &pod, validator.Results{}) - results = append(results, result) - } - w.WriteHeader(http.StatusOK) - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(results) -} - func startWebhookServer(c conf.Configuration, disableWebhookConfigInstaller bool) { logf.SetLogger(logf.ZapLogger(false)) entryLog := log.WithName("entrypoint") @@ -99,19 +78,6 @@ func startWebhookServer(c conf.Configuration, disableWebhookConfigInstaller bool os.Exit(1) } - podValidatingWebhook, err := builder.NewWebhookBuilder(). - Name("validating.k8s.io"). - Validating(). - Operations(admissionregistrationv1beta1.Create, admissionregistrationv1beta1.Update). - WithManager(mgr). - ForType(&corev1.Pod{}). - Handlers(&validator.PodValidator{Config: c}). - Build() - if err != nil { - entryLog.Error(err, "unable to setup validating webhook") - os.Exit(1) - } - entryLog.Info("setting up webhook server") as, err := webhook.NewServer(FairwindsName, mgr, webhook.ServerOptions{ Port: 9876, @@ -140,8 +106,10 @@ func startWebhookServer(c conf.Configuration, disableWebhookConfigInstaller bool os.Exit(1) } + deployWebhook := validator.NewDeployWebhook(mgr, c) + podWebhook := validator.NewPodWebhook(mgr, c) entryLog.Info("registering webhooks to the webhook server") - if err = as.Register(podValidatingWebhook); err != nil { + if err = as.Register(podWebhook, deployWebhook); err != nil { entryLog.Error(err, "unable to register webhooks in the admission server") os.Exit(1) } diff --git a/pkg/kube/clientset.go b/pkg/kube/clientset.go index 21caee5b4..bc8a268ef 100644 --- a/pkg/kube/clientset.go +++ b/pkg/kube/clientset.go @@ -21,3 +21,6 @@ var clientset = createClientset() // CoreV1API exports the v1 Core API client. var CoreV1API = clientset.CoreV1() + +// AppsV1API exports the v1 Apps API client. +var AppsV1API = clientset.AppsV1() diff --git a/pkg/validator/deploy.go b/pkg/validator/deploy.go new file mode 100644 index 000000000..c47c85e9c --- /dev/null +++ b/pkg/validator/deploy.go @@ -0,0 +1,77 @@ +package validator + +import ( + "context" + "net/http" + + conf "github.com/reactiveops/fairwinds/pkg/config" + appsv1 "k8s.io/api/apps/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/runtime/inject" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission/types" +) + +// DeployValidator validates Pods +type DeployValidator struct { + client client.Client + decoder types.Decoder + Config conf.Configuration +} + +// PodValidator implements inject.Client. +// A client will be automatically injected. +var _ inject.Client = &DeployValidator{} + +// InjectClient injects the client. +func (v *DeployValidator) InjectClient(c client.Client) error { + v.client = c + return nil +} + +// DeployValidator implements inject.Decoder. +// A decoder will be automatically injected. +var _ inject.Decoder = &DeployValidator{} + +// InjectDecoder injects the decoder. +func (v *DeployValidator) InjectDecoder(d types.Decoder) error { + v.decoder = d + return nil +} + +// Implement admission.Handler so the controller can handle admission request. +var _ admission.Handler = &DeployValidator{} + +// Handle for DeployValidator admits a pod if validation passes. +func (v *DeployValidator) Handle(ctx context.Context, req types.Request) types.Response { + deploy := appsv1.Deployment{} + + err := v.decoder.Decode(req, &deploy) + if err != nil { + return admission.ErrorResponse(http.StatusBadRequest, err) + } + + results := ValidateDeploys(v.Config, &deploy, Results{}) + allowed, reason := results.Format() + + return admission.ValidationResponse(allowed, reason) +} + +// ValidateDeploys does validates that each deployment conforms to the Fairwinds config. +func ValidateDeploys(conf conf.Configuration, deploy *appsv1.Deployment, results Results) Results { + for _, container := range deploy.Spec.Template.Spec.InitContainers { + results.InitContainerValidations = append( + results.InitContainerValidations, + validateContainer(conf, container), + ) + } + + for _, container := range deploy.Spec.Template.Spec.Containers { + results.ContainerValidations = append( + results.ContainerValidations, + validateContainer(conf, container), + ) + } + + return results +} diff --git a/pkg/validator/handlers.go b/pkg/validator/handlers.go new file mode 100644 index 000000000..af26763d2 --- /dev/null +++ b/pkg/validator/handlers.go @@ -0,0 +1,33 @@ +package validator + +import ( + "encoding/json" + "fmt" + "net/http" + + conf "github.com/reactiveops/fairwinds/pkg/config" + "github.com/reactiveops/fairwinds/pkg/kube" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// PingHandler is an endpoint to check the server is up. +func PingHandler(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "pong\n") +} + +// DeployHandler creates a handler for to validate the current deploy workloads. +func DeployHandler(w http.ResponseWriter, r *http.Request, c conf.Configuration) { + var results []Results + deploys, err := kube.AppsV1API.Deployments("").List(metav1.ListOptions{}) + if err != nil { + fmt.Fprintf(w, err.Error()) + return + } + for _, deploy := range deploys.Items { + result := ValidateDeploys(c, &deploy, Results{}) + results = append(results, result) + } + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(results) +} diff --git a/pkg/validator/webhooks.go b/pkg/validator/webhooks.go new file mode 100644 index 000000000..167c1e917 --- /dev/null +++ b/pkg/validator/webhooks.go @@ -0,0 +1,49 @@ +package validator + +import ( + "os" + + conf "github.com/reactiveops/fairwinds/pkg/config" + admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission/builder" +) + +// NewDeployWebhook creates a validating admission webhook for deploy creation and updates. +func NewDeployWebhook(mgr manager.Manager, c conf.Configuration) *admission.Webhook { + webhook, err := builder.NewWebhookBuilder(). + Name("deploy.k8s.io"). + Validating(). + Path("/validating-deployment"). + Operations(admissionregistrationv1beta1.Create, admissionregistrationv1beta1.Update). + WithManager(mgr). + ForType(&appsv1.Deployment{}). + Handlers(&DeployValidator{Config: c}). + Build() + if err != nil { + log.Error(err, "unable to setup deploy validating webhook") + os.Exit(1) + } + return webhook +} + +// NewPodWebhook creates a validating admission webhook for pod creation and updates. +func NewPodWebhook(mgr manager.Manager, c conf.Configuration) *admission.Webhook { + webhook, err := builder.NewWebhookBuilder(). + Name("pod.k8s.io"). + Validating(). + Path("/validating-pod"). + Operations(admissionregistrationv1beta1.Create, admissionregistrationv1beta1.Update). + WithManager(mgr). + ForType(&corev1.Pod{}). + Handlers(&PodValidator{Config: c}). + Build() + if err != nil { + log.Error(err, "unable to setup pod validating webhook") + os.Exit(1) + } + return webhook +} From e8ba0648332f25817593b7444b79a11415a30736 Mon Sep 17 00:00:00 2001 From: jessicagreben Date: Sat, 26 Jan 2019 18:17:57 -0800 Subject: [PATCH 2/6] fix comments --- pkg/validator/deploy.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pkg/validator/deploy.go b/pkg/validator/deploy.go index c47c85e9c..b83616831 100644 --- a/pkg/validator/deploy.go +++ b/pkg/validator/deploy.go @@ -19,8 +19,6 @@ type DeployValidator struct { Config conf.Configuration } -// PodValidator implements inject.Client. -// A client will be automatically injected. var _ inject.Client = &DeployValidator{} // InjectClient injects the client. @@ -29,8 +27,6 @@ func (v *DeployValidator) InjectClient(c client.Client) error { return nil } -// DeployValidator implements inject.Decoder. -// A decoder will be automatically injected. var _ inject.Decoder = &DeployValidator{} // InjectDecoder injects the decoder. @@ -39,10 +35,9 @@ func (v *DeployValidator) InjectDecoder(d types.Decoder) error { return nil } -// Implement admission.Handler so the controller can handle admission request. var _ admission.Handler = &DeployValidator{} -// Handle for DeployValidator admits a pod if validation passes. +// Handle for DeployValidator admits a deploy if validation passes. func (v *DeployValidator) Handle(ctx context.Context, req types.Request) types.Response { deploy := appsv1.Deployment{} From a2e2788cff7e1e7f271770ced377552333ae3032 Mon Sep 17 00:00:00 2001 From: jessicagreben Date: Sat, 26 Jan 2019 18:43:40 -0800 Subject: [PATCH 3/6] remove dup pod validation, move test yaml --- pkg/validator/deploy.go | 18 ++---------------- pkg/validator/pod.go | 8 ++++---- {deploy => test}/deploy.yaml | 0 {deploy => test}/pod.yaml | 0 4 files changed, 6 insertions(+), 20 deletions(-) rename {deploy => test}/deploy.yaml (100%) rename {deploy => test}/pod.yaml (100%) diff --git a/pkg/validator/deploy.go b/pkg/validator/deploy.go index b83616831..1445f39db 100644 --- a/pkg/validator/deploy.go +++ b/pkg/validator/deploy.go @@ -1,4 +1,3 @@ -package validator import ( "context" @@ -54,19 +53,6 @@ func (v *DeployValidator) Handle(ctx context.Context, req types.Request) types.R // ValidateDeploys does validates that each deployment conforms to the Fairwinds config. func ValidateDeploys(conf conf.Configuration, deploy *appsv1.Deployment, results Results) Results { - for _, container := range deploy.Spec.Template.Spec.InitContainers { - results.InitContainerValidations = append( - results.InitContainerValidations, - validateContainer(conf, container), - ) - } - - for _, container := range deploy.Spec.Template.Spec.Containers { - results.ContainerValidations = append( - results.ContainerValidations, - validateContainer(conf, container), - ) - } - - return results + pod := deploy.Spec.Template.Spec + return ValidatePods(conf, pod, results) } diff --git a/pkg/validator/pod.go b/pkg/validator/pod.go index 9737aaae8..eea912df4 100644 --- a/pkg/validator/pod.go +++ b/pkg/validator/pod.go @@ -48,22 +48,22 @@ func (v *PodValidator) Handle(ctx context.Context, req types.Request) types.Resp return admission.ErrorResponse(http.StatusBadRequest, err) } - results := ValidatePods(v.Config, pod, Results{}) + results := ValidatePods(v.Config, pod.Spec, Results{}) allowed, reason := results.Format() return admission.ValidationResponse(allowed, reason) } // ValidatePods does validates that each pod conforms to the Fairwinds config. -func ValidatePods(conf conf.Configuration, pod *corev1.Pod, results Results) Results { - for _, container := range pod.Spec.InitContainers { +func ValidatePods(conf conf.Configuration, pod *corev1.Pod.Spec, results Results) Results { + for _, container := range pod.InitContainers { results.InitContainerValidations = append( results.InitContainerValidations, validateContainer(conf, container), ) } - for _, container := range pod.Spec.Containers { + for _, container := range pod.Containers { results.ContainerValidations = append( results.ContainerValidations, validateContainer(conf, container), diff --git a/deploy/deploy.yaml b/test/deploy.yaml similarity index 100% rename from deploy/deploy.yaml rename to test/deploy.yaml diff --git a/deploy/pod.yaml b/test/pod.yaml similarity index 100% rename from deploy/pod.yaml rename to test/pod.yaml From 3270ed912b0c65f0c4221180971a4c58337808b4 Mon Sep 17 00:00:00 2001 From: jessicagreben Date: Sun, 27 Jan 2019 09:56:14 -0800 Subject: [PATCH 4/6] de-dup webhook factory and validator --- main.go | 8 +++--- pkg/validator/deploy.go | 41 ++++++++++++++++------------ pkg/validator/pod.go | 56 ++------------------------------------- pkg/validator/webhooks.go | 39 +++++++++------------------ 4 files changed, 43 insertions(+), 101 deletions(-) diff --git a/main.go b/main.go index f5f9e0e71..d42d26b60 100644 --- a/main.go +++ b/main.go @@ -22,6 +22,8 @@ import ( conf "github.com/reactiveops/fairwinds/pkg/config" "github.com/reactiveops/fairwinds/pkg/validator" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" apitypes "k8s.io/apimachinery/pkg/types" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" "sigs.k8s.io/controller-runtime/pkg/client/config" @@ -106,10 +108,10 @@ func startWebhookServer(c conf.Configuration, disableWebhookConfigInstaller bool os.Exit(1) } - deployWebhook := validator.NewDeployWebhook(mgr, c) - podWebhook := validator.NewPodWebhook(mgr, c) + p := validator.NewWebhook("pod", mgr, validator.Validator{Config: c}, &corev1.Pod{}) + d := validator.NewWebhook("deploy", mgr, validator.Validator{Config: c}, &appsv1.Deployment{}) entryLog.Info("registering webhooks to the webhook server") - if err = as.Register(podWebhook, deployWebhook); err != nil { + if err = as.Register(p, d); err != nil { entryLog.Error(err, "unable to register webhooks in the admission server") os.Exit(1) } diff --git a/pkg/validator/deploy.go b/pkg/validator/deploy.go index 1445f39db..5338c6761 100644 --- a/pkg/validator/deploy.go +++ b/pkg/validator/deploy.go @@ -1,3 +1,4 @@ +package validator import ( "context" @@ -5,54 +6,60 @@ import ( conf "github.com/reactiveops/fairwinds/pkg/config" appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/runtime/inject" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" "sigs.k8s.io/controller-runtime/pkg/webhook/admission/types" ) -// DeployValidator validates Pods -type DeployValidator struct { +// Validator validates Pods +type Validator struct { client client.Client decoder types.Decoder Config conf.Configuration } -var _ inject.Client = &DeployValidator{} +var _ inject.Client = &Validator{} // InjectClient injects the client. -func (v *DeployValidator) InjectClient(c client.Client) error { +func (v *Validator) InjectClient(c client.Client) error { v.client = c return nil } -var _ inject.Decoder = &DeployValidator{} +var _ inject.Decoder = &Validator{} // InjectDecoder injects the decoder. -func (v *DeployValidator) InjectDecoder(d types.Decoder) error { +func (v *Validator) InjectDecoder(d types.Decoder) error { v.decoder = d return nil } -var _ admission.Handler = &DeployValidator{} +var _ admission.Handler = &Validator{} // Handle for DeployValidator admits a deploy if validation passes. -func (v *DeployValidator) Handle(ctx context.Context, req types.Request) types.Response { +func (v *Validator) Handle(ctx context.Context, req types.Request) types.Response { deploy := appsv1.Deployment{} - err := v.decoder.Decode(req, &deploy) - if err != nil { - return admission.ErrorResponse(http.StatusBadRequest, err) + if err == nil { + results := ValidateDeploys(v.Config, &deploy, Results{}) + allowed, reason := results.Format() + return admission.ValidationResponse(allowed, reason) } - results := ValidateDeploys(v.Config, &deploy, Results{}) - allowed, reason := results.Format() - - return admission.ValidationResponse(allowed, reason) + pod := corev1.Pod{} + err = v.decoder.Decode(req, &pod) + if err == nil { + results := ValidatePods(v.Config, &pod.Spec, Results{}) + allowed, reason := results.Format() + return admission.ValidationResponse(allowed, reason) + } + return admission.ErrorResponse(http.StatusBadRequest, err) } -// ValidateDeploys does validates that each deployment conforms to the Fairwinds config. +// ValidateDeploys validates that each deployment conforms to the Fairwinds config. func ValidateDeploys(conf conf.Configuration, deploy *appsv1.Deployment, results Results) Results { pod := deploy.Spec.Template.Spec - return ValidatePods(conf, pod, results) + return ValidatePods(conf, &pod, results) } diff --git a/pkg/validator/pod.go b/pkg/validator/pod.go index eea912df4..4fb918ce0 100644 --- a/pkg/validator/pod.go +++ b/pkg/validator/pod.go @@ -15,47 +15,15 @@ package validator import ( - "context" - "net/http" - conf "github.com/reactiveops/fairwinds/pkg/config" corev1 "k8s.io/api/core/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/runtime/inject" logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission/types" ) var log = logf.Log.WithName("Fairwinds Validator") -// PodValidator validates Pods -type PodValidator struct { - client client.Client - decoder types.Decoder - Config conf.Configuration -} - -// Implement admission.Handler so the controller can handle admission request. -var _ admission.Handler = &PodValidator{} - -// Handle for PodValidator admits a pod if validation passes. -func (v *PodValidator) Handle(ctx context.Context, req types.Request) types.Response { - pod := &corev1.Pod{} - - err := v.decoder.Decode(req, pod) - if err != nil { - return admission.ErrorResponse(http.StatusBadRequest, err) - } - - results := ValidatePods(v.Config, pod.Spec, Results{}) - allowed, reason := results.Format() - - return admission.ValidationResponse(allowed, reason) -} - -// ValidatePods does validates that each pod conforms to the Fairwinds config. -func ValidatePods(conf conf.Configuration, pod *corev1.Pod.Spec, results Results) Results { +// ValidatePods validates that each pod conforms to the Fairwinds config. +func ValidatePods(conf conf.Configuration, pod *corev1.PodSpec, results Results) Results { for _, container := range pod.InitContainers { results.InitContainerValidations = append( results.InitContainerValidations, @@ -72,23 +40,3 @@ func ValidatePods(conf conf.Configuration, pod *corev1.Pod.Spec, results Results return results } - -// PodValidator implements inject.Client. -// A client will be automatically injected. -var _ inject.Client = &PodValidator{} - -// InjectClient injects the client. -func (v *PodValidator) InjectClient(c client.Client) error { - v.client = c - return nil -} - -// PodValidator implements inject.Decoder. -// A decoder will be automatically injected. -var _ inject.Decoder = &PodValidator{} - -// InjectDecoder injects the decoder. -func (v *PodValidator) InjectDecoder(d types.Decoder) error { - v.decoder = d - return nil -} diff --git a/pkg/validator/webhooks.go b/pkg/validator/webhooks.go index 167c1e917..4545a624b 100644 --- a/pkg/validator/webhooks.go +++ b/pkg/validator/webhooks.go @@ -1,49 +1,34 @@ package validator import ( + "fmt" "os" - conf "github.com/reactiveops/fairwinds/pkg/config" admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" "sigs.k8s.io/controller-runtime/pkg/webhook/admission/builder" ) -// NewDeployWebhook creates a validating admission webhook for deploy creation and updates. -func NewDeployWebhook(mgr manager.Manager, c conf.Configuration) *admission.Webhook { - webhook, err := builder.NewWebhookBuilder(). - Name("deploy.k8s.io"). - Validating(). - Path("/validating-deployment"). - Operations(admissionregistrationv1beta1.Create, admissionregistrationv1beta1.Update). - WithManager(mgr). - ForType(&appsv1.Deployment{}). - Handlers(&DeployValidator{Config: c}). - Build() - if err != nil { - log.Error(err, "unable to setup deploy validating webhook") - os.Exit(1) - } - return webhook -} +// NewWebhook creates a validating admission webhook for the apiType. +func NewWebhook(name string, mgr manager.Manager, validator Validator, apiType runtime.Object) *admission.Webhook { + name = fmt.Sprintf("%s.k8s.io", name) + path := fmt.Sprintf("/validating-%s", name) -// NewPodWebhook creates a validating admission webhook for pod creation and updates. -func NewPodWebhook(mgr manager.Manager, c conf.Configuration) *admission.Webhook { webhook, err := builder.NewWebhookBuilder(). - Name("pod.k8s.io"). + Name(name). Validating(). - Path("/validating-pod"). + Path(path). Operations(admissionregistrationv1beta1.Create, admissionregistrationv1beta1.Update). WithManager(mgr). - ForType(&corev1.Pod{}). - Handlers(&PodValidator{Config: c}). + ForType(apiType). + Handlers(&validator). Build() if err != nil { - log.Error(err, "unable to setup pod validating webhook") + log.Error(err, "unable to setup validating webhook:", name) os.Exit(1) } + return webhook } From 8dcef36cef6a8ada7ad900edadb2e8ed20568a58 Mon Sep 17 00:00:00 2001 From: jessicagreben Date: Sun, 27 Jan 2019 09:59:21 -0800 Subject: [PATCH 5/6] fix comments --- pkg/validator/deploy.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/validator/deploy.go b/pkg/validator/deploy.go index 5338c6761..23126b683 100644 --- a/pkg/validator/deploy.go +++ b/pkg/validator/deploy.go @@ -13,7 +13,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook/admission/types" ) -// Validator validates Pods +// Validator validates k8s resources. type Validator struct { client client.Client decoder types.Decoder @@ -38,7 +38,7 @@ func (v *Validator) InjectDecoder(d types.Decoder) error { var _ admission.Handler = &Validator{} -// Handle for DeployValidator admits a deploy if validation passes. +// Handle for Validator to run validation checks. func (v *Validator) Handle(ctx context.Context, req types.Request) types.Response { deploy := appsv1.Deployment{} err := v.decoder.Decode(req, &deploy) From 31897ee9fc5d4343504896260d8a6107a2b33823 Mon Sep 17 00:00:00 2001 From: jessicagreben Date: Mon, 28 Jan 2019 10:39:56 -0800 Subject: [PATCH 6/6] check req kind in webhook handle func --- pkg/validator/deploy.go | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/pkg/validator/deploy.go b/pkg/validator/deploy.go index 23126b683..cdf4b401a 100644 --- a/pkg/validator/deploy.go +++ b/pkg/validator/deploy.go @@ -40,22 +40,27 @@ var _ admission.Handler = &Validator{} // Handle for Validator to run validation checks. func (v *Validator) Handle(ctx context.Context, req types.Request) types.Response { - deploy := appsv1.Deployment{} - err := v.decoder.Decode(req, &deploy) - if err == nil { - results := ValidateDeploys(v.Config, &deploy, Results{}) - allowed, reason := results.Format() - return admission.ValidationResponse(allowed, reason) - } + var err error + var allowed bool + var reason string + var results Results - pod := corev1.Pod{} - err = v.decoder.Decode(req, &pod) - if err == nil { - results := ValidatePods(v.Config, &pod.Spec, Results{}) - allowed, reason := results.Format() - return admission.ValidationResponse(allowed, reason) + switch req.AdmissionRequest.Kind.Kind { + case "Deployment": + deploy := appsv1.Deployment{} + err = v.decoder.Decode(req, &deploy) + results = ValidateDeploys(v.Config, &deploy, Results{}) + case "Pod": + pod := corev1.Pod{} + err = v.decoder.Decode(req, &pod) + results = ValidatePods(v.Config, &pod.Spec, Results{}) + } + if err != nil { + return admission.ErrorResponse(http.StatusBadRequest, err) } - return admission.ErrorResponse(http.StatusBadRequest, err) + + allowed, reason = results.Format() + return admission.ValidationResponse(allowed, reason) } // ValidateDeploys validates that each deployment conforms to the Fairwinds config.