Repository: cdk8s-team/cdk8s-plus
Pull request #1300 (2022-10-20 09:31:04)

feat(horizontal-pod-autoscaler): Introduce `HorizontalPodAutoscaler` construct (#1174)
# Backport

This will backport the following commits from `k8s-25/main` to `k8s-23/main`:
 - [feat(horizontal-pod-autoscaler): Introduce `HorizontalPodAutoscaler` construct (#1174)](https://github.com/cdk8s-team/cdk8s-plus/pull/1174)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool documentation](https://github.com/sqren/backport)


Patch:
diff --git a/docs/plus/horizontal-pod-autoscaler.md b/docs/plus/horizontal-pod-autoscaler.md
new file mode 100644
index 00000000..907d62b4
--- /dev/null
+++ b/docs/plus/horizontal-pod-autoscaler.md
@@ -0,0 +1,226 @@
+# HorizontalPodAutoscaler
+
+HorizontalPodAutoscaler allows your services to scale up when demand is high and scale down when they are no longer needed.
+
+!!! tip ""
+    [API Reference](../reference/cdk8s-plus-25/typescript.md#horizontalpodautoscaler)
+
+## Using a HorizontalPodAutoscaler
+
+The example below creates a HorizontalPodAutoscaler that scales the number of replicas of a Deployment based on the average CPU utilization of the pods.
+
+```typescript
+import * as k from 'cdk8s';
+import * as kplus from 'cdk8s-plus-25';
+
+const app = new k.App();
+const chart = new k.Chart(app, 'Chart');
+
+// Lets create a deployment for our web server
+const bookstoreWebsite = new kplus.Deployment(chart, 'BookstoreWebsite', {
+  containers: [ { image: 'node' } ],
+});
+
+// Now define a HorizontalPodAutoscaler
+const hpa = new kplus.HorizontalPodAutoscaler(chart, 'BookstoreWebsiteHpa', {
+  target: bookstoreWebsite,
+  maxReplicas: 10,
+  metrics: [
+     kplus.Metric.resourceCpu(kplus.MetricTarget.averageUtilization(70)),
+  ],
+ });
+// This will scale our website deployment up when the average CPU utilization
+// is 70% or higher up to a maximum of 10 replicas.
+```
+
+Using the example above, if our web server passes 70% CPU utilization the autoscaler will try and figure out the number of replicas to scale.
+
+The autoscaler uses the following formula:
+
+> desiredReplicas = ceil[currentReplicas * ( currentMetricValue / desiredMetricValue )]
+
+Based on this formula we can find out how many replicas will be scaled. For example if we have 1 replica and the CPU utilization is at 70%.
+
+> desiredReplicas = ceil[1 * ( 70 / 70 )] = 1
+
+The autoscaler will try and add 1 replica, meaning we'll have 2 web servers running, the original and the new replica.
+
+If the CPU utilization is 140% the autoscaler will try and provision 2 replicas. Scaling us up to 3 web servers total.
+
+> desiredReplicas = ceil[1 * ( 140 / 70 )] = 2
+
+This works the same way when scaling down.
+
+For example if our target CPU utilization is 70% and we have 3 replicas running and the CPU utilization is at 50%.
+
+> desiredReplicas = ceil[3 * ( 50 / 70 )] = 2
+
+The autoscaler will try and remove 1 replica, meaning we'll have 2 web servers running, the original and the new replica.
+
+
+## Metrics
+
+The HorizontalPodAutoscaler supports the following metrics:
+
+### Resource Metrics
+
+Resource metrics are used to scale on a resource like CPU or memory.
+
+!!! note ""
+    Since the resource usages of all the containers are summed up the total pod utilization may not accurately represent the individual container resource usage. This could lead to situations where a single container might be running with high usage and the HPA will not scale out because the overall pod usage is still within acceptable limits.
+
+
+```typescript
+import * as kplus from 'cdk8s-plus-25';
+
+
+const hpa = new kplus.HorizontalPodAutoscaler(chart, 'BookstoreWebsiteHpa', {
+  target: bookstoreWebsite,
+  maxReplicas: 10,
+  metrics: [
+     kplus.Metric.resourceCpu(kplus.MetricTarget.averageUtilization(70)),
+  ],
+ });
+```
+
+### Pods Metrics
+
+Pods metrics are used to scale on a metric describing each pod in the current scale target (for example, transactions-processed-per-second).
+
+```typescript
+import * as kplus from 'cdk8s-plus-25';
+
+
+const hpa = new kplus.HorizontalPodAutoscaler(chart, 'BookstoreWebsiteHpa', {
+  target: bookstoreWebsite,
+  maxReplicas: 10,
+  metrics: [
+     kplus.Metric.pods({
+      name: 'requests-per-second',
+      target: kplus.MetricTarget.averageUtilization(50),
+      labelSelector: kplus.LabelSelector.of({ labels: { app: 'scraper' } }),
+    }),
+  ],
+ });
+```
+
+
+## Container Metrics
+
+Container metrics are used to scale on one of the scaling target's container metrics.
+
+```typescript
+import * as kplus from 'cdk8s-plus-25';
+
+
+const hpa = new kplus.HorizontalPodAutoscaler(chart, 'BookstoreWebsiteHpa', {
+  target: bookstoreWebsite,
+  maxReplicas: 10,
+  metrics: [
+     kplus.Metric.containerMemory(kplus.MetricTarget.value(4096)),
+  ],
+ });
+```
+
+### Object Metrics
+
+Object metrics are used to scale on a metric describing a single kubernetes object (for example, requests-per-second on an Ingress object).
+
+```typescript
+import * as kplus from 'cdk8s-plus-25';
+
+
+const hpa = new kplus.HorizontalPodAutoscaler(chart, 'BookstoreWebsiteHpa', {
+  target: bookstoreWebsite,
+  maxReplicas: 10,
+  metrics: [
+     kplus.Metric.object({
+        object: ingress,
+        name: 'requests-per-second',
+        target: kplus.MetricTarget.averageUtilization(50),
+      }),
+  ],
+});
+```
+
+### External Metrics
+
+External metrics are used to scale on a metric not associated with any Kubernetes object (for example, an SQS queue).
+
+```typescript
+import * as kplus from 'cdk8s-plus-25';
+
+
+const hpa = new kplus.HorizontalPodAutoscaler(chart, 'BookstoreWebsiteHpa', {
+  target: bookstoreWebsite,
+  maxReplicas: 10,
+  metrics: [
+    kplus.Metric.external({
+      labelSelector: kplus.LabelSelector.of({ labels: { app: 'scraper' } }),
+      name: 'sqs-queue',
+      target: kplus.MetricTarget.averageUtilization(50),
+    }),
+  ],
+ });
+```
+
+## Scaling behavior
+
+The HorizontalPodAutoscaler has a few options to control the scaling behavior.
+
+### scaleUp /scaleDown
+
+The `scaleUp` / `scaleDown` options controls how fast the HorizontalPodAutoscaler scales up and down. They share the same options but have slightly different defaults.
+
+```typescript
+const hpa = new kplus.HorizontalPodAutoscaler(chart, 'BookstoreWebsiteHpa', {
+  target: bookstoreWebsite,
+  maxReplicas: 10,
+  scaleUp: {
+    stabilizationWindow: 60,
+    policies: [
+      kplus.PolicyType.pods(4),
+      kplus.PolicyType.percent(200),
+    ],
+  },
+  scaleDown: {
+    stabilizationWindow: 60,
+    policies: [
+      kplus.PolicyType.pods(2),
+      kplus.PolicyType.percent(100),
+    ],
+  },
+ });
+```
+
+| Option                | Description                                                                                                                     | `scaleUp` Default                                                                         | `scaleDown` Default                                                                        |
+| --------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
+| `stabilizationWindow` | Defines the window of past metrics that the autoscaler should consider when calculating wether or not autoscaling should occur. | 5 minutes                                                                                 | 0 (no stabilization is performed)                                                          |
+| `strategy`            | Determines if the the Autoscaler should scale as much or as little as possible.                                                 | `ScalingStrategy.MAX_CHANGE` Choose the scaling policy that will scale the most replicas. | `ScalingStrategy.MIN_CHANGE` Choose the scaling policy that will scale the least replicas. |
+| `policies`            | Defines how many replicas are scaled. Can be an absolute number or a percentage of the current replica count.                   | * Increase no more than 4 pods per 60 seconds  * Double the number of pods per 60 seconds | * Decrease to `minReplica` count (default 1)                                               |
+
+Check out the example below. We have a `scaleUp` policy that will scale up by 4 pods or double the current number of pods per 60 seconds. Because the `strategy` has been configured to `ScalingStrategy.MAX_CHANGE` the autoscaler will choose the policy that will make the most replicas.
+
+```typescript
+const hpa = new kplus.HorizontalPodAutoscaler(chart, 'BookstoreWebsiteHpa', {
+  target: bookstoreWebsite,
+  maxReplicas: 10,
+  metrics: [
+     kplus.Metric.resourceCpu(kplus.MetricTarget.averageUtilization(70)),
+  ],
+  scaleUp: {
+    strategy: kplus.ScalingStrategy.MAX_CHANGE,
+    stabilizationWindow: 60,
+    policies: [
+      kplus.PolicyType.pods(4),
+      kplus.PolicyType.percent(200),
+    ],
+  },
+ });
+```
+
+This means that if we currently have 3 web server pods, and the CPU utilization is at 72% the autoscaler will try and add 6 pods every 60 seconds.
+
+This will result in a total of 9 web server pods. If after 60 seconds the CPU utilization is still at 72% the autoscaler will only be allowed to add one more replica because `maxReplicas` has been configured to 10.
+
+For more information on HorizontalPodAutoscaler check out the [API Reference](../reference/cdk8s-plus-25/typescript.md#horizontalpodautoscaler).
diff --git a/src/deployment.ts b/src/deployment.ts
index 5ab994cd..9ecb4218 100644
--- a/src/deployment.ts
+++ b/src/deployment.ts
@@ -1,6 +1,7 @@
 import { ApiObject, Lazy, Duration } from 'cdk8s';
 import { Construct } from 'constructs';
 import * as container from './container';
+import { IScalable, ScalingTarget } from './horizontal-pod-autoscaler';
 import * as k8s from './imports/k8s';
 import * as ingress from './ingress';
 import * as service from './service';
@@ -111,12 +112,12 @@ export interface ExposeDeploymentViaIngressOptions extends DeploymentExposeViaSe
 * - Clean up older ReplicaSets that you don't need anymore.
 *
 **/
-export class Deployment extends workload.Workload {
+export class Deployment extends workload.Workload implements IScalable {
 
   /**
    * Number of desired pods.
    */
-  public readonly replicas: number;
+  public readonly replicas?: number;
 
   /**
    * Minimum duration for which a newly created pod should be ready without
@@ -141,6 +142,8 @@ export class Deployment extends workload.Workload {
 
   public readonly resourceType = 'deployments';
 
+  public hasAutoscaler = false;
+
   constructor(scope: Construct, id: string, props: DeploymentProps = {}) {
     super(scope, id, props);
 
@@ -156,7 +159,7 @@ export class Deployment extends workload.Workload {
       throw new Error(`'progressDeadline' (${this.progressDeadline.toSeconds()}s) must be greater than 'minReady' (${this.minReady.toSeconds()}s)`);
     }
 
-    this.replicas = props.replicas ?? 2;
+    this.replicas = props.replicas;
     this.strategy = props.strategy ?? DeploymentStrategy.rollingUpdate();
 
     if (this.isolate) {
@@ -219,7 +222,7 @@ export class Deployment extends workload.Workload {
    */
   public _toKube(): k8s.DeploymentSpec {
     return {
-      replicas: this.replicas,
+      replicas: this.hasAutoscaler ? undefined : (this.replicas ?? 2),
       minReadySeconds: this.minReady.toSeconds(),
       progressDeadlineSeconds: this.progressDeadline.toSeconds(),
       template: {
@@ -231,6 +234,25 @@ export class Deployment extends workload.Workload {
     };
   }
 
+  /**
+   * @see IScalable.markHasAutoscaler()
+   */
+  public markHasAutoscaler() {
+    this.hasAutoscaler = true;
+  }
+
+  /**
+   * @see IScalable.toScalingTarget()
+   */
+  public toScalingTarget(): ScalingTarget {
+    return {
+      kind: this.apiObject.kind,
+      apiVersion: this.apiObject.apiVersion,
+      name: this.name,
+      containers: this.containers,
+      replicas: this.replicas,
+    };
+  }
 }
 
 /**
diff --git a/src/horizontal-pod-autoscaler.ts b/src/horizontal-pod-autoscaler.ts
new file mode 100644
index 00000000..acf87f8e
--- /dev/null
+++ b/src/horizontal-pod-autoscaler.ts
@@ -0,0 +1,814 @@
+import { ApiObject, Duration, Lazy } from 'cdk8s';
+import { Construct } from 'constructs';
+import * as kplus from '../src';
+import { Resource, ResourceProps, IResource } from './base';
+import * as k8s from './imports/k8s';
+
+
+/**
+ * Properties used to configure the target of an Autoscaler.
+ */
+export interface ScalingTarget {
+  /**
+   * The object kind (e.g. "Deployment").
+   */
+  readonly kind: string;
+  /**
+   * The object's API version (e.g. "authorization.k8s.io/v1")
+   */
+  readonly apiVersion: string;
+  /**
+   * The Kubernetes name of this resource.
+   */
+  readonly name: string;
+  /**
+   * Container definitions associated with the target.
+   */
+  readonly containers: kplus.Container[];
+  /**
+   * The fixed number of replicas defined on the target. This is used
+   * for validation purposes as Scalable targets should not have a
+   * fixed number of replicas.
+   */
+  readonly replicas?: number;
+
+
+}
+
+/**
+ * Represents a scalable workload.
+ */
+export interface IScalable {
+  /**
+   * If this is a target of an autoscaler.
+   */
+  hasAutoscaler: boolean;
+  /**
+   * Called on all IScalable targets when they are associated with an autoscaler.
+   */
+  markHasAutoscaler(): void;
+  /**
+   * Return the target spec properties of this Scalable.
+   */
+  toScalingTarget(): ScalingTarget;
+}
+
+/**
+ * Properties for HorizontalPodAutoscaler
+ */
+export interface HorizontalPodAutoscalerProps extends ResourceProps {
+  /**
+   * The workload to scale up or down.
+   *
+   * Scalable workload types:
+   * * Deployment
+   * * StatefulSet
+   */
+  readonly target: IScalable;
+  /**
+   * The maximum number of replicas that can be scaled up to.
+   */
+  readonly maxReplicas: number;
+  /**
+   * The minimum number of replicas that can be scaled down to.
+   *
+   * Can be set to 0 if the alpha feature gate `HPAScaleToZero` is enabled and
+   * at least one Object or External metric is configured.
+   *
+   * @default 1
+   */
+  readonly minReplicas?: number;
+  /**
+   * The metric conditions that trigger a scale up or scale down.
+   *
+   * @default - If metrics are not provided, then the target resource
+   * constraints (e.g. cpu limit) will be used as scaling metrics.
+   */
+  readonly metrics?: Metric[];
+  /**
+   * The scaling behavior when scaling up.
+   *
+   * @default - Is the higher of:
+   * * Increase no more than 4 pods per 60 seconds
+   * * Double the number of pods per 60 seconds
+   */
+  readonly scaleUp?: ScalingRules;
+  /**
+   * The scaling behavior when scaling down.
+   *
+   * @default - Scale down to minReplica count with a 5 minute stabilization window.
+   */
+  readonly scaleDown?: ScalingRules;
+}
+
+/**
+ * A HorizontalPodAutoscaler scales a workload up or down in response to a metric
+ * change. This allows your services to scale up when demand is high and scale down
+ * when they are no longer needed.
+ *
+ *
+ * Typical use cases for HorizontalPodAutoscaler:
+ *
+ * * When Memory usage is above 70%, scale up the number of replicas to meet the demand.
+ * * When CPU usage is below 30%, scale down the number of replicas to save resources.
+ * * When a service is experiencing a spike in traffic, scale up the number of replicas
+ *   to meet the demand. Then, when the traffic subsides, scale down the number of
+ *   replicas to save resources.
+ *
+ * The autoscaler uses the following algorithm to determine the number of replicas to scale:
+ *
+ * `desiredReplicas = ceil[currentReplicas * ( currentMetricValue / desiredMetricValue )]`
+ *
+ * HorizontalPodAutoscaler's can be used to with any `Scalable` workload:
+ * * Deployment
+ * * StatefulSet
+ *
+ * **Targets that already have a replica count defined:**
+ *
+ * Remove any replica counts from the target resource before associating with a
+ * HorizontalPodAutoscaler. If this isn't done, then any time a change to that object is applied,
+ * Kubernetes will scale the current number of Pods to the value of the target.replicas key. This
+ * may not be desired and could lead to unexpected behavior.
+ *
+ * @see https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#implicit-maintenance-mode-deactivation
+ *
+ * @example
+ * const backend = new kplus.Deployment(this, 'Backend', ...);
+ *
+ * const hpa = new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+ *  target: backend,
+ *  maxReplicas: 10,
+ *  scaleUp: {
+ *    policies: [
+ *      {
+ *        replicas: kplus.Replicas.absolute(3),
+ *        duration: Duration.minutes(5),
+ *      },
+ *    ],
+ *  },
+ * });
+ */
+export class HorizontalPodAutoscaler extends Resource {
+  /**
+ * @see base.Resource.apiObject
+ */
+  protected readonly apiObject: ApiObject;
+  public readonly resourceType = 'horizontalpodautoscaler';
+  /**
+   * The workload to scale up or down.
+   */
+  public readonly target: IScalable;
+  /**
+   * The maximum number of replicas that can be scaled up to.
+   */
+  public readonly maxReplicas: number;
+  /**
+   * The minimum number of replicas that can be scaled down to.
+   */
+  public readonly minReplicas: number;
+  /**
+   * The metric conditions that trigger a scale up or scale down.
+   */
+  public readonly metrics?: Metric[];
+  /**
+   * The scaling behavior when scaling up.
+   */
+  public readonly scaleUp: ScalingRules;
+  /**
+   * The scaling behavior when scaling down.
+   */
+  public readonly scaleDown: ScalingRules;
+
+  private readonly _defaultScalingDuration = Duration.seconds(15);
+
+
+  constructor(scope: Construct, id: string, props: HorizontalPodAutoscalerProps) {
+    super(scope, id);
+
+    this.apiObject = new k8s.KubeHorizontalPodAutoscalerV2(this, 'Resource', {
+      metadata: props.metadata,
+      spec: Lazy.any({ produce: () => this._toKube() }),
+    });
+
+    if (props?.minReplicas && props.minReplicas > props.maxReplicas) {
+      throw new Error(`'minReplicas' (${props.minReplicas}) must be less than or equal to 'maxReplicas' (${props.maxReplicas}) in order for HorizontalPodAutoscaler to scale.`);
+    }
+    if (props?.scaleUp?.stabilizationWindow !== undefined) {
+      this._validateStabilizationWindow('scaleUp', props.scaleUp.stabilizationWindow);
+    }
+    if (props?.scaleDown?.stabilizationWindow !== undefined) {
+      this._validateStabilizationWindow('scaleDown', props.scaleDown.stabilizationWindow);
+    }
+    if (props?.scaleUp?.policies?.length) {
+      this._validateScalingPolicies('scaleUp', props.scaleUp.policies);
+    }
+    if (props?.scaleDown?.policies?.length) {
+      this._validateScalingPolicies('scaleDown', props.scaleDown.policies);
+    }
+
+    this.target = props.target;
+    this.target.markHasAutoscaler();
+    this.maxReplicas = props.maxReplicas;
+    this.minReplicas = props.minReplicas ?? 1;
+    this.metrics = props.metrics;
+    this.scaleUp = {
+      strategy: ScalingStrategy.MAX_CHANGE,
+      stabilizationWindow: Duration.seconds(0),
+      ...props.scaleUp,
+      policies: props.scaleUp?.policies?.map((p) => ({ duration: this._defaultScalingDuration, ...p })) ?? [
+        {
+          replicas: Replicas.absolute(4),
+          duration: Duration.minutes(1),
+        },
+        {
+          replicas: Replicas.percent(200),
+          duration: Duration.minutes(1),
+        },
+      ],
+    };
+    if (props?.scaleUp?.policies?.length) {
+      this._validateScalingPolicies('scaleUp', props.scaleUp.policies);
+    }
+    this.scaleDown = {
+      strategy: ScalingStrategy.MAX_CHANGE,
+      stabilizationWindow: Duration.minutes(5),
+      ...props.scaleDown,
+      policies: props.scaleDown?.policies?.map((p) => ({ duration: this._defaultScalingDuration, ...p })) ?? [
+        {
+          replicas: Replicas.absolute(this.minReplicas),
+          duration: Duration.minutes(5),
+        },
+      ],
+    };
+
+    this.node.addValidation({ validate: () => this._validateTargetReplicas() });
+    this.node.addValidation({ validate: () => this._validateTargetContainers() });
+  }
+
+  /**
+   * Validate a list of scaling policies.
+   * @internal
+   */
+  private _validateScalingPolicies(direction: 'scaleUp' | 'scaleDown', policies: ScalingPolicy[]) {
+    policies.forEach((p) => {
+      if (p.duration !== undefined) {
+        this._validateScalingPolicyDuration(direction, p.duration);
+      }
+    });
+  }
+
+  /**
+   * Validate `ScalingPolicy.duration` is within the allowed range.
+   *
+   * `duration` range: 1 second - 30 min
+   *
+   * Kubernetes name: `ScalingPolicy.periodSeconds`.
+   * @see https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/horizontal-pod-autoscaler-v2/#HorizontalPodAutoscalerSpec
+   * @internal
+   */
+  private _validateScalingPolicyDuration(direction: 'scaleUp' | 'scaleDown', duration: Duration) {
+    const periodSeconds = duration.toSeconds() ?? 15;
+    const isWithinRange = Boolean(0 < periodSeconds && periodSeconds <= 1800);
+    if (!isWithinRange) {
+      throw new Error(`'${direction}.policies' duration (${duration.toHumanString()}) is outside of the allowed range. Must be at least 1 second long and no longer than 30 minutes.`);
+    }
+  }
+
+  /**
+   * Validate `ScalingRules.stabilizationWindow` is within the allowed range.
+   *
+   * `stabilizationWindow` range: 0 seconds - 1 hour
+   *
+   * @see https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/horizontal-pod-autoscaler-v2/#HorizontalPodAutoscalerSpec
+   * @internal
+   */
+  private _validateStabilizationWindow(direction: 'scaleUp' | 'scaleDown', window: Duration) {
+    const windowSeconds = window.toSeconds();
+    const isWithinRange = Boolean(0 <= windowSeconds && windowSeconds <= 3600);
+    if (!isWithinRange) {
+      throw new Error(`'${direction}.stabilizationWindow' (${window.toHumanString()}) must be 0 seconds or more with a max of 1 hour.`);
+    }
+  }
+
+  /**
+   * Guarantee the HPA has a metric to scale on.
+   * Verify that metrics are configured, if not check every pod container has a resource limit or
+   * request defined.
+   * @internal
+   */
+  private _validateTargetContainers() {
+    const containers = this.target.toScalingTarget().containers;
+    const hasResourceConstraints = containers.some((c) => this._hasRequestsOrLimits(c));
+    if (!hasResourceConstraints && !this.metrics) {
+      return ['If HorizontalPodAutoscaler does not have metrics defined, then every container in the target must have a CPU or memory resource constraint defined.'];
+    }
+    return [];
+  }
+
+  /**
+   * Prevent the HPA from scaling a target with a replica count defined.
+   * @see https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#implicit-maintenance-mode-deactivation
+   * @internal
+   */
+  private _validateTargetReplicas() {
+    const replicas = this.target.toScalingTarget().replicas;
+    if (replicas) {
+      return [
+        `HorizontalPodAutoscaler target cannot have a fixed number of replicas (${replicas}).`,
+      ];
+    }
+    return [];
+  }
+
+  /**
+   * Validate that the container has at least one CPU/memory request/limit defined.
+   * @internal
+   */
+  private _hasRequestsOrLimits(container: kplus.Container): Boolean {
+    const hasRequests = container.resources?.cpu?.request || container.resources?.memory?.request;
+    const hasLimits = container.resources?.cpu?.limit || container.resources?.memory?.limit;
+    return Boolean(hasRequests || hasLimits);
+  }
+
+
+  /**
+   * @internal
+   */
+  public _toKube(): k8s.HorizontalPodAutoscalerSpecV2 {
+    return {
+      maxReplicas: this.maxReplicas,
+      minReplicas: this.minReplicas,
+      scaleTargetRef: this.target.toScalingTarget(),
+      metrics: this.metrics?.map(m => m._toKube()),
+      behavior: {
+        scaleUp: {
+          policies: this.scaleUp.policies?.map((p) => ({
+            ...p.replicas._toKube(),
+            periodSeconds: p.duration?.toSeconds() ?? this._defaultScalingDuration.toSeconds(),
+          })),
+          selectPolicy: this.scaleUp.strategy,
+          stabilizationWindowSeconds: this.scaleUp.stabilizationWindow?.toSeconds(),
+        },
+        scaleDown: {
+          policies: this.scaleDown.policies?.map((p) => ({
+            ...p.replicas._toKube(),
+            periodSeconds: p.duration?.toSeconds() ?? this._defaultScalingDuration.toSeconds(),
+          })),
+          selectPolicy: this.scaleDown.strategy,
+          stabilizationWindowSeconds: this.scaleDown.stabilizationWindow?.toSeconds(),
+        },
+      },
+    };
+  }
+}
+
+/**
+ * Base options for a Metric
+ */
+export interface MetricOptions {
+  /**
+   * The target metric value that will trigger scaling.
+   */
+  readonly target: MetricTarget;
+  /**
+  * The name of the metric to scale on.
+  */
+  readonly name: string;
+  /**
+   * A selector to find a metric by label.
+   *
+   * When set, it is passed as an additional parameter to the metrics server
+   * for more specific metrics scoping.
+   *
+   * @default - Just the metric 'name' will be used to gather metrics.
+   */
+  readonly labelSelector?: kplus.LabelSelector;
+}
+
+
+/**
+ * Options for `Metric.containerResource()`
+ */
+export interface MetricContainerResourceOptions {
+  /**
+   * Container where the metric can be found.
+   */
+  readonly container: kplus.Container;
+  /**
+   * Target metric value that will trigger scaling.
+   */
+  readonly target: MetricTarget;
+}
+
+/**
+ * Options for `Metric.object()`
+ */
+export interface MetricObjectOptions extends MetricOptions {
+  /**
+   * Resource where the metric can be found.
+   */
+  readonly object: IResource;
+}
+
+/**
+ * A metric condition that HorizontalPodAutoscaler's scale on.
+ */
+export class Metric {
+
+  /**
+   * Metric that tracks the CPU of a container. This metric
+   * will be tracked across all pods of the current scale target.
+   *
+   */
+  public static containerCpu(options: MetricContainerResourceOptions): Metric {
+    return new Metric({
+      type: 'ContainerResource',
+      containerResource: {
+        name: 'cpu',
+        container: options.container.name,
+        target: options.target._toKube(),
+      },
+    });
+  }
+
+  /**
+   * Metric that tracks the Memory of a container. This metric
+   * will be tracked across all pods of the current scale target.
+   *
+   */
+  public static containerMemory(options: MetricContainerResourceOptions): Metric {
+    return new Metric({
+      type: 'ContainerResource',
+      containerResource: {
+        name: 'memory',
+        container: options.container.name,
+        target: options.target._toKube(),
+      },
+    });
+  }
+
+  /**
+   * Metric that tracks the volume size of a container. This metric
+   * will be tracked across all pods of the current scale target.
+   *
+   */
+  public static containerStorage(options: MetricContainerResourceOptions): Metric {
+    return new Metric({
+      type: 'ContainerResource',
+      containerResource: {
+        name: 'storage',
+        container: options.container.name,
+        target: options.target._toKube(),
+      },
+    });
+  }
+
+  /**
+   * Metric that tracks the local ephemeral storage of a container. This metric
+   * will be tracked across all pods of the current scale target.
+   *
+   */
+  public static containerEphemeralStorage(options: MetricContainerResourceOptions): Metric {
+    return new Metric({
+      type: 'ContainerResource',
+      containerResource: {
+        name: 'ephemeral-storage',
+        container: options.container.name,
+        target: options.target._toKube(),
+      },
+    });
+  }
+
+  /**
+   * A global metric that is not associated with any Kubernetes object.
+   * Allows for autoscaling based on information coming from components running outside of
+   * the cluster.
+   *
+   * Use case:
+   * * Scale up when the length of an SQS queue is greater than 10 messages.
+   * * Scale down when an outside load balancer's queries are less than 10000 per second.
+   */
+  public static external(options: MetricOptions): Metric {
+    return new Metric({
+      type: 'External',
+      external: {
+        metric: {
+          name: options.name,
+          selector: options.labelSelector?._toKube(),
+        },
+        target: options.target._toKube(),
+      },
+    });
+  }
+
+  /**
+  * Metric that describes a metric of a kubernetes object
+  *
+  * Use case:
+  * * Scale on a Kubernetes Ingress's hits-per-second metric.
+  */
+  public static object(options: MetricObjectOptions): Metric {
+    return new Metric({
+      type: 'Object',
+      object: {
+        describedObject: {
+          apiVersion: options.object.apiVersion,
+          kind: options.object.kind,
+          name: options.object.name,
+        },
+        metric: {
+          name: options.name,
+          selector: options.labelSelector?._toKube(),
+        },
+        target: options.target._toKube(),
+      },
+    });
+  }
+
+  /**
+   * A pod metric that will be averaged across all pods of the current scale target.
+   *
+   * Use case:
+   * * Average CPU utilization across all pods
+   * * Transactions processed per second across all pods
+   */
+  public static pods(options: MetricOptions): Metric {
+    return new Metric({
+      type: 'Pods',
+      pods: {
+        metric: {
+          name: options.name,
+          selector: options.labelSelector?._toKube(),
+        },
+        target: options.target._toKube(),
+      },
+    });
+  }
+
+  /**
+   * Tracks the available CPU of the pods in a target.
+   *
+   * Note: Since the resource usages of all the containers are summed up the total
+   * pod utilization may not accurately represent the individual container resource
+   * usage. This could lead to situations where a single container might be running
+   * with high usage and the HPA will not scale out because the overall pod usage
+   * is still within acceptable limits.
+   *
+   * Use case:
+   * * Scale up when CPU is above 40%.
+   */
+  public static resourceCpu(target: MetricTarget): Metric {
+    return new Metric({
+      type: 'Resource',
+      resource: {
+        name: 'cpu',
+        target: target._toKube(),
+      },
+    });
+  }
+
+  /**
+   * Tracks the available Memory of the pods in a target.
+   *
+   * Note: Since the resource usages of all the containers are summed up the total
+   * pod utilization may not accurately represent the individual container resource
+   * usage. This could lead to situations where a single container might be running
+   * with high usage and the HPA will not scale out because the overall pod usage
+   * is still within acceptable limits.
+   *
+   * Use case:
+   * * Scale up when Memory is above 512MB.
+   */
+  public static resourceMemory(target: MetricTarget): Metric {
+    return new Metric({
+      type: 'Resource',
+      resource: {
+        name: 'memory',
+        target: target._toKube(),
+      },
+    });
+  }
+
+  /**
+   * Tracks the available Storage of the pods in a target.
+   *
+   * Note: Since the resource usages of all the containers are summed up the total
+   * pod utilization may not accurately represent the individual container resource
+   * usage. This could lead to situations where a single container might be running
+   * with high usage and the HPA will not scale out because the overall pod usage
+   * is still within acceptable limits.
+   *
+   */
+  public static resourceStorage(target: MetricTarget): Metric {
+    return new Metric({
+      type: 'Resource',
+      resource: {
+        name: 'storage',
+        target: target._toKube(),
+      },
+    });
+  }
+
+  /**
+   * Tracks the available Ephemeral Storage of the pods in a target.
+   *
+   * Note: Since the resource usages of all the containers are summed up the total
+   * pod utilization may not accurately represent the individual container resource
+   * usage. This could lead to situations where a single container might be running
+   * with high usage and the HPA will not scale out because the overall pod usage
+   * is still within acceptable limits.
+   *
+   */
+  public static resourceEphemeralStorage(target: MetricTarget): Metric {
+    return new Metric({
+      type: 'Resource',
+      resource: {
+        name: 'ephemeral-storage',
+        target: target._toKube(),
+      },
+    });
+  }
+
+  public readonly type: string;
+  private constructor(private readonly metric: k8s.MetricSpecV2) {
+    this.type = metric.type;
+  }
+
+  /**
+   * @internal
+   */
+  public _toKube(): k8s.MetricSpecV2 {
+    return this.metric;
+  }
+
+}
+
+
+/**
+ * A metric condition that will trigger scaling behavior when satisfied.
+ *
+ * @example
+ *
+ * MetricTarget.averageUtilization(70); // 70% average utilization
+ *
+ */
+export class MetricTarget {
+  /**
+  * Target a specific target value.
+  *
+  * @param value The target value.
+  */
+  public static value(value: number): MetricTarget {
+    return new MetricTarget({
+      type: 'Value',
+      value: k8s.Quantity.fromNumber(value),
+    });
+  }
+
+  /**
+   * Target the average value across all relevant pods.
+   *
+   * @param averageValue The average metric value.
+   */
+  public static averageValue(averageValue: number): MetricTarget {
+    return new MetricTarget({
+      type: 'AverageValue',
+      averageValue: k8s.Quantity.fromNumber(averageValue),
+    });
+  }
+
+  /**
+   * Target a percentage value across all relevant pods.
+   *
+   * @param averageUtilization The percentage of the utilization metric. e.g. `50` for 50%.
+   */
+  public static averageUtilization(averageUtilization: number): MetricTarget {
+    return new MetricTarget({
+      type: 'Utilization',
+      averageUtilization,
+    });
+  }
+
+  private constructor(private readonly metric: k8s.MetricTargetV2) { }
+
+  /**
+   * @internal
+   */
+  public _toKube(): k8s.MetricSpecV2 {
+    return this.metric;
+  }
+}
+
+/**
+ * Defines the scaling behavior for one direction.
+ */
+export interface ScalingRules {
+  /**
+   * Defines the window of past metrics that the autoscaler should consider when calculating
+   * wether or not autoscaling should occur.
+   *
+   * Minimum duration is 1 second, max is 1 hour.
+   *
+   * @example
+   * stabilizationWindow: Duration.minutes(30)
+   * // Autoscaler considers the last 30 minutes of metrics when deciding whether to scale.
+   *
+   * @default * On scale down no stabilization is performed.
+   * * On scale up stabilization is performed for 5 minutes.
+   */
+  readonly stabilizationWindow?: Duration;
+  /**
+   * The strategy to use when scaling.
+   *
+   * @default MAX_CHANGE
+   */
+  readonly strategy?: ScalingStrategy;
+  /**
+   * The scaling policies.
+   *
+   * @default * Scale up
+   *            * Increase no more than 4 pods per 60 seconds
+   *            * Double the number of pods per 60 seconds
+   *          * Scale down
+   *            * Decrease to minReplica count
+   */
+  readonly policies?: ScalingPolicy[];
+}
+
+
+export enum ScalingStrategy {
+  /**
+   * Use the policy that provisions the most changes.
+   */
+  MAX_CHANGE = 'Max',
+  /**
+   * Use the policy that provisions the least amount of changes.
+   */
+  MIN_CHANGE = 'Min',
+  /**
+   * Disables scaling in this direction.
+   *
+   * @deprecated - Omit the ScalingRule instead
+   */
+  DISABLED = 'Disabled',
+}
+
+export interface ScalingPolicy {
+  /**
+   * The type and quantity of replicas to change.
+   */
+  readonly replicas: Replicas;
+  /**
+   * The amount of time the scaling policy has to
+   * continue scaling before the target metric must be
+   * revalidated.
+   *
+   * Must be greater than 0 seconds and no longer than 30 minutes.
+   *
+   * @default - 15 seconds
+   */
+  readonly duration?: Duration;
+}
+
+
+/**
+ * The amount of replicas that will change.
+ */
+export class Replicas {
+
+  /**
+   * Changes the pods by a percentage of the it's current value.
+   *
+   * @param value The percentage of change to apply. Must be greater than 0.
+   */
+  public static percent(value: number) {
+    return new Replicas ({
+      type: 'Percent',
+      value,
+    });
+  }
+
+  /**
+   * Changes the pods by a percentage of the it's current value.
+   *
+   * @param value The amount of change to apply. Must be greater than 0.
+   */
+  public static absolute(value: number) {
+    return new Replicas({
+      type: 'Pods',
+      value,
+    });
+  }
+
+  private constructor(private readonly replicas: Pick<k8s.HpaScalingPolicyV2, 'type' | 'value'>) { }
+
+  /**
+   * @internal
+   */
+  public _toKube(): Pick<k8s.HpaScalingPolicyV2, 'type' | 'value'> {
+    return {
+      type: this.replicas.type,
+      value: this.replicas.value,
+    };
+  }
+}
diff --git a/src/index.ts b/src/index.ts
index 9b50def6..f66b7e95 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -15,6 +15,7 @@ export * from './probe';
 export * from './pvc';
 export * from './pv';
 export * from './handler';
+export * from './horizontal-pod-autoscaler';
 export * from './workload';
 export * from './daemon-set';
 export * from './role';
diff --git a/src/stateful-set.ts b/src/stateful-set.ts
index 62ea7732..62fd1286 100644
--- a/src/stateful-set.ts
+++ b/src/stateful-set.ts
@@ -1,6 +1,7 @@
 import { ApiObject, Lazy, Duration } from 'cdk8s';
 import { Construct } from 'constructs';
 import * as container from './container';
+import { IScalable, ScalingTarget } from './horizontal-pod-autoscaler';
 import * as k8s from './imports/k8s';
 import * as service from './service';
 import * as workload from './workload';
@@ -92,11 +93,11 @@ export interface StatefulSetProps extends workload.WorkloadProps {
  * - Ordered, graceful deployment and scaling.
  * - Ordered, automated rolling updates.
  */
-export class StatefulSet extends workload.Workload {
+export class StatefulSet extends workload.Workload implements IScalable {
   /**
     * Number of desired pods.
     */
-  public readonly replicas: number;
+  public readonly replicas?: number;
 
   /**
     * Management policy to use for the set.
@@ -121,6 +122,8 @@ export class StatefulSet extends workload.Workload {
 
   public readonly resourceType = 'statefulsets';
 
+  public hasAutoscaler = false;
+
   public readonly service: service.Service;
 
   constructor(scope: Construct, id: string, props: StatefulSetProps) {
@@ -134,7 +137,7 @@ export class StatefulSet extends workload.Workload {
 
     this.apiObject.addDependency(this.service);
 
-    this.replicas = props.replicas ?? 1;
+    this.replicas = props.replicas;
     this.strategy = props.strategy ?? StatefulSetUpdateStrategy.rollingUpdate(),
     this.podManagementPolicy = props.podManagementPolicy ?? PodManagementPolicy.ORDERED_READY;
     this.minReady = props.minReady ?? Duration.seconds(0);
@@ -179,10 +182,9 @@ export class StatefulSet extends workload.Workload {
     * @internal
     */
   public _toKube(): k8s.StatefulSetSpec {
-
     return {
+      replicas: this.hasAutoscaler ? undefined : (this.replicas ?? 1),
       serviceName: this.service.name,
-      replicas: this.replicas,
       minReadySeconds: this.minReady.toSeconds(),
       template: {
         metadata: this.podMetadata.toJson(),
@@ -204,6 +206,26 @@ export class StatefulSet extends workload.Workload {
         throw new Error(`Unsupported pod management policy: ${podManagementPolicy}`);
     }
   }
+
+  /**
+   * @see IScalable.markHasAutoscaler()
+   */
+  public markHasAutoscaler() {
+    this.hasAutoscaler = true;
+  }
+
+  /**
+   * @see IScalable.toScalingTarget()
+   */
+  public toScalingTarget(): ScalingTarget {
+    return {
+      kind: this.apiObject.kind,
+      apiVersion: this.apiObject.apiVersion,
+      name: this.name,
+      containers: this.containers,
+      replicas: this.replicas,
+    };
+  }
 }
 
 /**
@@ -267,4 +289,4 @@ export class StatefulSetUpdateStrategy {
     return this.strategy;
   }
 
-}
\ No newline at end of file
+}


Test patch:
diff --git a/test/__snapshots__/horizontal-pod-autoscaler.test.ts.snap b/test/__snapshots__/horizontal-pod-autoscaler.test.ts.snap
new file mode 100644
index 00000000..f333c750
--- /dev/null
+++ b/test/__snapshots__/horizontal-pod-autoscaler.test.ts.snap
@@ -0,0 +1,2925 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`creates HPA with 2 default scaleUp policies and 1 default scaleDown policy, when all other scaleUp and scaleDown options are provided 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "pod",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "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": 300,
+        },
+      },
+      "maxReplicas": 10,
+      "minReplicas": 1,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c898c72d",
+      },
+    },
+  },
+]
+`;
+
+exports[`creates HPA with CPU ContainerResource metric, when provided a Metric.containerCpu() 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "pod",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "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": 10,
+      "metrics": Array [
+        Object {
+          "containerResource": Object {
+            "container": "main",
+            "name": "cpu",
+            "target": Object {
+              "averageUtilization": 50,
+              "type": "Utilization",
+            },
+          },
+          "type": "ContainerResource",
+        },
+      ],
+      "minReplicas": 1,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c898c72d",
+      },
+    },
+  },
+]
+`;
+
+exports[`creates HPA with Ephemeral Storage ContainerResource metric, when provided a Metric.containerStorage() 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "pod",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "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": 10,
+      "metrics": Array [
+        Object {
+          "containerResource": Object {
+            "container": "main",
+            "name": "ephemeral-storage",
+            "target": Object {
+              "averageUtilization": 50,
+              "type": "Utilization",
+            },
+          },
+          "type": "ContainerResource",
+        },
+      ],
+      "minReplicas": 1,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c898c72d",
+      },
+    },
+  },
+]
+`;
+
+exports[`creates HPA with Memory ContainerResource metric, when provided a Metric.containerMemory() 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "pod",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "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": 10,
+      "metrics": Array [
+        Object {
+          "containerResource": Object {
+            "container": "main",
+            "name": "memory",
+            "target": Object {
+              "averageUtilization": 50,
+              "type": "Utilization",
+            },
+          },
+          "type": "ContainerResource",
+        },
+      ],
+      "minReplicas": 1,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c898c72d",
+      },
+    },
+  },
+]
+`;
+
+exports[`creates HPA with Resource CPU metric targeting 47.2 average value, when provided a MetricTarget.averageUtilization() 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "pod",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "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": 10,
+      "metrics": Array [
+        Object {
+          "resource": Object {
+            "name": "cpu",
+            "target": Object {
+              "averageValue": 47.2,
+              "type": "AverageValue",
+            },
+          },
+          "type": "Resource",
+        },
+      ],
+      "minReplicas": 1,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c898c72d",
+      },
+    },
+  },
+]
+`;
+
+exports[`creates HPA with Resource CPU metric targeting 70% average utilization, when provided a MetricTarget.averageUtilization() 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "pod",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "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": 10,
+      "metrics": Array [
+        Object {
+          "resource": Object {
+            "name": "cpu",
+            "target": Object {
+              "averageUtilization": 70,
+              "type": "Utilization",
+            },
+          },
+          "type": "Resource",
+        },
+      ],
+      "minReplicas": 1,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c898c72d",
+      },
+    },
+  },
+]
+`;
+
+exports[`creates HPA with Resource CPU metric targeting the exact value of 29.5, when provided a MetricTarget.value() 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "pod",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "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": 10,
+      "metrics": Array [
+        Object {
+          "resource": Object {
+            "name": "cpu",
+            "target": Object {
+              "type": "Value",
+              "value": 29.5,
+            },
+          },
+          "type": "Resource",
+        },
+      ],
+      "minReplicas": 1,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c898c72d",
+      },
+    },
+  },
+]
+`;
+
+exports[`creates HPA with Resource CPU metric, when provided a Metric.resourceCpu() 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "pod",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "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": 10,
+      "metrics": Array [
+        Object {
+          "resource": Object {
+            "name": "cpu",
+            "target": Object {
+              "averageUtilization": 50,
+              "type": "Utilization",
+            },
+          },
+          "type": "Resource",
+        },
+      ],
+      "minReplicas": 1,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c898c72d",
+      },
+    },
+  },
+]
+`;
+
+exports[`creates HPA with Resource Ephemeral Storage metric, when provided a Metric.resourceEphemeralStorage() 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "pod",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "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": 10,
+      "metrics": Array [
+        Object {
+          "resource": Object {
+            "name": "ephemeral-storage",
+            "target": Object {
+              "averageUtilization": 50,
+              "type": "Utilization",
+            },
+          },
+          "type": "Resource",
+        },
+      ],
+      "minReplicas": 1,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c898c72d",
+      },
+    },
+  },
+]
+`;
+
+exports[`creates HPA with Resource Memory metric, when provided a Metric.resourceMemory() 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "pod",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "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": 10,
+      "metrics": Array [
+        Object {
+          "resource": Object {
+            "name": "memory",
+            "target": Object {
+              "averageUtilization": 50,
+              "type": "Utilization",
+            },
+          },
+          "type": "Resource",
+        },
+      ],
+      "minReplicas": 1,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c898c72d",
+      },
+    },
+  },
+]
+`;
+
+exports[`creates HPA with Resource Storage metric, when provided a Metric.resourceStorage() 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "pod",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "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": 10,
+      "metrics": Array [
+        Object {
+          "resource": Object {
+            "name": "storage",
+            "target": Object {
+              "averageUtilization": 50,
+              "type": "Utilization",
+            },
+          },
+          "type": "Resource",
+        },
+      ],
+      "minReplicas": 1,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c898c72d",
+      },
+    },
+  },
+]
+`;
+
+exports[`creates HPA with Storage ContainerResource metric, when provided a Metric.containerStorage() 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "pod",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "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": 10,
+      "metrics": Array [
+        Object {
+          "containerResource": Object {
+            "container": "main",
+            "name": "storage",
+            "target": Object {
+              "averageUtilization": 50,
+              "type": "Utilization",
+            },
+          },
+          "type": "ContainerResource",
+        },
+      ],
+      "minReplicas": 1,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c898c72d",
+      },
+    },
+  },
+]
+`;
+
+exports[`creates HPA with expected spec, when metrics, scaleUp, and scaleDown are all configured 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "pod",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "spec": Object {
+      "behavior": Object {
+        "scaleDown": Object {
+          "policies": Array [
+            Object {
+              "periodSeconds": 180,
+              "type": "Pods",
+              "value": 3,
+            },
+            Object {
+              "periodSeconds": 15,
+              "type": "Percent",
+              "value": 30,
+            },
+          ],
+          "selectPolicy": "Max",
+          "stabilizationWindowSeconds": 300,
+        },
+        "scaleUp": Object {
+          "policies": Array [
+            Object {
+              "periodSeconds": 180,
+              "type": "Pods",
+              "value": 3,
+            },
+            Object {
+              "periodSeconds": 15,
+              "type": "Percent",
+              "value": 30,
+            },
+          ],
+          "selectPolicy": "Max",
+          "stabilizationWindowSeconds": 300,
+        },
+      },
+      "maxReplicas": 10,
+      "metrics": Array [
+        Object {
+          "resource": Object {
+            "name": "cpu",
+            "target": Object {
+              "averageUtilization": 50,
+              "type": "Utilization",
+            },
+          },
+          "type": "Resource",
+        },
+      ],
+      "minReplicas": 2,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c898c72d",
+      },
+    },
+  },
+]
+`;
+
+exports[`creates HPA with external metric, when provided a Metric.external() 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "pod",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "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": 10,
+      "metrics": Array [
+        Object {
+          "external": Object {
+            "metric": Object {
+              "name": "sqs-queue",
+              "selector": Object {
+                "matchLabels": Object {
+                  "app": "scraper",
+                },
+              },
+            },
+            "target": Object {
+              "averageUtilization": 50,
+              "type": "Utilization",
+            },
+          },
+          "type": "External",
+        },
+      ],
+      "minReplicas": 1,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c898c72d",
+      },
+    },
+  },
+]
+`;
+
+exports[`creates HPA with object metric, when provided a Metric.object() 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "pod",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "v1",
+    "kind": "Service",
+    "metadata": Object {
+      "name": "test-my-service-c8493104",
+    },
+    "spec": Object {
+      "externalIPs": Array [],
+      "ports": Array [
+        Object {
+          "port": 80,
+        },
+      ],
+      "selector": Object {},
+      "type": "ClusterIP",
+    },
+  },
+  Object {
+    "apiVersion": "networking.k8s.io/v1",
+    "kind": "Ingress",
+    "metadata": Object {
+      "name": "test-my-ingress-c8135042",
+    },
+    "spec": Object {
+      "defaultBackend": Object {
+        "service": Object {
+          "name": "test-my-service-c8493104",
+          "port": Object {
+            "number": 80,
+          },
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "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": 10,
+      "metrics": Array [
+        Object {
+          "object": Object {
+            "describedObject": Object {
+              "apiVersion": "networking.k8s.io/v1",
+              "kind": "Ingress",
+              "name": "test-my-ingress-c8135042",
+            },
+            "metric": Object {
+              "name": "requests-per-second",
+            },
+            "target": Object {
+              "averageUtilization": 50,
+              "type": "Utilization",
+            },
+          },
+          "type": "Object",
+        },
+      ],
+      "minReplicas": 1,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c898c72d",
+      },
+    },
+  },
+]
+`;
+
+exports[`creates HPA with pods metric, when provided a Metric.pods() 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "pod",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "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": 10,
+      "metrics": Array [
+        Object {
+          "pods": Object {
+            "metric": Object {
+              "name": "requests-per-second",
+              "selector": Object {
+                "matchLabels": Object {
+                  "app": "scraper",
+                },
+              },
+            },
+            "target": Object {
+              "averageUtilization": 50,
+              "type": "Utilization",
+            },
+          },
+          "type": "Pods",
+        },
+      ],
+      "minReplicas": 1,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c898c72d",
+      },
+    },
+  },
+]
+`;
+
+exports[`creates HPA with scaleUp and scaleDown policies with the default 15 second periodSeconds, when policies are provided without duration option 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "pod",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "spec": Object {
+      "behavior": Object {
+        "scaleDown": Object {
+          "policies": Array [
+            Object {
+              "periodSeconds": 15,
+              "type": "Pods",
+              "value": 3,
+            },
+            Object {
+              "periodSeconds": 15,
+              "type": "Percent",
+              "value": 30,
+            },
+          ],
+          "selectPolicy": "Max",
+          "stabilizationWindowSeconds": 300,
+        },
+        "scaleUp": Object {
+          "policies": Array [
+            Object {
+              "periodSeconds": 15,
+              "type": "Pods",
+              "value": 3,
+            },
+            Object {
+              "periodSeconds": 15,
+              "type": "Percent",
+              "value": 30,
+            },
+          ],
+          "selectPolicy": "Max",
+          "stabilizationWindowSeconds": 0,
+        },
+      },
+      "maxReplicas": 10,
+      "minReplicas": 1,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c898c72d",
+      },
+    },
+  },
+]
+`;
+
+exports[`creates HPA with two different scaling strategies, when provided a scaleUp strategy of Max and scaleDown strategy of Min 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "pod",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "spec": Object {
+      "behavior": Object {
+        "scaleDown": Object {
+          "policies": Array [
+            Object {
+              "periodSeconds": 180,
+              "type": "Pods",
+              "value": 3,
+            },
+          ],
+          "selectPolicy": "Min",
+          "stabilizationWindowSeconds": 300,
+        },
+        "scaleUp": Object {
+          "policies": Array [
+            Object {
+              "periodSeconds": 180,
+              "type": "Pods",
+              "value": 3,
+            },
+          ],
+          "selectPolicy": "Max",
+          "stabilizationWindowSeconds": 300,
+        },
+      },
+      "maxReplicas": 10,
+      "metrics": Array [
+        Object {
+          "resource": Object {
+            "name": "cpu",
+            "target": Object {
+              "averageUtilization": 50,
+              "type": "Utilization",
+            },
+          },
+          "type": "Resource",
+        },
+      ],
+      "minReplicas": 2,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c898c72d",
+      },
+    },
+  },
+]
+`;
+
+exports[`creates HPA, when metrics are not provided and one of the two target containers does not have any resources limits/requests 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "pod1",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+            Object {
+              "image": "pod2",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "requests": Object {
+                  "cpu": "256m",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "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": 10,
+      "minReplicas": 1,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c898c72d",
+      },
+    },
+  },
+]
+`;
+
+exports[`creates HPA, when minReplicas is same as maxReplicas 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "pod",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "spec": Object {
+      "behavior": Object {
+        "scaleDown": Object {
+          "policies": Array [
+            Object {
+              "periodSeconds": 300,
+              "type": "Pods",
+              "value": 10,
+            },
+          ],
+          "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": 10,
+      "minReplicas": 10,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c898c72d",
+      },
+    },
+  },
+]
+`;
+
+exports[`creates HPA, when target is a StatefulSet 1`] = `
+Array [
+  Object {
+    "apiVersion": "v1",
+    "kind": "Service",
+    "metadata": Object {
+      "name": "test-testservice-c876eb6f",
+    },
+    "spec": Object {
+      "externalIPs": Array [],
+      "ports": Array [
+        Object {
+          "port": 80,
+        },
+      ],
+      "selector": Object {
+        "cdk8s.io/metadata.addr": "test-StatefulSet-c809b559",
+      },
+      "type": "ClusterIP",
+    },
+  },
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "StatefulSet",
+    "metadata": Object {
+      "name": "test-statefulset-c8a6ec86",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "podManagementPolicy": "OrderedReady",
+      "selector": Object {},
+      "serviceName": "test-testservice-c876eb6f",
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-StatefulSet-c809b559",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "foobar",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+      "updateStrategy": Object {
+        "rollingUpdate": Object {
+          "partition": 0,
+        },
+        "type": "RollingUpdate",
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "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": 10,
+      "minReplicas": 1,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "StatefulSet",
+        "name": "test-statefulset-c8a6ec86",
+      },
+    },
+  },
+]
+`;
+
+exports[`creates HPA, when target is a deployment 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "pod",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "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": 10,
+      "minReplicas": 1,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c898c72d",
+      },
+    },
+  },
+]
+`;
+
+exports[`default configuration 1`] = `
+Array [
+  Object {
+    "apiVersion": "apps/v1",
+    "kind": "Deployment",
+    "metadata": Object {
+      "name": "test-deployment-c898c72d",
+    },
+    "spec": Object {
+      "minReadySeconds": 0,
+      "progressDeadlineSeconds": 600,
+      "selector": Object {
+        "matchLabels": Object {
+          "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+        },
+      },
+      "strategy": Object {
+        "rollingUpdate": Object {
+          "maxSurge": "25%",
+          "maxUnavailable": "25%",
+        },
+        "type": "RollingUpdate",
+      },
+      "template": Object {
+        "metadata": Object {
+          "labels": Object {
+            "cdk8s.io/metadata.addr": "test-Deployment-c83f5e59",
+          },
+        },
+        "spec": Object {
+          "automountServiceAccountToken": false,
+          "containers": Array [
+            Object {
+              "image": "pod",
+              "imagePullPolicy": "Always",
+              "name": "main",
+              "resources": Object {
+                "limits": Object {
+                  "cpu": "1500m",
+                  "memory": "2048Mi",
+                },
+                "requests": Object {
+                  "cpu": "1000m",
+                  "memory": "512Mi",
+                },
+              },
+              "securityContext": Object {
+                "allowPrivilegeEscalation": false,
+                "privileged": false,
+                "readOnlyRootFilesystem": true,
+                "runAsGroup": 26000,
+                "runAsNonRoot": true,
+                "runAsUser": 25000,
+              },
+            },
+          ],
+          "dnsPolicy": "ClusterFirst",
+          "restartPolicy": "Always",
+          "securityContext": Object {
+            "fsGroupChangePolicy": "Always",
+            "runAsNonRoot": true,
+          },
+          "setHostnameAsFQDN": false,
+        },
+      },
+    },
+  },
+  Object {
+    "apiVersion": "autoscaling/v2",
+    "kind": "HorizontalPodAutoscaler",
+    "metadata": Object {
+      "name": "test-hpa-c8e6eb3e",
+    },
+    "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": 10,
+      "minReplicas": 1,
+      "scaleTargetRef": Object {
+        "apiVersion": "apps/v1",
+        "kind": "Deployment",
+        "name": "test-deployment-c898c72d",
+      },
+    },
+  },
+]
+`;
diff --git a/test/horizontal-pod-autoscaler.test.ts b/test/horizontal-pod-autoscaler.test.ts
new file mode 100644
index 00000000..c4ce1df2
--- /dev/null
+++ b/test/horizontal-pod-autoscaler.test.ts
@@ -0,0 +1,1082 @@
+import { Testing, Duration, ApiObject } from 'cdk8s';
+import { Node } from 'constructs';
+import * as kplus from '../src';
+
+// Checking defaults
+test('defaultChild', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  const hpa = new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+  });
+
+  const defaultChild = Node.of(hpa).defaultChild as ApiObject;
+  expect(defaultChild.kind).toEqual('HorizontalPodAutoscaler');
+});
+
+test('default configuration', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[1].spec;
+  expect(spec.maxReplicas).toEqual(10);
+  expect(spec.minReplicas).toEqual(1);
+  expect(spec.metrics).toEqual(undefined);
+  expect(spec.behavior.scaleUp).toEqual({
+    policies: [
+      {
+        periodSeconds: 60,
+        type: 'Pods',
+        value: 4,
+      },
+      {
+        periodSeconds: 60,
+        type: 'Percent',
+        value: 200,
+      },
+    ],
+    selectPolicy: 'Max',
+    stabilizationWindowSeconds: 0,
+  });
+  expect(spec.behavior.scaleDown).toEqual({
+    policies: [
+      { periodSeconds: 300, type: 'Pods', value: 1 },
+    ],
+    selectPolicy: 'Max',
+    stabilizationWindowSeconds: 300,
+  });
+});
+
+test('creates HPA with 2 default scaleUp policies and 1 default scaleDown policy, when all other scaleUp and scaleDown options are provided', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+    scaleUp: {
+      stabilizationWindow: Duration.minutes(5),
+      strategy: kplus.ScalingStrategy.MAX_CHANGE,
+    },
+    scaleDown: {
+      stabilizationWindow: Duration.minutes(5),
+      strategy: kplus.ScalingStrategy.MAX_CHANGE,
+    },
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[1].spec;
+  expect(spec.maxReplicas).toEqual(10);
+  expect(spec.behavior.scaleUp).toEqual({
+    policies: [
+      {
+        periodSeconds: 60,
+        type: 'Pods',
+        value: 4,
+      },
+      {
+        periodSeconds: 60,
+        type: 'Percent',
+        value: 200,
+      },
+    ],
+    selectPolicy: 'Max',
+    stabilizationWindowSeconds: 300,
+  });
+  expect(spec.behavior.scaleDown).toEqual({
+    policies: [{
+      periodSeconds: 300,
+      type: 'Pods',
+      value: 1,
+    }],
+    selectPolicy: 'Max',
+    stabilizationWindowSeconds: 300,
+  });
+});
+
+
+test('creates HPA with scaleUp and scaleDown policies with the default 15 second periodSeconds, when policies are provided without duration option', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+    scaleUp: {
+      policies: [
+        {
+          replicas: kplus.Replicas.absolute(3),
+        },
+        {
+          replicas: kplus.Replicas.percent(30),
+        },
+      ],
+    },
+    scaleDown: {
+      policies: [
+        {
+          replicas: kplus.Replicas.absolute(3),
+        },
+        {
+          replicas: kplus.Replicas.percent(30),
+        },
+      ],
+    },
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[1].spec;
+  expect(spec.maxReplicas).toEqual(10);
+  expect(spec.behavior.scaleUp).toEqual({
+    policies: [
+      { periodSeconds: 15, type: 'Pods', value: 3 },
+      { periodSeconds: 15, type: 'Percent', value: 30 },
+    ],
+    selectPolicy: 'Max',
+    stabilizationWindowSeconds: 0,
+  });
+  expect(spec.behavior.scaleDown).toEqual({
+    policies: [
+      { periodSeconds: 15, type: 'Pods', value: 3 },
+      { periodSeconds: 15, type: 'Percent', value: 30 },
+    ],
+    selectPolicy: 'Max',
+    stabilizationWindowSeconds: 300,
+  });
+});
+
+test('creates HPA with two different scaling strategies, when provided a scaleUp strategy of Max and scaleDown strategy of Min', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+    minReplicas: 2,
+    metrics: [
+      kplus.Metric.resourceCpu(kplus.MetricTarget.averageUtilization(50)),
+    ],
+    scaleUp: {
+      stabilizationWindow: Duration.minutes(5),
+      strategy: kplus.ScalingStrategy.MAX_CHANGE,
+      policies: [
+        {
+          replicas: kplus.Replicas.absolute(3),
+          duration: Duration.minutes(3),
+        },
+      ],
+    },
+    scaleDown: {
+      stabilizationWindow: Duration.minutes(5),
+      strategy: kplus.ScalingStrategy.MIN_CHANGE,
+      policies: [
+        {
+          replicas: kplus.Replicas.absolute(3),
+          duration: Duration.minutes(3),
+        },
+      ],
+    },
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[1].spec;
+  expect(spec.maxReplicas).toEqual(10);
+  expect(spec.minReplicas).toEqual(2);
+  expect(spec.metrics).toEqual([{
+    resource: {
+      name: 'cpu',
+      target: {
+        averageUtilization: 50,
+        type: 'Utilization',
+      },
+    },
+    type: 'Resource',
+  }]);
+  expect(spec.behavior.scaleUp).toEqual({
+    policies: [
+      { periodSeconds: 180, type: 'Pods', value: 3 },
+    ],
+    selectPolicy: 'Max',
+    stabilizationWindowSeconds: 300,
+  });
+  expect(spec.behavior.scaleDown).toEqual({
+    policies: [
+      { periodSeconds: 180, type: 'Pods', value: 3 },
+    ],
+    selectPolicy: 'Min',
+    stabilizationWindowSeconds: 300,
+  });
+});
+
+// Metrics
+
+test('creates HPA with CPU ContainerResource metric, when provided a Metric.containerCpu()', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+    metrics: [
+      kplus.Metric.containerCpu({
+        container: deployment.containers[0],
+        target: kplus.MetricTarget.averageUtilization(50),
+      }),
+    ],
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[1].spec;
+  expect(spec.maxReplicas).toEqual(10);
+  expect(spec.metrics).toEqual([{
+    type: 'ContainerResource',
+    containerResource: {
+      container: 'main',
+      name: 'cpu',
+      target: {
+        averageUtilization: 50,
+        type: 'Utilization',
+      },
+    },
+  }]);
+});
+
+
+test('creates HPA with Memory ContainerResource metric, when provided a Metric.containerMemory()', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+    metrics: [
+      kplus.Metric.containerMemory({
+        container: deployment.containers[0],
+        target: kplus.MetricTarget.averageUtilization(50),
+      }),
+    ],
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[1].spec;
+  expect(spec.maxReplicas).toEqual(10);
+  expect(spec.metrics).toEqual([{
+    type: 'ContainerResource',
+    containerResource: {
+      container: 'main',
+      name: 'memory',
+      target: {
+        averageUtilization: 50,
+        type: 'Utilization',
+      },
+    },
+  }]);
+});
+
+test('creates HPA with Storage ContainerResource metric, when provided a Metric.containerStorage()', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+    metrics: [
+      kplus.Metric.containerStorage({
+        container: deployment.containers[0],
+        target: kplus.MetricTarget.averageUtilization(50),
+      }),
+    ],
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[1].spec;
+  expect(spec.maxReplicas).toEqual(10);
+  expect(spec.metrics).toEqual([{
+    type: 'ContainerResource',
+    containerResource: {
+      container: 'main',
+      name: 'storage',
+      target: {
+        averageUtilization: 50,
+        type: 'Utilization',
+      },
+    },
+  }]);
+});
+
+test('creates HPA with Ephemeral Storage ContainerResource metric, when provided a Metric.containerStorage()', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+    metrics: [
+      kplus.Metric.containerEphemeralStorage({
+        container: deployment.containers[0],
+        target: kplus.MetricTarget.averageUtilization(50),
+      }),
+    ],
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[1].spec;
+  expect(spec.maxReplicas).toEqual(10);
+  expect(spec.metrics).toEqual([{
+    type: 'ContainerResource',
+    containerResource: {
+      container: 'main',
+      name: 'ephemeral-storage',
+      target: {
+        averageUtilization: 50,
+        type: 'Utilization',
+      },
+    },
+  }]);
+});
+
+test('creates HPA with external metric, when provided a Metric.external()', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+    metrics: [
+      kplus.Metric.external({
+        labelSelector: kplus.LabelSelector.of({ labels: { app: 'scraper' } }),
+        name: 'sqs-queue',
+        target: kplus.MetricTarget.averageUtilization(50),
+      }),
+    ],
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[1].spec;
+  expect(spec.maxReplicas).toEqual(10);
+  expect(spec.metrics).toEqual([{
+    type: 'External',
+    external: {
+      metric: {
+        name: 'sqs-queue',
+        selector: {
+          matchLabels: {
+            app: 'scraper',
+          },
+        },
+      },
+      target: {
+        averageUtilization: 50,
+        type: 'Utilization',
+      },
+    },
+  }]);
+});
+
+test('creates HPA with object metric, when provided a Metric.object()', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+  const service = new kplus.Service(chart, 'my-service', { ports: [{ port: 80 }] } );
+  const ingress = new kplus.Ingress(chart, 'my-ingress', {
+    defaultBackend: kplus.IngressBackend.fromService(service),
+  });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+    metrics: [
+      kplus.Metric.object({
+        object: ingress,
+        name: 'requests-per-second',
+        target: kplus.MetricTarget.averageUtilization(50),
+      }),
+    ],
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[3].spec;
+  expect(spec.maxReplicas).toEqual(10);
+  expect(spec.metrics).toEqual([{
+    type: 'Object',
+    object: {
+      describedObject: {
+        apiVersion: 'networking.k8s.io/v1',
+        kind: 'Ingress',
+        name: 'test-my-ingress-c8135042',
+      },
+      metric: {
+        name: 'requests-per-second',
+      },
+      target: {
+        averageUtilization: 50,
+        type: 'Utilization',
+      },
+    },
+  }]);
+});
+
+test('creates HPA with pods metric, when provided a Metric.pods()', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+    metrics: [
+      kplus.Metric.pods({
+        name: 'requests-per-second',
+        target: kplus.MetricTarget.averageUtilization(50),
+        labelSelector: kplus.LabelSelector.of({ labels: { app: 'scraper' } }),
+      }),
+    ],
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[1].spec;
+  expect(spec.maxReplicas).toEqual(10);
+  expect(spec.metrics).toEqual([{
+    type: 'Pods',
+    pods: {
+      metric: {
+        name: 'requests-per-second',
+        selector: {
+          matchLabels: {
+            app: 'scraper',
+          },
+        },
+      },
+      target: {
+        averageUtilization: 50,
+        type: 'Utilization',
+      },
+    },
+  }]);
+});
+
+test('creates HPA with Resource CPU metric, when provided a Metric.resourceCpu()', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+    metrics: [
+      kplus.Metric.resourceCpu( kplus.MetricTarget.averageUtilization(50)),
+    ],
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[1].spec;
+  expect(spec.maxReplicas).toEqual(10);
+  expect(spec.metrics).toEqual([{
+    type: 'Resource',
+    resource: {
+      name: 'cpu',
+      target: {
+        averageUtilization: 50,
+        type: 'Utilization',
+      },
+    },
+  }]);
+});
+
+test('creates HPA with Resource Memory metric, when provided a Metric.resourceMemory()', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+    metrics: [
+      kplus.Metric.resourceMemory( kplus.MetricTarget.averageUtilization(50)),
+    ],
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[1].spec;
+  expect(spec.maxReplicas).toEqual(10);
+  expect(spec.metrics).toEqual([{
+    type: 'Resource',
+    resource: {
+      name: 'memory',
+      target: {
+        averageUtilization: 50,
+        type: 'Utilization',
+      },
+    },
+  }]);
+});
+
+test('creates HPA with Resource Storage metric, when provided a Metric.resourceStorage()', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+    metrics: [
+      kplus.Metric.resourceStorage( kplus.MetricTarget.averageUtilization(50)),
+    ],
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[1].spec;
+  expect(spec.maxReplicas).toEqual(10);
+  expect(spec.metrics).toEqual([{
+    type: 'Resource',
+    resource: {
+      name: 'storage',
+      target: {
+        averageUtilization: 50,
+        type: 'Utilization',
+      },
+    },
+  }]);
+});
+
+test('creates HPA with Resource Ephemeral Storage metric, when provided a Metric.resourceEphemeralStorage()', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+    metrics: [
+      kplus.Metric.resourceEphemeralStorage( kplus.MetricTarget.averageUtilization(50)),
+    ],
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[1].spec;
+  expect(spec.maxReplicas).toEqual(10);
+  expect(spec.metrics).toEqual([{
+    type: 'Resource',
+    resource: {
+      name: 'ephemeral-storage',
+      target: {
+        averageUtilization: 50,
+        type: 'Utilization',
+      },
+    },
+  }]);
+});
+
+test('creates HPA with Resource CPU metric targeting 70% average utilization, when provided a MetricTarget.averageUtilization()', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+    metrics: [
+      kplus.Metric.resourceCpu(kplus.MetricTarget.averageUtilization(70)),
+    ],
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[1].spec;
+  expect(spec.maxReplicas).toEqual(10);
+  expect(spec.metrics).toEqual([{
+    type: 'Resource',
+    resource: {
+      name: 'cpu',
+      target: {
+        averageUtilization: 70,
+        type: 'Utilization',
+      },
+    },
+  }]);
+});
+
+test('creates HPA with Resource CPU metric targeting 47.2 average value, when provided a MetricTarget.averageUtilization()', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+    metrics: [
+      kplus.Metric.resourceCpu(kplus.MetricTarget.averageValue(47.2)),
+    ],
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[1].spec;
+  expect(spec.maxReplicas).toEqual(10);
+  expect(spec.metrics).toEqual([{
+    type: 'Resource',
+    resource: {
+      name: 'cpu',
+      target: {
+        averageValue: 47.2,
+        type: 'AverageValue',
+      },
+    },
+  }]);
+});
+
+test('creates HPA with Resource CPU metric targeting the exact value of 29.5, when provided a MetricTarget.value()', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+    metrics: [
+      kplus.Metric.resourceCpu(kplus.MetricTarget.value(29.5)),
+    ],
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[1].spec;
+  expect(spec.maxReplicas).toEqual(10);
+  expect(spec.metrics).toEqual([{
+    type: 'Resource',
+    resource: {
+      name: 'cpu',
+      target: {
+        value: 29.5,
+        type: 'Value',
+      },
+    },
+  }]);
+});
+
+
+// Different config combinations
+test('creates HPA, when target is a deployment', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[1].spec;
+  expect(spec.scaleTargetRef.kind).toEqual('Deployment');
+});
+
+test('creates HPA, when target is a StatefulSet', () => {
+  const chart = Testing.chart();
+  const service = new kplus.Service(chart, 'TestService', { ports: [{ port: 80 }] });
+
+  const statefulset = new kplus.StatefulSet(chart, 'StatefulSet', {
+    select: false,
+    containers: [{ image: 'foobar' }],
+    service: service,
+  });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: statefulset,
+    maxReplicas: 10,
+  });
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[2].spec;
+  expect(spec.scaleTargetRef.kind).toEqual('StatefulSet');
+});
+
+test('creates HPA, when minReplicas is same as maxReplicas', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+    minReplicas: 10,
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[1].spec;
+  expect(spec.maxReplicas).toEqual(10);
+  expect(spec.minReplicas).toEqual(10);
+});
+
+test('creates HPA with expected spec, when metrics, scaleUp, and scaleDown are all configured', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+    minReplicas: 2,
+    metrics: [
+      kplus.Metric.resourceCpu(kplus.MetricTarget.averageUtilization(50)),
+    ],
+    scaleUp: {
+      stabilizationWindow: Duration.minutes(5),
+      strategy: kplus.ScalingStrategy.MAX_CHANGE,
+      policies: [
+        {
+          replicas: kplus.Replicas.absolute(3),
+          duration: Duration.minutes(3),
+        },
+        {
+          replicas: kplus.Replicas.percent(30),
+        },
+      ],
+    },
+    scaleDown: {
+      stabilizationWindow: Duration.minutes(5),
+      strategy: kplus.ScalingStrategy.MAX_CHANGE,
+      policies: [
+        {
+          replicas: kplus.Replicas.absolute(3),
+          duration: Duration.minutes(3),
+        },
+        {
+          replicas: kplus.Replicas.percent(30),
+        },
+      ],
+    },
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[1].spec;
+  expect(spec.maxReplicas).toEqual(10);
+  expect(spec.minReplicas).toEqual(2);
+  expect(spec.metrics).toEqual([{
+    resource: {
+      name: 'cpu',
+      target: {
+        averageUtilization: 50,
+        type: 'Utilization',
+      },
+    },
+    type: 'Resource',
+  }]);
+  expect(spec.behavior.scaleUp).toEqual({
+    policies: [
+      { periodSeconds: 180, type: 'Pods', value: 3 },
+      { periodSeconds: 15, type: 'Percent', value: 30 },
+    ],
+    selectPolicy: 'Max',
+    stabilizationWindowSeconds: 300,
+  });
+  expect(spec.behavior.scaleDown).toEqual({
+    policies: [
+      { periodSeconds: 180, type: 'Pods', value: 3 },
+      { periodSeconds: 15, type: 'Percent', value: 30 },
+    ],
+    selectPolicy: 'Max',
+    stabilizationWindowSeconds: 300,
+  });
+});
+
+test('creates HPA, when metrics are not provided and one of the two target containers does not have any resources limits/requests', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', {
+    containers: [{
+      image: 'pod1',
+      resources: {
+        cpu: undefined,
+        memory: undefined,
+        ephemeralStorage: undefined,
+      },
+    },
+    {
+      image: 'pod2',
+      resources: {
+        cpu: {
+          request: kplus.Cpu.millis(256),
+        },
+        memory: undefined,
+        ephemeralStorage: undefined,
+      },
+    }],
+  });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+  });
+
+  const manifest = Testing.synth(chart);
+  expect(manifest).toMatchSnapshot();
+  const spec = manifest[1].spec;
+  expect(spec.maxReplicas).toEqual(10);
+});
+
+// Errors
+
+test('throws error at synth, when metrics are not provided and target container does not have resource constraints specified', () => {
+  const chart = Testing.chart();
+
+  const deployment = new kplus.Deployment(chart, 'Deployment', {
+    containers: [{
+      image: 'pod',
+      resources: {
+        cpu: undefined,
+        memory: undefined,
+        ephemeralStorage: undefined,
+      },
+    }],
+  });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+  });
+  const regex = new RegExp(/Validation failed with the following errors:[\s]*\[test\/Hpa\] If HorizontalPodAutoscaler does not have metrics defined, then every container in the target must have a CPU or memory resource constraint defined\./);
+  expect(() => Testing.synth(chart)).toThrowError(regex);
+});
+
+test('throws error, when minReplicas is more than maxReplicas', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  expect(() =>
+    new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+      target: deployment,
+      maxReplicas: 10,
+      minReplicas: 11,
+    }),
+  ).toThrowError("'minReplicas' (11) must be less than or equal to 'maxReplicas' (10) in order for HorizontalPodAutoscaler to scale.");
+});
+
+test('throws error, when scaleUp.stabilizationWindow is more than 1 hour', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  expect(() =>
+    new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+      target: deployment,
+      maxReplicas: 10,
+      scaleUp: {
+        stabilizationWindow: Duration.hours(2),
+      },
+    }),
+  ).toThrowError("'scaleUp.stabilizationWindow' (1 hour 60 minutes) must be 0 seconds or more with a max of 1 hour.");
+});
+
+
+test('throws error, when scaleDown.stabilizationWindow is more than 1 hour', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  expect(() =>
+    new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+      target: deployment,
+      maxReplicas: 10,
+      scaleDown: {
+        stabilizationWindow: Duration.hours(2),
+      },
+    }),
+  ).toThrowError("'scaleDown.stabilizationWindow' (1 hour 60 minutes) must be 0 seconds or more with a max of 1 hour.");
+});
+
+test('throws error, when scaleUp.stabilizationWindow is -1', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  expect(() =>
+    new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+      target: deployment,
+      maxReplicas: 10,
+      scaleUp: {
+        stabilizationWindow: Duration.seconds(-1),
+      },
+    }),
+  ).toThrowError('Duration amounts cannot be negative. Received: -1');
+});
+
+test('throws error, when scaleDown.stabilizationWindow is -1 seconds', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  expect(() =>
+    new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+      target: deployment,
+      maxReplicas: 10,
+      scaleDown: {
+        stabilizationWindow: Duration.seconds(-1),
+      },
+    }),
+  ).toThrowError('Duration amounts cannot be negative. Received: -1');
+});
+
+test('throws error, when scaleUp policy has a duration longer than 30 minutes', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  expect(() =>
+    new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+      target: deployment,
+      maxReplicas: 10,
+      scaleUp: {
+        policies: [
+          {
+            replicas: kplus.Replicas.absolute(3),
+            duration: Duration.minutes(31),
+          },
+        ],
+      },
+    }),
+  ).toThrowError("'scaleUp.policies' duration (31 minutes) is outside of the allowed range. Must be at least 1 second long and no longer than 30 minutes.");
+});
+
+test('throws error, when scaleDown policy has a duration longer than 30 minutes', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  expect(() =>
+    new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+      target: deployment,
+      maxReplicas: 10,
+      scaleDown: {
+        policies: [
+          {
+            replicas: kplus.Replicas.absolute(3),
+            duration: Duration.minutes(31),
+          },
+        ],
+      },
+    }),
+  ).toThrowError("'scaleDown.policies' duration (31 minutes) is outside of the allowed range. Must be at least 1 second long and no longer than 30 minutes.");
+});
+
+test('throws error, when scaleUp policy has a duration set to 0', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  expect(() =>
+    new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+      target: deployment,
+      maxReplicas: 10,
+      scaleUp: {
+        policies: [
+          {
+            replicas: kplus.Replicas.absolute(3),
+            duration: Duration.minutes(0),
+          },
+        ],
+      },
+    }),
+  ).toThrowError("'scaleUp.policies' duration (0 minutes) is outside of the allowed range. Must be at least 1 second long and no longer than 30 minutes.");
+});
+
+test('throws error, when scaleDown policy has a duration set to 0', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  expect(() =>
+    new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+      target: deployment,
+      maxReplicas: 10,
+      scaleDown: {
+        policies: [
+          {
+            replicas: kplus.Replicas.absolute(3),
+            duration: Duration.minutes(0),
+          },
+        ],
+      },
+    }),
+  ).toThrowError("'scaleDown.policies' duration (0 minutes) is outside of the allowed range. Must be at least 1 second long and no longer than 30 minutes.");
+});
+
+test('throws error, when scaleUp policy has a duration set to -10 minutes', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  expect(() =>
+    new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+      target: deployment,
+      maxReplicas: 10,
+      scaleUp: {
+        policies: [
+          {
+            replicas: kplus.Replicas.absolute(3),
+            duration: Duration.minutes(-10),
+          },
+        ],
+      },
+    }),
+  ).toThrowError('Duration amounts cannot be negative. Received: -10');
+});
+
+test('throws error, when scaleDown policy has a duration set to -10 minutes', () => {
+  const chart = Testing.chart();
+  const deployment = new kplus.Deployment(chart, 'Deployment', { containers: [{ image: 'pod' }] });
+
+  expect(() =>
+    new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+      target: deployment,
+      maxReplicas: 10,
+      scaleDown: {
+        policies: [
+          {
+            replicas: kplus.Replicas.absolute(3),
+            duration: Duration.minutes(-10),
+          },
+        ],
+      },
+    }),
+  ).toThrowError('Duration amounts cannot be negative. Received: -10');
+});
+
+test('throws error at synth, when Deployment target has replicas defined', () => {
+  const chart = Testing.chart();
+
+  const deployment = new kplus.Deployment(chart, 'Deployment', {
+    containers: [{ image: 'pod' }],
+    replicas: 3,
+  });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: deployment,
+    maxReplicas: 10,
+  });
+  const regex = new RegExp(/Validation failed with the following errors:[\s]*\[test\/Hpa\] HorizontalPodAutoscaler target cannot have a fixed number of replicas \(3\)\./);
+  expect(() => Testing.synth(chart) ).toThrowError(regex);
+});
+
+test('throws error at synth, when StatefulSet target has replicas defined', () => {
+  const chart = Testing.chart();
+  const service = new kplus.Service(chart, 'TestService', { ports: [{ port: 80 }] });
+
+  const statefulset = new kplus.StatefulSet(chart, 'StatefulSet', {
+    select: false,
+    containers: [{ image: 'foobar' }],
+    service: service,
+    replicas: 5,
+  });
+
+  new kplus.HorizontalPodAutoscaler(chart, 'Hpa', {
+    target: statefulset,
+    maxReplicas: 10,
+  });
+  const regex = new RegExp(/Validation failed with the following errors:[\s]*\[test\/Hpa\] HorizontalPodAutoscaler target cannot have a fixed number of replicas \(5\)\./);
+  expect(() =>Testing.synth(chart)).toThrowError(regex);
+});