Repository: cdk8s-team/cdk8s-plus
Pull request #1438 (2022-11-23 17:00:20)

fix(hpa): unable to target a deployment with a container that has volume mounts
The `_toKube` method of `HorizontalPodAutoscaler` was populating the result with a reference to the `.containers` property of a deployment. This created a resolving loop since `containers` can contain constructs, which refer contain circular references to each other via the `host` and `defaultChild` properties. 

Remove the `.containers` from the result of `_toKube`, it was oversight, and is not necessary. In general as a rule of thumb, `_toKube` methods should always result in a JSON serializable object.

Fixes https://github.com/cdk8s-team/cdk8s-plus/issues/1423

ryparker: Do we need backport PRs?

Patch:
diff --git a/src/horizontal-pod-autoscaler.ts b/src/horizontal-pod-autoscaler.ts
index 990c0f8b..e9fba6da 100644
--- a/src/horizontal-pod-autoscaler.ts
+++ b/src/horizontal-pod-autoscaler.ts
@@ -336,10 +336,15 @@ export class HorizontalPodAutoscaler extends Resource {
    * @internal
    */
   public _toKube(): k8s.HorizontalPodAutoscalerSpecV2 {
+    const scalingTarget = this.target.toScalingTarget();
     return {
       maxReplicas: this.maxReplicas,
       minReplicas: this.minReplicas,
-      scaleTargetRef: this.target.toScalingTarget(),
+      scaleTargetRef: {
+        apiVersion: scalingTarget.apiVersion,
+        name: scalingTarget.name,
+        kind: scalingTarget.kind,
+      },
       metrics: this.metrics?.map(m => m._toKube()),
       behavior: {
         scaleUp: {


Test patch:
diff --git a/test/__snapshots__/horizontal-pod-autoscaler.test.ts.snap b/test/__snapshots__/horizontal-pod-autoscaler.test.ts.snap
index 05d3c866..9642d22a 100644
--- a/test/__snapshots__/horizontal-pod-autoscaler.test.ts.snap
+++ b/test/__snapshots__/horizontal-pod-autoscaler.test.ts.snap
@@ -2875,3 +2875,128 @@ Array [
   },
 ]
 `;
+
+exports[`targets a deployment that has containers with volume mounts 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c8dc6bdf",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-deployment-c84c12f6",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-deployment-c84c12f6",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "ubuntu",
+              "imagePullPolicy": "Always",
+              "name": "test",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsNonRoot": true,
+              },
+              "volumeMounts": Array [
+                Object {
+                  "mountPath": "./test",
+                  "name": "test",
+                },
+              ],
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+          "volumes": Array [
+            Object {
+              "emptyDir": Object {},
+              "name": "test",
+            },
+          ],
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8054d86",
+    },
+    "spec": Object {
+      "behavior": Object {
+        "scaleDown": Object {
+          "policies": Array [
+            Object {
+              "periodSeconds": 300,
+              "type": "Pods",
+              "value": 1,
+            },
+          ],
+          "selectPolicy": "Max",
+          "stabilizationWindowSeconds": 300,
+        },
+        "scaleUp": Object {
+          "policies": Array [
+            Object {
+              "periodSeconds": 60,
+              "type": "Pods",
+              "value": 4,
+            },
+            Object {
+              "periodSeconds": 60,
+              "type": "Percent",
+              "value": 200,
+            },
+          ],
+          "selectPolicy": "Max",
+          "stabilizationWindowSeconds": 0,
+        },
+      },
+      "maxReplicas": 5,
+      "minReplicas": 1,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c8dc6bdf",
+      },
+    },
+  },
+]
+`;
diff --git a/test/horizontal-pod-autoscaler.test.ts b/test/horizontal-pod-autoscaler.test.ts
index c4ce1df2..1772473a 100644
--- a/test/horizontal-pod-autoscaler.test.ts
+++ b/test/horizontal-pod-autoscaler.test.ts
@@ -2,6 +2,30 @@ import { Testing, Duration, ApiObject } from 'cdk8s';
 import { Node } from 'constructs';
 import * as kplus from '../src';
 
+// https://github.com/cdk8s-team/cdk8s-plus/issues/1423
+test('targets a deployment that has containers with volume mounts', () => {
+
+  const chart = Testing.chart();
+
+  const deployment = new kplus.Deployment(chart, 'deployment');
+  const volumeMounts = [{ volume: kplus.Volume.fromEmptyDir(chart, 'test-volume', 'test'), path: './test' }];
+
+  deployment.addContainer({
+    image: 'ubuntu',
+    name: 'test',
+    volumeMounts,
+  });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'HPA', {
+    target: deployment,
+    maxReplicas: 5,
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+
+});
+
 // Checking defaults
 test('defaultChild', () => {
   const chart = Testing.chart();