Repository: influxdata/telegraf-operator (go)

Issue:
Allow customizing resources for telegraf sidecar
Provide a way to customize requests and limits for telegraf operator sidecar, both globally and at the pod level.


Pull request description:
feat: allow configuring custom resources for telegraf sidecar
Closes #27

Add configuration options along with validation for default resource requests and limits as well as an option to override those at individual pod level.

- [x] Signed the [InfluxData CLA](https://www.influxdata.com/legal/cla/)
  ([if necessary](https://github.com/influxdata/telegraf-operator/blob/master/CONTRIBUTING.md#sign-the-influxdata-cla))
- [x] Tests pass (no build errors)
- [x] Rebased/mergeable


Patch:
diff --git a/Dockerfile b/Dockerfile
index 76de6cd..b095868 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -10,10 +10,8 @@ COPY go.sum go.sum
 RUN go mod download
 
 # Copy the go source
-COPY main.go .
-COPY sidecar.go .
-COPY handler.go .
-COPY class_data.go .
+COPY main.go sidecar.go handler.go class_data.go ./
+
 # Build
 RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager *.go
 
diff --git a/README.md b/README.md
index e6b4718..3a20405 100644
--- a/README.md
+++ b/README.md
@@ -139,6 +139,11 @@ The available pod annotions are:
 - `telegraf.influxdata.com/image` : is used to configure telegraf image to be used for the `telegraf` sidecar container
 - `telegraf.influxdata.com/class` : configures which kind of class to use (classes are configured on the operator)
 - `telegraf.influxdata.com/secret-env` : allows adding secrets to the telegraf sidecar in the form of environment variables
+- `telegraf.influxdata.com/requests-cpu` : allows specifying resource requests for CPU
+- `telegraf.influxdata.com/requests-memory` : allows specifying resource requests for memory
+- `telegraf.influxdata.com/limits-cpu` : allows specifying resource limits for CPU
+- `telegraf.influxdata.com/limits-memory` : allows specifying resource limits for memory
+
 
 # Contributing to telegraf-operator
 
diff --git a/examples/redis.yml b/examples/redis.yml
index 9689d44..f010e78 100644
--- a/examples/redis.yml
+++ b/examples/redis.yml
@@ -17,6 +17,9 @@ spec:
           [[inputs.redis]]
             servers = ["tcp://localhost:6379"]
         telegraf.influxdata.com/class: app
+        telegraf.influxdata.com/limits-cpu: '750m'
+        # invalid memory limit, which will be ignored
+        telegraf.influxdata.com/limits-memory: '800x'
     spec:
       containers:
       - name: redis
diff --git a/main.go b/main.go
index 081cf04..2dc2943 100644
--- a/main.go
+++ b/main.go
@@ -38,7 +38,11 @@ var (
 )
 
 const (
-	defaultTelegrafImage = "docker.io/library/telegraf:1.13"
+	defaultTelegrafImage  = "docker.io/library/telegraf:1.13"
+	defaultRequestsCPU    = "50m"
+	defaultRequestsMemory = "50Mi"
+	defaultLimitsCPU      = "500m"
+	defaultLimitsMemory   = "500Mi"
 )
 
 func init() {
@@ -54,6 +58,11 @@ func main() {
 	var defaultTelegrafClass string
 	var telegrafImage string
 	var enableDefaultInternalPlugin bool
+	var telegrafRequestsCPU string
+	var telegrafRequestsMemory string
+	var telegrafLimitsCPU string
+	var telegrafLimitsMemory string
+
 	flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
 	flag.BoolVar(&enableLeaderElection, "enable-leader-election", false,
 		"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.")
@@ -62,6 +71,10 @@ func main() {
 	flag.StringVar(&telegrafClassesDirectory, "telegraf-classes-directory", "/config/classes", "The name of the directory in which the telegraf classes are configured")
 	flag.StringVar(&defaultTelegrafClass, "telegraf-default-class", "default", "Default telegraf class to use")
 	flag.StringVar(&telegrafImage, "telegraf-image", defaultTelegrafImage, "Telegraf image to inject")
+	flag.StringVar(&telegrafRequestsCPU, "telegraf-requests-cpu", defaultRequestsCPU, "Default requests for CPU")
+	flag.StringVar(&telegrafRequestsMemory, "telegraf-requests-memory", defaultRequestsMemory, "Default requests for memory")
+	flag.StringVar(&telegrafLimitsCPU, "telegraf-limits-cpu", defaultLimitsCPU, "Default limits for CPU")
+	flag.StringVar(&telegrafLimitsMemory, "telegraf-limits-memory", defaultLimitsMemory, "Default limits for memory")
 	flag.Parse()
 
 	ctrl.SetLogger(zap.New(func(o *zap.Options) {
@@ -102,12 +115,25 @@ func main() {
 		os.Exit(1)
 	}
 
+	sidecar := &sidecarHandler{
+		Logger:                      logger,
+		TelegrafImage:               telegrafImage,
+		EnableDefaultInternalPlugin: enableDefaultInternalPlugin,
+		RequestsCPU:                 telegrafRequestsCPU,
+		RequestsMemory:              telegrafRequestsMemory,
+		LimitsCPU:                   telegrafLimitsCPU,
+		LimitsMemory:                telegrafLimitsMemory,
+	}
+
+	err = sidecar.validateRequestsAndLimits()
+	if err != nil {
+		setupLog.Error(err, "default resources validation failed")
+		os.Exit(1)
+	}
+
 	hookServer.Register("/mutate-v1-pod", &webhook.Admission{Handler: &podInjector{
-		Logger: logger,
-		SidecarHandler: &sidecarHandler{
-			TelegrafImage:               telegrafImage,
-			EnableDefaultInternalPlugin: enableDefaultInternalPlugin,
-		},
+		Logger:           logger,
+		SidecarHandler:   sidecar,
 		ClassDataHandler: classData,
 	}})
 
diff --git a/sidecar.go b/sidecar.go
index 95f82e9..b9140d6 100644
--- a/sidecar.go
+++ b/sidecar.go
@@ -6,6 +6,7 @@ import (
 	"strconv"
 	"strings"
 
+	"github.com/go-logr/logr"
 	"github.com/influxdata/toml"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/resource"
@@ -36,13 +37,25 @@ const (
 	TelegrafSecretEnv = "telegraf.influxdata.com/secret-env"
 	// TelegrafImage allows specifying a custom telegraf image to be used in the sidecar container
 	TelegrafImage = "telegraf.influxdata.com/image"
-
+	// TelegrafRequestsCPU allows specifying custom CPU resource requests
+	TelegrafRequestsCPU = "telegraf.influxdata.com/requests-cpu"
+	// TelegrafRequestsMemory allows specifying custom memory resource requests
+	TelegrafRequestsMemory = "telegraf.influxdata.com/requests-memory"
+	// TelegrafLimitsCPU allows specifying custom CPU resource limits
+	TelegrafLimitsCPU = "telegraf.influxdata.com/limits-cpu"
+	// TelegrafLimitsMemory allows specifying custom memory resource limits
+	TelegrafLimitsMemory = "telegraf.influxdata.com/limits-memory"
 	telegrafSecretPrefix = "telegraf-config"
 )
 
 type sidecarHandler struct {
+	Logger                      logr.Logger
 	TelegrafImage               string
 	EnableDefaultInternalPlugin bool
+	RequestsCPU                 string
+	RequestsMemory              string
+	LimitsCPU                   string
+	LimitsMemory                string
 }
 
 // This function check if the pod have the correct annotations, otherwise the controller will skip this pod entirely
@@ -55,8 +68,29 @@ func (h *sidecarHandler) skip(pod *corev1.Pod) bool {
 	return true
 }
 
+func (h *sidecarHandler) validateRequestsAndLimits() error {
+	if _, err := resource.ParseQuantity(h.RequestsCPU); err != nil {
+		return err
+	}
+	if _, err := resource.ParseQuantity(h.RequestsMemory); err != nil {
+		return err
+	}
+	if _, err := resource.ParseQuantity(h.LimitsCPU); err != nil {
+		return err
+	}
+	if _, err := resource.ParseQuantity(h.LimitsMemory); err != nil {
+		return err
+	}
+
+	return nil
+}
+
 func (h *sidecarHandler) addSidecar(pod *corev1.Pod, name, namespace, telegrafConf string) (*corev1.Secret, error) {
-	pod.Spec.Containers = append(pod.Spec.Containers, h.newContainer(pod))
+	container, err := h.newContainer(pod)
+	if err != nil {
+		return nil, err
+	}
+	pod.Spec.Containers = append(pod.Spec.Containers, container)
 	pod.Spec.Volumes = append(pod.Spec.Volumes, h.newVolume(name))
 	return h.newSecret(pod, name, namespace, telegrafConf)
 }
@@ -139,24 +173,80 @@ func (h *sidecarHandler) newVolume(name string) corev1.Volume {
 	}
 }
 
-func (h *sidecarHandler) newContainer(pod *corev1.Pod) corev1.Container {
+// parseCustomOrDefaultQuantity parses custom quantity from annotations,
+// defaulting to quantity specified to the handler if the custom one is not valid
+func (h *sidecarHandler) parseCustomOrDefaultQuantity(customQuantity string, defaultQuantity string) (quantity resource.Quantity, err error) {
+	if quantity, err = resource.ParseQuantity(customQuantity); err != nil {
+		h.Logger.Info(fmt.Sprintf("unable to parse resource \"%s\": %v", customQuantity, err))
+		quantity, err = resource.ParseQuantity(defaultQuantity)
+	}
+	return quantity, err
+}
+
+func (h *sidecarHandler) newContainer(pod *corev1.Pod) (corev1.Container, error) {
 	var telegrafImage string
+	var telegrafRequestsCPU string
+	var telegrafRequestsMemory string
+	var telegrafLimitsCPU string
+	var telegrafLimitsMemory string
+
 	if customTelegrafImage, ok := pod.Annotations[TelegrafImage]; ok {
 		telegrafImage = customTelegrafImage
 	} else {
 		telegrafImage = h.TelegrafImage
 	}
+	if customTelegrafRequestsCPU, ok := pod.Annotations[TelegrafRequestsCPU]; ok {
+		telegrafRequestsCPU = customTelegrafRequestsCPU
+	} else {
+		telegrafRequestsCPU = h.RequestsCPU
+	}
+	if customTelegrafRequestsMemory, ok := pod.Annotations[TelegrafRequestsMemory]; ok {
+		telegrafRequestsMemory = customTelegrafRequestsMemory
+	} else {
+		telegrafRequestsMemory = h.RequestsMemory
+	}
+	if customTelegrafLimitsCPU, ok := pod.Annotations[TelegrafLimitsCPU]; ok {
+		telegrafLimitsCPU = customTelegrafLimitsCPU
+	} else {
+		telegrafLimitsCPU = h.LimitsCPU
+	}
+	if customTelegrafLimitsMemory, ok := pod.Annotations[TelegrafLimitsMemory]; ok {
+		telegrafLimitsMemory = customTelegrafLimitsMemory
+	} else {
+		telegrafLimitsMemory = h.LimitsMemory
+	}
+
+	var parsedRequestsCPU resource.Quantity
+	var parsedRequestsMemory resource.Quantity
+	var parsedLimitsCPU resource.Quantity
+	var parsedLimitsMemory resource.Quantity
+	var err error
+
+	if parsedRequestsCPU, err = h.parseCustomOrDefaultQuantity(telegrafRequestsCPU, h.RequestsCPU); err != nil {
+		return corev1.Container{}, err
+	}
+	if parsedRequestsMemory, err = h.parseCustomOrDefaultQuantity(telegrafRequestsMemory, h.RequestsMemory); err != nil {
+		return corev1.Container{}, err
+	}
+
+	if parsedLimitsCPU, err = h.parseCustomOrDefaultQuantity(telegrafLimitsCPU, h.LimitsCPU); err != nil {
+		return corev1.Container{}, err
+	}
+	if parsedLimitsMemory, err = h.parseCustomOrDefaultQuantity(telegrafLimitsMemory, h.LimitsMemory); err != nil {
+		return corev1.Container{}, err
+	}
+
 	baseContainer := corev1.Container{
 		Name:  "telegraf",
 		Image: telegrafImage,
 		Resources: corev1.ResourceRequirements{
 			Limits: corev1.ResourceList{
-				"cpu":    resource.MustParse("500m"),
-				"memory": resource.MustParse("500Mi"),
+				"cpu":    parsedLimitsCPU,
+				"memory": parsedLimitsMemory,
 			},
 			Requests: corev1.ResourceList{
-				"cpu":    resource.MustParse("50m"),
-				"memory": resource.MustParse("50Mi"),
+				"cpu":    parsedRequestsCPU,
+				"memory": parsedRequestsMemory,
 			},
 		},
 		Env: []corev1.EnvVar{
@@ -177,6 +267,7 @@ func (h *sidecarHandler) newContainer(pod *corev1.Pod) corev1.Container {
 			},
 		},
 	}
+
 	if secretEnv, ok := pod.Annotations[TelegrafSecretEnv]; ok {
 		baseContainer.EnvFrom = []corev1.EnvFromSource{
 			{
@@ -189,7 +280,7 @@ func (h *sidecarHandler) newContainer(pod *corev1.Pod) corev1.Container {
 			},
 		}
 	}
-	return baseContainer
+	return baseContainer, nil
 }
 
 // ports gathers and merges unique ports from both TelegrafMetricsPort and TelegrafMetricsPorts.


Test patch:
diff --git a/handler_test.go b/handler_test.go
index df32276..e7f0ce3 100644
--- a/handler_test.go
+++ b/handler_test.go
@@ -256,7 +256,11 @@ func Test_podInjector_Handle(t *testing.T) {
 				},
 			},
 			handler: &sidecarHandler{
-				TelegrafImage: "docker.io/library/telegraf:1.11",
+				TelegrafImage:  "docker.io/library/telegraf:1.11",
+				RequestsCPU:    defaultRequestsCPU,
+				RequestsMemory: defaultRequestsMemory,
+				LimitsCPU:      defaultLimitsCPU,
+				LimitsMemory:   defaultLimitsMemory,
 			},
 			fields: fields{
 				TelegrafDefaultClass: testTelegrafClass,
@@ -395,6 +399,155 @@ func Test_podInjector_Handle(t *testing.T) {
 				Allowed: true,
 			},
 		},
+		{
+			name: "inject telegraf with custom image into container",
+			req: admission.Request{
+				AdmissionRequest: admv1.AdmissionRequest{
+					Operation: admv1.Create,
+					Object: runtime.RawExtension{
+						Raw: []byte(`{
+								"apiVersion": "v1",
+								"kind": "Pod",
+								"metadata": {
+								  "name": "simple",
+								  "annotations": {
+									"telegraf.influxdata.com/port": "8080",
+									"telegraf.influxdata.com/path": "/v1/metrics",
+									"telegraf.influxdata.com/interval": "5s",
+									"telegraf.influxdata.com/image": "docker.io/library/telegraf:1.11"
+								  }
+								},
+								"spec": {
+								  "containers": [
+									{
+									  "name": "busybox",
+									  "image": "busybox",
+									  "args": [
+										"sleep",
+										"1000000"
+									  ]
+									}
+								  ]
+								}
+							  }`),
+					},
+				},
+			},
+			fields: fields{
+				TelegrafDefaultClass: testTelegrafClass,
+			},
+			classes: map[string]string{testTelegrafClass: sampleClassData},
+			want: want{
+				Allowed: true,
+				Patches: []string{
+					`{"op":"add","path":"/metadata/creationTimestamp"}`,
+					`{"op":"add","path":"/spec/containers/0/resources","value":{}}`,
+					`{"op":"add","path":"/spec/containers/1","value":{"env":[{"name":"NODENAME","valueFrom":{"fieldRef":{"fieldPath":"spec.nodeName"}}}],"image":"docker.io/library/telegraf:1.11","name":"telegraf","resources":{"limits":{"cpu":"500m","memory":"500Mi"},"requests":{"cpu":"50m","memory":"50Mi"}},"volumeMounts":[{"mountPath":"/etc/telegraf","name":"telegraf-config"}]}}`,
+					`{"op":"add","path":"/spec/volumes","value":[{"name":"telegraf-config","secret":{"secretName":"telegraf-config-simple"}}]}`,
+					`{"op":"add","path":"/status","value":{}}`,
+				},
+			},
+		},
+		{
+			name: "accept custom requests CPU",
+			req: admission.Request{
+				AdmissionRequest: admv1.AdmissionRequest{
+					Operation: admv1.Create,
+					Object: runtime.RawExtension{
+						Raw: []byte(`{
+								"apiVersion": "v1",
+								"kind": "Pod",
+								"metadata": {
+								  "name": "simple",
+								  "annotations": {
+									"telegraf.influxdata.com/port": "8080",
+									"telegraf.influxdata.com/path": "/v1/metrics",
+									"telegraf.influxdata.com/interval": "5s",
+									"telegraf.influxdata.com/requests-cpu": "10m"
+								  }
+								},
+								"spec": {
+								  "containers": [
+									{
+									  "name": "busybox",
+									  "image": "busybox",
+									  "args": [
+										"sleep",
+										"1000000"
+									  ]
+									}
+								  ]
+								}
+							  }`),
+					},
+				},
+			},
+			fields: fields{
+				TelegrafDefaultClass: testTelegrafClass,
+			},
+			classes: map[string]string{testTelegrafClass: sampleClassData},
+			want: want{
+				Allowed: true,
+				Patches: []string{
+					`{"op":"add","path":"/metadata/creationTimestamp"}`,
+					`{"op":"add","path":"/spec/containers/0/resources","value":{}}`,
+					`{"op":"add","path":"/spec/containers/1","value":{"env":[{"name":"NODENAME","valueFrom":{"fieldRef":{"fieldPath":"spec.nodeName"}}}],"image":"docker.io/library/telegraf:1.13","name":"telegraf","resources":{"limits":{"cpu":"500m","memory":"500Mi"},"requests":{"cpu":"10m","memory":"50Mi"}},"volumeMounts":[{"mountPath":"/etc/telegraf","name":"telegraf-config"}]}}`,
+					`{"op":"add","path":"/spec/volumes","value":[{"name":"telegraf-config","secret":{"secretName":"telegraf-config-simple"}}]}`,
+					`{"op":"add","path":"/status","value":{}}`,
+				},
+			},
+		},
+		{
+			name: "accept invalid custom requests CPU and fall back to default",
+			req: admission.Request{
+				AdmissionRequest: admv1.AdmissionRequest{
+					Operation: admv1.Create,
+					Object: runtime.RawExtension{
+						Raw: []byte(`{
+								"apiVersion": "v1",
+								"kind": "Pod",
+								"metadata": {
+								  "name": "simple",
+								  "annotations": {
+									"telegraf.influxdata.com/port": "8080",
+									"telegraf.influxdata.com/path": "/v1/metrics",
+									"telegraf.influxdata.com/interval": "5s",
+									"telegraf.influxdata.com/limits-memory": "800x",
+									"telegraf.influxdata.com/limits-cpu": "750m"
+								  }
+								},
+								"spec": {
+								  "containers": [
+									{
+									  "name": "busybox",
+									  "image": "busybox",
+									  "args": [
+										"sleep",
+										"1000000"
+									  ]
+									}
+								  ]
+								}
+							  }`),
+					},
+				},
+			},
+			fields: fields{
+				TelegrafDefaultClass: testTelegrafClass,
+			},
+			classes: map[string]string{testTelegrafClass: sampleClassData},
+			want: want{
+				// TODO: clean up
+				Allowed: true,
+				Patches: []string{
+					`{"op":"add","path":"/metadata/creationTimestamp"}`,
+					`{"op":"add","path":"/spec/containers/0/resources","value":{}}`,
+					`{"op":"add","path":"/spec/containers/1","value":{"env":[{"name":"NODENAME","valueFrom":{"fieldRef":{"fieldPath":"spec.nodeName"}}}],"image":"docker.io/library/telegraf:1.13","name":"telegraf","resources":{"limits":{"cpu":"750m","memory":"500Mi"},"requests":{"cpu":"50m","memory":"50Mi"}},"volumeMounts":[{"mountPath":"/etc/telegraf","name":"telegraf-config"}]}}`,
+					`{"op":"add","path":"/spec/volumes","value":[{"name":"telegraf-config","secret":{"secretName":"telegraf-config-simple"}}]}`,
+					`{"op":"add","path":"/status","value":{}}`,
+				},
+			},
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -405,13 +558,22 @@ func Test_podInjector_Handle(t *testing.T) {
 			}
 
 			if tt.handler == nil {
-				tt.handler = &sidecarHandler{}
+				tt.handler = &sidecarHandler{
+					RequestsCPU:    defaultRequestsCPU,
+					RequestsMemory: defaultRequestsMemory,
+					LimitsCPU:      defaultLimitsCPU,
+					LimitsMemory:   defaultLimitsMemory,
+				}
 			}
 
 			if tt.handler.TelegrafImage == "" {
 				tt.handler.TelegrafImage = defaultTelegrafImage
 			}
 
+			if tt.handler.Logger == nil {
+				tt.handler.Logger = &logrTesting.TestLogger{T: t}
+			}
+
 			dir := createTempClassesDirectory(t, tt.classes)
 			defer os.RemoveAll(dir)
 
diff --git a/sidecar_test.go b/sidecar_test.go
index 6f92337..5d9b8a8 100644
--- a/sidecar_test.go
+++ b/sidecar_test.go
@@ -2,11 +2,11 @@ package main
 
 import (
 	"bytes"
-	"fmt"
 	"reflect"
 	"strings"
 	"testing"
 
+	logrTesting "github.com/go-logr/logr/testing"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
@@ -14,7 +14,12 @@ import (
 )
 
 func Test_skip(t *testing.T) {
-	handler := &sidecarHandler{}
+	handler := &sidecarHandler{
+		RequestsCPU:    defaultRequestsCPU,
+		RequestsMemory: defaultRequestsMemory,
+		LimitsCPU:      defaultLimitsCPU,
+		LimitsMemory:   defaultLimitsMemory,
+	}
 	withTelegraf := &corev1.Pod{
 		ObjectMeta: metav1.ObjectMeta{
 			Annotations: map[string]string{
@@ -38,6 +43,49 @@ func Test_skip(t *testing.T) {
 	}
 }
 
+func Test_validateRequestsAndLimits(t *testing.T) {
+	tests := []struct {
+		name    string
+		sidecar *sidecarHandler
+		wantErr bool
+	}{
+		{
+			name: "validate default values",
+			sidecar: &sidecarHandler{
+				RequestsCPU:    defaultRequestsCPU,
+				RequestsMemory: defaultRequestsMemory,
+				LimitsCPU:      defaultLimitsCPU,
+				LimitsMemory:   defaultLimitsMemory,
+			},
+			wantErr: false,
+		},
+		{
+			name: "validate incorrect values",
+			sidecar: &sidecarHandler{
+				RequestsCPU:    "100x",
+				RequestsMemory: "100x",
+				LimitsCPU:      "100x",
+				LimitsMemory:   "100x",
+			},
+			wantErr: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			tt.sidecar.Logger = &logrTesting.TestLogger{T: t}
+
+			err := tt.sidecar.validateRequestsAndLimits()
+			if tt.wantErr && err == nil {
+				t.Errorf("expected an error, but none was reported")
+			}
+			if !tt.wantErr && err != nil {
+				t.Errorf("unexpected error received: %v", err)
+			}
+		})
+	}
+}
+
 func Test_assembleConf(t *testing.T) {
 	tests := []struct {
 		name                        string
@@ -170,6 +218,11 @@ func Test_assembleConf(t *testing.T) {
 
 			handler := &sidecarHandler{
 				EnableDefaultInternalPlugin: tt.enableDefaultInternalPlugin,
+				RequestsCPU:                 defaultRequestsCPU,
+				RequestsMemory:              defaultRequestsMemory,
+				LimitsCPU:                   defaultLimitsCPU,
+				LimitsMemory:                defaultLimitsMemory,
+				Logger:                      &logrTesting.TestLogger{T: t},
 			}
 			gotConfig, err := handler.assembleConf(tt.pod, tt.classData)
 			if (err != nil) != tt.wantErr {
@@ -306,6 +359,94 @@ stringData:
 
 type: Opaque`,
 		},
+		{
+			name: "validate custom resources and limits",
+			pod: &corev1.Pod{
+				ObjectMeta: metav1.ObjectMeta{
+					Annotations: map[string]string{
+						TelegrafRequestsCPU:    "100m",
+						TelegrafRequestsMemory: "100Mi",
+						TelegrafLimitsCPU:      "400m",
+						TelegrafLimitsMemory:   "400Mi",
+					},
+				},
+			},
+			wantPod: `
+metadata:
+  annotations:
+    telegraf.influxdata.com/limits-cpu: 400m
+    telegraf.influxdata.com/limits-memory: 400Mi
+    telegraf.influxdata.com/requests-cpu: 100m
+    telegraf.influxdata.com/requests-memory: 100Mi
+  creationTimestamp: null
+spec:
+  containers:
+  - env:
+    - name: NODENAME
+      valueFrom:
+        fieldRef:
+          fieldPath: spec.nodeName
+    image: docker.io/library/telegraf:1.13
+    name: telegraf
+    resources:
+      limits:
+        cpu: 400m
+        memory: 400Mi
+      requests:
+        cpu: 100m
+        memory: 100Mi
+    volumeMounts:
+    - mountPath: /etc/telegraf
+      name: telegraf-config
+  volumes:
+  - name: telegraf-config
+    secret:
+      secretName: telegraf-config-myname
+status: {}
+			`,
+		},
+		{
+			name: "validate incorrect resources to fallback default resources",
+			pod: &corev1.Pod{
+				ObjectMeta: metav1.ObjectMeta{
+					Annotations: map[string]string{
+						TelegrafRequestsCPU: "100x",
+						TelegrafLimitsCPU:   "750m",
+					},
+				},
+			},
+			wantPod: `
+metadata:
+  annotations:
+    telegraf.influxdata.com/limits-cpu: 750m
+    telegraf.influxdata.com/requests-cpu: 100x
+  creationTimestamp: null
+spec:
+  containers:
+  - env:
+    - name: NODENAME
+      valueFrom:
+        fieldRef:
+          fieldPath: spec.nodeName
+    image: docker.io/library/telegraf:1.13
+    name: telegraf
+    resources:
+      limits:
+        cpu: 750m
+        memory: 500Mi
+      requests:
+        cpu: 50m
+        memory: 50Mi
+    volumeMounts:
+    - mountPath: /etc/telegraf
+      name: telegraf-config
+  volumes:
+  - name: telegraf-config
+    secret:
+      secretName: telegraf-config-myname
+status: {}
+`,
+		},
 	}
 
 	for _, tt := range tests {
@@ -313,6 +454,11 @@ type: Opaque`,
 			handler := &sidecarHandler{
 				TelegrafImage:               defaultTelegrafImage,
 				EnableDefaultInternalPlugin: tt.enableDefaultInternalPlugin,
+				RequestsCPU:                 defaultRequestsCPU,
+				RequestsMemory:              defaultRequestsMemory,
+				LimitsCPU:                   defaultLimitsCPU,
+				LimitsMemory:                defaultLimitsMemory,
+				Logger:                      &logrTesting.TestLogger{T: t},
 			}
 			telegrafConf, err := handler.assembleConf(tt.pod, "")
 			if err != nil {
@@ -331,7 +477,6 @@ type: Opaque`,
 
 			if tt.wantPod != "" {
 				if want, got := strings.TrimSpace(tt.wantPod), strings.TrimSpace(toYAML(t, tt.pod)); got != want {
-					fmt.Printf("WTF\n%s\n", got)
 					t.Errorf("unexpected pod got:\n%v\nwant:\n%v", got, want)
 				}
 			}