diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..c71f32b --- /dev/null +++ b/README.MD @@ -0,0 +1,4 @@ +# Default-request-adder +A small container which periodically (every 10s) checks for a LimitRange on all non-excluded namespaces named `extreme-request-defaults` and creates it using the configured memory settings if absent. + +See the example-dir for an example deployment-file. \ No newline at end of file diff --git a/example/deploy.yaml b/example/deploy.yaml new file mode 100644 index 0000000..4afdd7d --- /dev/null +++ b/example/deploy.yaml @@ -0,0 +1,38 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: default-request-adder + name: default-request-adder + namespace: kube-system + annotations: + kubernetes.io/change-cause: "${TIMESTAMP} Deployed commit id: ${COMMIT}" +spec: + replicas: 1 + selector: + matchLabels: + app: default-request-adder + strategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 1 + type: RollingUpdate + template: + metadata: + labels: + app: default-request-adder + spec: + containers: + - name: default-request-adder + resources: + limits: + memory: "50Mi" + requests: + memory: "50Mi" + imagePullPolicy: Always + image: registry.gitlab.com/unboundsoftware/default-request-adder:1.0 + args: + - /default-request-adder + - -excluded-ns=kube-system,ingress-nginx + - -memory=1Pi + restartPolicy: Always diff --git a/go.mod b/go.mod index 46b3fe3..6c1b192 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.2.2 // indirect - k8s.io/api v0.0.0-20181128191700-6db15a15d2d3 // indirect + k8s.io/api v0.0.0-20181128191700-6db15a15d2d3 k8s.io/apimachinery v0.0.0-20181128191346-49ce2735e507 k8s.io/client-go v9.0.0+incompatible ) diff --git a/main.go b/main.go index 98265f9..a088191 100644 --- a/main.go +++ b/main.go @@ -1,15 +1,26 @@ package main import ( + "flag" "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "log" + "strings" + "time" ) +type args struct { + ExcludedNS *string + Memory *string + _ struct{} +} + func main() { + args := parseArgs() + config, err := rest.InClusterConfig() if err != nil { panic(err.Error()) @@ -18,32 +29,67 @@ func main() { if err != nil { panic(err.Error()) } - namespaces, err := clientset.CoreV1().Namespaces().List(metav1.ListOptions{}) + + memory, err := resource.ParseQuantity(*args.Memory) if err != nil { panic(err.Error()) } + log.Printf("Memory: %v", memory.String()) - for _, ns := range namespaces.Items { - log.Printf("Checking for LimitRange named extreme-request-defaults in namespace '%v'\n", ns.Name) - if limitRange, err := clientset.CoreV1().LimitRanges(ns.Name).List(metav1.ListOptions{FieldSelector: "metadata.name=extreme-request-defaults"}); err == nil && len(limitRange.Items) == 0 { - log.Printf("Trying to create LimitaRange\n") - limits := []v1.LimitRangeItem{ - { - DefaultRequest: v1.ResourceList{"memory": *resource.NewQuantity(1024*1024*1024*1024, resource.BinarySI)}, - Type: "Container", - }, - } - limitRange := v1.LimitRange{ - Spec: v1.LimitRangeSpec{Limits: limits}, - ObjectMeta: metav1.ObjectMeta{ - Name: "extreme-request-defaults", - }, - } - if _, err := clientset.CoreV1().LimitRanges(ns.Name).Create(&limitRange); err != nil { - log.Printf("Unable to create LimitRange in namespace '%v': Error: %v\n", ns.Name, err) - } else { - log.Printf("LimitRange extreme-request-defaults created in namespace '%v'\n", ns.Name) + limits := []v1.LimitRangeItem{ + { + DefaultRequest: v1.ResourceList{"memory": memory}, + Type: "Container", + }, + } + limitRange := v1.LimitRange{ + Spec: v1.LimitRangeSpec{Limits: limits}, + ObjectMeta: metav1.ObjectMeta{ + Name: "extreme-request-defaults", + }, + } + + excludedNS := strings.Split(*args.ExcludedNS, ",") + + for { + namespaces, err := clientset.CoreV1().Namespaces().List(metav1.ListOptions{}) + if err != nil { + panic(err.Error()) + } + + for _, ns := range namespaces.Items { + if !nsExcluded(ns.Name, excludedNS) { + log.Printf("Checking for LimitRange named extreme-request-defaults in namespace '%v'\n", ns.Name) + if limitRanges, err := clientset.CoreV1().LimitRanges(ns.Name).List(metav1.ListOptions{FieldSelector: "metadata.name=extreme-request-defaults"}); err == nil && len(limitRanges.Items) == 0 { + log.Printf("Trying to create LimitRange\n") + if _, err := clientset.CoreV1().LimitRanges(ns.Name).Create(&limitRange); err != nil { + log.Printf("Unable to create LimitRange in namespace '%v': Error: %v\n", ns.Name, err) + } else { + log.Printf("LimitRange extreme-request-defaults created in namespace '%v'\n", ns.Name) + } + } } } + + time.Sleep(10 * time.Second) } } + +func nsExcluded(name string, excludedNS []string) bool { + for _, ns := range excludedNS { + if name == ns { + return true + } + } + return false +} + +func parseArgs() args { + args := args{ + ExcludedNS: flag.String("excluded-ns", "kube-system", "Comma-separated list of namespaces to be excluded"), + Memory: flag.String("memory", "1Ti", "The default memory requests to set in the LimitRange. Default: 1Ti"), + } + flag.Parse() + + return args +}