Skip to content

Commit

Permalink
new validator package, basic resource limit validation
Browse files Browse the repository at this point in the history
  • Loading branch information
robscott committed Dec 7, 2018
1 parent 963d0e1 commit 3091cd9
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 98 deletions.
10 changes: 9 additions & 1 deletion deploy/all.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ subjects:
name: fairwinds
namespace: fairwinds
---
apiVersion: v1
kind: Secret
metadata:
name: fairwinds
namespace: fairwinds
labels:
app: fairwinds
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
Expand All @@ -65,7 +73,7 @@ spec:
serviceAccountName: fairwinds
containers:
- name: fairwinds
image: "quay.io/robertjscott/fairwinds:a3"
image: "quay.io/robertjscott/fairwinds:a12"
imagePullPolicy: Always
resources:
limits:
Expand Down
24 changes: 14 additions & 10 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package main

import (
"flag"
"github.com/reactiveops/fairwinds/pkg/validator"
"os"

admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
Expand All @@ -30,7 +31,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/webhook/admission/builder"
)

var log = logf.Log.WithName("example-controller")
// FairwindsName is used for Kubernetes resource naming
var FairwindsName = "fairwinds"
var log = logf.Log.WithName(FairwindsName)

func main() {
var disableWebhookConfigInstaller bool
Expand All @@ -49,36 +52,37 @@ func main() {
os.Exit(1)
}

validatingWebhook, err := builder.NewWebhookBuilder().
podValidatingWebhook, err := builder.NewWebhookBuilder().
Name("validating.k8s.io").
Validating().
Operations(admissionregistrationv1beta1.Create, admissionregistrationv1beta1.Update).
WithManager(mgr).
ForType(&corev1.Pod{}).
Handlers(&podValidator{}).
Handlers(&validator.PodValidator{}).
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("fairwinds", mgr, webhook.ServerOptions{
as, err := webhook.NewServer(FairwindsName, mgr, webhook.ServerOptions{
Port: 9876,
CertDir: "/tmp/cert",
DisableWebhookConfigInstaller: &disableWebhookConfigInstaller,
BootstrapOptions: &webhook.BootstrapOptions{
ValidatingWebhookConfigName: FairwindsName,
Secret: &apitypes.NamespacedName{
Namespace: "fairwinds",
Name: "fairwinds",
Namespace: FairwindsName,
Name: FairwindsName,
},

Service: &webhook.Service{
Namespace: "fairwinds",
Name: "fairwinds",
Namespace: FairwindsName,
Name: FairwindsName,
// Selectors should select the pods that runs this webhook server.
Selectors: map[string]string{
"app": "fairwinds",
"app": FairwindsName,
},
},
},
Expand All @@ -89,7 +93,7 @@ func main() {
}

entryLog.Info("registering webhooks to the webhook server")
err = as.Register(validatingWebhook)
err = as.Register(podValidatingWebhook)
if err != nil {
entryLog.Error(err, "unable to register webhooks in the admission server")
os.Exit(1)
Expand Down
109 changes: 109 additions & 0 deletions pkg/validator/pod.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright 2018 ReactiveOps
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package validator

import (
"context"
"net/http"

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
}

// 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)
}

allowed, reason, err := v.validatePods(ctx, pod)
if err != nil {
return admission.ErrorResponse(http.StatusInternalServerError, err)
}
return admission.ValidationResponse(allowed, reason)
}

func (v *PodValidator) validatePods(ctx context.Context, pod *corev1.Pod) (bool, string, error) {
for _, container := range pod.Spec.InitContainers {
if container.Resources.Requests.Cpu().IsZero() {
return false, "CPU resource request not Set", nil
}
if container.Resources.Requests.Memory().IsZero() {
return false, "Memory resource request not Set", nil
}
if container.Resources.Limits.Cpu().IsZero() {
return false, "CPU resource limit not Set", nil
}
if container.Resources.Limits.Memory().IsZero() {
return false, "Memory resource limit not Set", nil
}
}

for _, container := range pod.Spec.Containers {
log.Info("validating container", "container", container.Resources, "memoryLimit", container.Resources.Limits.Memory().Value(), "isZero", container.Resources.Limits.Memory().IsZero())
if container.Resources.Requests.Cpu().IsZero() {
return false, "CPU resource request not Set", nil
}
if container.Resources.Requests.Memory().IsZero() {
return false, "Memory resource request not Set", nil
}
if container.Resources.Limits.Cpu().IsZero() {
return false, "CPU resource limit not Set", nil
}
if container.Resources.Limits.Memory().IsZero() {
return false, "Memory resource limit not Set", nil
}
}

return true, "", nil
}

// 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
}
87 changes: 0 additions & 87 deletions validator.go

This file was deleted.

0 comments on commit 3091cd9

Please sign in to comment.