Skip to content

Commit

Permalink
de-dup webhook factory and validator
Browse files Browse the repository at this point in the history
  • Loading branch information
JessicaGreben committed Jan 27, 2019
1 parent a2e2788 commit 3270ed9
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 101 deletions.
8 changes: 5 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
}
Expand Down
41 changes: 24 additions & 17 deletions pkg/validator/deploy.go
Original file line number Diff line number Diff line change
@@ -1,58 +1,65 @@
package validator

import (
"context"
"net/http"

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)
}
56 changes: 2 additions & 54 deletions pkg/validator/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
}
39 changes: 12 additions & 27 deletions pkg/validator/webhooks.go
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 3270ed9

Please sign in to comment.