Repository: influxdata/telegraf-operator (go)

Issue:
Telegraf-istio sidecar is killed with OOM
We use `--enable-istio-injection=true` to inject sidecar for Envoy instances. The default memory limit is 200Mi. OOM killer kills the sidecar approximately every 10 minutes. With the sidecar instance fails.
For workloads we can tune limits with metadata annotations. It should be good to add annotations to workloads to tune resources for telegraf-istio sidecar. Of course, it's better if we can tune memory/cpu/ephemeral storage globally in Istio-operator config (not sure if it's possible).

Pull request:
feat: support custom cpu request limit istio sidecar
Closes #107

Adds resource requests/limits to istio sidecar via pod annotations or input argument flags the same as the telegraf sidecar

- [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/README.md b/README.md
index 26a2093..36b6442 100644
--- a/README.md
+++ b/README.md
@@ -279,6 +279,10 @@ Additional pod annotations that can be used to configure the Telegraf sidecar:
 - `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
+- `telegraf.influxdata.com/istio-requests-cpu` : allows specifying resource requests for CPU for istio sidecar
+- `telegraf.influxdata.com/istio-requests-memory` : allows specifying resource requests for memory for istio sidecar
+- `telegraf.influxdata.com/istio-limits-cpu` : allows specifying resource limits for CPU for istio sidecar
+- `telegraf.influxdata.com/istio-limits-memory` : allows specifying resource limits for memory for istio sidecar
 - `telegraf.influxdata.com/volume-mounts` : allows specifying extra volumes mount into the telegraf sidecar, the value should be json formatted, eg: {"volumeName": "mountPath"}
 
 
diff --git a/main.go b/main.go
index 3a3ab91..d0d4acd 100644
--- a/main.go
+++ b/main.go
@@ -69,6 +69,10 @@ func main() {
 	var istioTelegrafImage string
 	var istioTelegrafWatchConfig string
 	var requireAnnotationsForSecret bool
+	var istioTelegrafRequestsCPU string
+	var istioTelegrafRequestsMemory string
+	var istioTelegrafLimitsCPU string
+	var istioTelegrafLimitsMemory string
 
 	flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
 	flag.BoolVar(&enableLeaderElection, "enable-leader-election", false,
@@ -91,6 +95,11 @@ func main() {
 	flag.StringVar(&istioOutputClass, "istio-output-class", "istio", "Class to use for adding telegraf-istio sidecar to monitor its sidecar")
 	flag.StringVar(&istioTelegrafImage, "istio-telegraf-image", "", "If specified, use a custom image for telegraf-istio sidecar")
 	flag.StringVar(&istioTelegrafWatchConfig, "istio-telegraf-watch-config", "", "Optional setting to use for telegraf to watch for changes in configuration")
+	flag.StringVar(&istioTelegrafRequestsCPU, "istio-telegraf-requests-cpu", defaultRequestsCPU, "Default requests for CPU for istio sidecar")
+	flag.StringVar(&istioTelegrafRequestsMemory, "istio-ttelegraf-requests-memory", defaultRequestsMemory, "Default requests for memory for istio sidecar")
+	flag.StringVar(&istioTelegrafLimitsCPU, "istio-ttelegraf-limits-cpu", defaultLimitsCPU, "Default limits for CPU for istio sidecar")
+	flag.StringVar(&istioTelegrafLimitsMemory, "istio-ttelegraf-limits-memory", defaultLimitsMemory, "Default limits for memory for istio sidecar")
+
 	flag.Parse()
 
 	ctrl.SetLogger(zap.New(func(o *zap.Options) {
@@ -142,6 +151,10 @@ func main() {
 		RequestsMemory:              telegrafRequestsMemory,
 		LimitsCPU:                   telegrafLimitsCPU,
 		LimitsMemory:                telegrafLimitsMemory,
+		IstioRequestsCPU:            istioTelegrafRequestsCPU,
+		IstioRequestsMemory:         istioTelegrafRequestsMemory,
+		IstioLimitsCPU:              istioTelegrafLimitsCPU,
+		IstioLimitsMemory:           istioTelegrafLimitsMemory,
 	}
 
 	err = sidecar.validateRequestsAndLimits()
diff --git a/sidecar.go b/sidecar.go
index a623a56..be4bb2f 100644
--- a/sidecar.go
+++ b/sidecar.go
@@ -70,6 +70,14 @@ const (
 	TelegrafLimitsCPU = "telegraf.influxdata.com/limits-cpu"
 	// TelegrafLimitsMemory allows specifying custom memory resource limits
 	TelegrafLimitsMemory = "telegraf.influxdata.com/limits-memory"
+	// IstioTelegrafRequestsCPU allows specifying custom CPU resource requests
+	IstioTelegrafRequestsCPU = "telegraf.influxdata.com/istio-requests-cpu"
+	// IstioTelegrafRequestsMemory allows specifying custom memory resource requests
+	IstioTelegrafRequestsMemory = "telegraf.influxdata.com/istio-requests-memory"
+	// IstioTelegrafLimitsCPU allows specifying custom CPU resource limits
+	IstioTelegrafLimitsCPU = "telegraf.influxdata.com/istio-limits-cpu"
+	// IstioTelegrafLimitsMemory allows specifying custom memory resource limits
+	IstioTelegrafLimitsMemory = "telegraf.influxdata.com/istio-limits-memory"
 	// TelegrafVolumeMounts allows specifying custom extra volumes to mount on telegraf sidecar, should be json formatted, eg: {"volumeName": "mountPath"}
 	TelegrafVolumeMounts = "telegraf.influxdata.com/volume-mounts"
 	telegrafSecretInfix  = "config"
@@ -93,6 +101,10 @@ type sidecarHandler struct {
 	RequestsMemory              string
 	LimitsCPU                   string
 	LimitsMemory                string
+	IstioRequestsCPU            string
+	IstioRequestsMemory         string
+	IstioLimitsCPU              string
+	IstioLimitsMemory           string
 	EnableIstioInjection        bool
 	IstioOutputClass            string
 	IstioTelegrafImage          string
@@ -387,7 +399,7 @@ func (h *sidecarHandler) parseCustomOrDefaultQuantity(result corev1.ResourceList
 	return nil
 }
 
-// parseCustomTelegrafVolumeMounts parses custom volumeMounts from annotations, 
+// parseCustomTelegrafVolumeMounts parses custom volumeMounts from annotations,
 // telegrafVolumeMount should be json formatted, eg: {"volumeName": "mountPath"}
 // default is empty string.
 func (h *sidecarHandler) parseCustomTelegrafVolumeMounts(volumeMounts *map[string]string, telegrafVolumeMount string) (err error) {
@@ -492,7 +504,7 @@ func (h *sidecarHandler) newContainer(pod *corev1.Pod, containerName string) (co
 	vls := baseContainer.VolumeMounts
 	for volumeName, mountPath := range volumeMounts {
 		vls = append(vls, corev1.VolumeMount{
-			Name: volumeName,
+			Name:      volumeName,
 			MountPath: mountPath,
 		})
 	}
@@ -584,23 +596,47 @@ func AnnotationsWithPrefix(annotations map[string]string, prefix string) map[str
 }
 
 func (h *sidecarHandler) newIstioContainer(pod *corev1.Pod, containerName string) (corev1.Container, error) {
-	var parsedRequestsCPU resource.Quantity
-	var parsedRequestsMemory resource.Quantity
-	var parsedLimitsCPU resource.Quantity
-	var parsedLimitsMemory resource.Quantity
-	var err error
 
-	if parsedRequestsCPU, err = resource.ParseQuantity(h.RequestsCPU); err != nil {
+	var istioTelegrafRequestsCPU string
+	var istioTelegrafRequestsMemory string
+	var istioTelegrafLimitsCPU string
+	var istioTelegrafLimitsMemory string
+
+	if customIstioTelegrafRequestsCPU, ok := pod.Annotations[IstioTelegrafRequestsCPU]; ok {
+		istioTelegrafRequestsCPU = customIstioTelegrafRequestsCPU
+	} else {
+		istioTelegrafRequestsCPU = h.IstioRequestsCPU
+	}
+	if customIstioTelegrafRequestsMemory, ok := pod.Annotations[IstioTelegrafRequestsMemory]; ok {
+		istioTelegrafRequestsMemory = customIstioTelegrafRequestsMemory
+	} else {
+		istioTelegrafRequestsMemory = h.IstioRequestsMemory
+	}
+	if customIstioTelegrafLimitsCPU, ok := pod.Annotations[IstioTelegrafLimitsCPU]; ok {
+		istioTelegrafLimitsCPU = customIstioTelegrafLimitsCPU
+	} else {
+		istioTelegrafLimitsCPU = h.IstioLimitsCPU
+	}
+	if customIstioTelegrafLimitsMemory, ok := pod.Annotations[IstioTelegrafLimitsMemory]; ok {
+		istioTelegrafLimitsMemory = customIstioTelegrafLimitsMemory
+	} else {
+		istioTelegrafLimitsMemory = h.IstioLimitsMemory
+	}
+
+	resourceRequests := corev1.ResourceList{}
+	resourceLimits := corev1.ResourceList{}
+
+	if err := h.parseCustomOrDefaultQuantity(resourceRequests, "cpu", istioTelegrafRequestsCPU, h.IstioRequestsCPU); err != nil {
 		return corev1.Container{}, err
 	}
-	if parsedRequestsMemory, err = resource.ParseQuantity(h.RequestsMemory); err != nil {
+	if err := h.parseCustomOrDefaultQuantity(resourceRequests, "memory", istioTelegrafRequestsMemory, h.IstioRequestsMemory); err != nil {
 		return corev1.Container{}, err
 	}
 
-	if parsedLimitsCPU, err = resource.ParseQuantity(h.LimitsCPU); err != nil {
+	if err := h.parseCustomOrDefaultQuantity(resourceLimits, "cpu", istioTelegrafLimitsCPU, h.IstioLimitsCPU); err != nil {
 		return corev1.Container{}, err
 	}
-	if parsedLimitsMemory, err = resource.ParseQuantity(h.LimitsMemory); err != nil {
+	if err := h.parseCustomOrDefaultQuantity(resourceLimits, "memory", istioTelegrafLimitsMemory, h.IstioLimitsMemory); err != nil {
 		return corev1.Container{}, err
 	}
 
@@ -616,14 +652,8 @@ func (h *sidecarHandler) newIstioContainer(pod *corev1.Pod, containerName string
 		Image:   telegrafImage,
 		Command: telegrafContainerCommand,
 		Resources: corev1.ResourceRequirements{
-			Limits: corev1.ResourceList{
-				"cpu":    parsedLimitsCPU,
-				"memory": parsedLimitsMemory,
-			},
-			Requests: corev1.ResourceList{
-				"cpu":    parsedRequestsCPU,
-				"memory": parsedRequestsMemory,
-			},
+			Requests: resourceRequests,
+			Limits:   resourceLimits,
 		},
 		Env: []corev1.EnvVar{
 			{


Test patch:
diff --git a/handler_test.go b/handler_test.go
index d1989e0..7d2f534 100644
--- a/handler_test.go
+++ b/handler_test.go
@@ -627,6 +627,10 @@ func Test_podInjector_Handle(t *testing.T) {
 				RequestsMemory:       defaultRequestsMemory,
 				LimitsCPU:            defaultLimitsCPU,
 				LimitsMemory:         defaultLimitsMemory,
+				IstioRequestsCPU:     defaultRequestsCPU,
+				IstioRequestsMemory:  defaultRequestsMemory,
+				IstioLimitsCPU:       defaultLimitsCPU,
+				IstioLimitsMemory:    defaultLimitsMemory,
 				EnableIstioInjection: true,
 				IstioOutputClass:     testTelegrafClass,
 			},
@@ -684,6 +688,10 @@ func Test_podInjector_Handle(t *testing.T) {
 				RequestsMemory:       defaultRequestsMemory,
 				LimitsCPU:            defaultLimitsCPU,
 				LimitsMemory:         defaultLimitsMemory,
+				IstioRequestsCPU:     defaultRequestsCPU,
+				IstioRequestsMemory:  defaultRequestsMemory,
+				IstioLimitsCPU:       defaultLimitsCPU,
+				IstioLimitsMemory:    defaultLimitsMemory,
 				EnableIstioInjection: true,
 				IstioOutputClass:     testTelegrafClass,
 			},
diff --git a/sidecar_test.go b/sidecar_test.go
index efd9fbd..ee983f0 100644
--- a/sidecar_test.go
+++ b/sidecar_test.go
@@ -570,7 +570,7 @@ type: Opaque`,
 			},
 		},
 		{
-			name: "validate custom resources and limits",
+			name: "validate custom resourcesmetadata:\n          annotations:\n            sidecar.istio.io/status: dummy\n            telegraf.influxdata.com/istio-limits-cpu: 400m\n            telegraf.influxdata.com/istio-limits-memory: 400Mi\n            telegraf.influxdata.com/istio-requests-cpu: 100m\n            telegraf.influxdata.com/istio-requests-memory: 100Mi\n          creationTimestamp: null\n        spec:\n          containers:\n          - command:\n            - telegraf\n            - --config\n            - /etc/telegraf/telegraf.conf\n            env:\n            - name: NODENAME\n              valueFrom:\n                fieldRef:\n                  fieldPath: spec.nodeName\n            image: docker.io/library/telegraf:1.22\n            name: telegraf\n            resources:\n              limits:\n                cpu: 200m\n                memory: 200Mi\n              requests:\n                cpu: 10m\n                memory: 10Mi\n            volumeMounts:\n            - mountPath: /etc/telegraf\n              name: telegraf-config\n          - command:\n            - telegraf\n            - --config\n            - /etc/telegraf/telegraf.conf\n            env:\n            - name: NODENAME\n              valueFrom:\n                fieldRef:\n                  fieldPath: spec.nodeName\n            image: docker.io/library/telegraf:1.22\n            name: telegraf-istio\n            resources:\n              limits:\n                cpu: 400m\n                memory: 400Mi\n              requests:\n                cpu: 100m\n                memory: 100Mi\n            volumeMounts:\n            - mountPath: /etc/telegraf\n              name: telegraf-istio-config\n          volumes:\n          - name: telegraf-config\n            secret:\n              secretName: telegraf-config-myname\n          - name: telegraf-istio-config\n            secret:\n              secretName: telegraf-istio-config-mynameust and limits",
 			pod: &corev1.Pod{
 				ObjectMeta: metav1.ObjectMeta{
 					Annotations: map[string]string{
@@ -812,6 +812,172 @@ status: {}
 `,
 			wantSecrets: []string{testEmptyIstioSecret},
 		},
+		{
+			name: "validate istio sidecar memory and cpu requests via annotations",
+			pod: &corev1.Pod{
+				ObjectMeta: metav1.ObjectMeta{
+					Annotations: map[string]string{
+						IstioSidecarAnnotation:      "dummy",
+						IstioTelegrafRequestsCPU:    "250m",
+						IstioTelegrafRequestsMemory: "200Mi",
+						IstioTelegrafLimitsCPU:      "300m",
+						IstioTelegrafLimitsMemory:   "500Mi",
+					},
+				},
+			},
+			enableIstioInjection: true,
+			istioOutputClass:     "istio",
+			wantPod: `
+metadata:
+  annotations:
+    sidecar.istio.io/status: dummy
+    telegraf.influxdata.com/istio-limits-cpu: 300m
+    telegraf.influxdata.com/istio-limits-memory: 500Mi
+    telegraf.influxdata.com/istio-requests-cpu: 250m
+    telegraf.influxdata.com/istio-requests-memory: 200Mi
+  creationTimestamp: null
+spec:
+  containers:
+  - command:
+    - telegraf
+    - --config
+    - /etc/telegraf/telegraf.conf
+    env:
+    - name: NODENAME
+      valueFrom:
+        fieldRef:
+          fieldPath: spec.nodeName
+    image: docker.io/library/telegraf:1.22
+    name: telegraf
+    resources:
+      limits:
+        cpu: 200m
+        memory: 200Mi
+      requests:
+        cpu: 10m
+        memory: 10Mi
+    volumeMounts:
+    - mountPath: /etc/telegraf
+      name: telegraf-config
+  - command:
+    - telegraf
+    - --config
+    - /etc/telegraf/telegraf.conf
+    env:
+    - name: NODENAME
+      valueFrom:
+        fieldRef:
+          fieldPath: spec.nodeName
+    image: docker.io/library/telegraf:1.22
+    name: telegraf-istio
+    resources:
+      limits:
+        cpu: 300m
+        memory: 500Mi
+      requests:
+        cpu: 250m
+        memory: 200Mi
+    volumeMounts:
+    - mountPath: /etc/telegraf
+      name: telegraf-istio-config
+  volumes:
+  - name: telegraf-config
+    secret:
+      secretName: telegraf-config-myname
+  - name: telegraf-istio-config
+    secret:
+      secretName: telegraf-istio-config-myname
+status: {}
+`,
+			wantSecrets: []string{testEmptySecret, testEmptyIstioSecret},
+		},
+		{
+			name: "validate istio and telegraf sidecar memory and cpu requests via annotations",
+			pod: &corev1.Pod{
+				ObjectMeta: metav1.ObjectMeta{
+					Annotations: map[string]string{
+						IstioSidecarAnnotation:      "dummy",
+						IstioTelegrafRequestsCPU:    "250m",
+						IstioTelegrafRequestsMemory: "200Mi",
+						IstioTelegrafLimitsCPU:      "300m",
+						IstioTelegrafLimitsMemory:   "500Mi",
+						TelegrafRequestsCPU:         "150m",
+						TelegrafRequestsMemory:      "100Mi",
+						TelegrafLimitsCPU:           "200m",
+						TelegrafLimitsMemory:        "400Mi",
+					},
+				},
+			},
+			enableIstioInjection: true,
+			istioOutputClass:     "istio",
+			wantPod: `
+metadata:
+  annotations:
+    sidecar.istio.io/status: dummy
+    telegraf.influxdata.com/istio-limits-cpu: 300m
+    telegraf.influxdata.com/istio-limits-memory: 500Mi
+    telegraf.influxdata.com/istio-requests-cpu: 250m
+    telegraf.influxdata.com/istio-requests-memory: 200Mi
+    telegraf.influxdata.com/limits-cpu: 200m
+    telegraf.influxdata.com/limits-memory: 400Mi
+    telegraf.influxdata.com/requests-cpu: 150m
+    telegraf.influxdata.com/requests-memory: 100Mi
+  creationTimestamp: null
+spec:
+  containers:
+  - command:
+    - telegraf
+    - --config
+    - /etc/telegraf/telegraf.conf
+    env:
+    - name: NODENAME
+      valueFrom:
+        fieldRef:
+          fieldPath: spec.nodeName
+    image: docker.io/library/telegraf:1.22
+    name: telegraf
+    resources:
+      limits:
+        cpu: 200m
+        memory: 400Mi
+      requests:
+        cpu: 150m
+        memory: 100Mi
+    volumeMounts:
+    - mountPath: /etc/telegraf
+      name: telegraf-config
+  - command:
+    - telegraf
+    - --config
+    - /etc/telegraf/telegraf.conf
+    env:
+    - name: NODENAME
+      valueFrom:
+        fieldRef:
+          fieldPath: spec.nodeName
+    image: docker.io/library/telegraf:1.22
+    name: telegraf-istio
+    resources:
+      limits:
+        cpu: 300m
+        memory: 500Mi
+      requests:
+        cpu: 250m
+        memory: 200Mi
+    volumeMounts:
+    - mountPath: /etc/telegraf
+      name: telegraf-istio-config
+  volumes:
+  - name: telegraf-config
+    secret:
+      secretName: telegraf-config-myname
+  - name: telegraf-istio-config
+    secret:
+      secretName: telegraf-istio-config-myname
+status: {}
+`,
+			wantSecrets: []string{testEmptySecret, testEmptyIstioSecret},
+		},
 		{
 			name: "does not add istio sidecar when container already exists",
 			pod: &corev1.Pod{
@@ -1393,6 +1559,10 @@ status: {}
 				RequestsMemory:              defaultRequestsMemory,
 				LimitsCPU:                   defaultLimitsCPU,
 				LimitsMemory:                defaultLimitsMemory,
+				IstioRequestsCPU:            defaultRequestsCPU,
+				IstioRequestsMemory:         defaultRequestsMemory,
+				IstioLimitsCPU:              defaultLimitsCPU,
+				IstioLimitsMemory:           defaultLimitsMemory,
 				Logger:                      &logrTesting.TestLogger{T: t},
 			}