> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Containers Should Set CPU And Memory Requests

### More Info:

Verifies every container sets resources.requests so the scheduler can place the pod correctly and QoS is not BestEffort.

### Risk Level

Low

### Address

Security

### Compliance Standards

* Cloudanix Best Practice

### Triage and Remediation

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. Identify noncompliant pods (any machine with kubectl access):
           ```bash theme={null}
           kubectl get pods --all-namespaces -o json | jq -r '
             [ .items[]
             | select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
             | .metadata as $m
             | (.spec.nodeName // "") as $node
             | (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
             | ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
             | (.spec.containers // [])[]
             | ((.resources.requests.cpu != null) and (.resources.requests.memory != null)) as $ok
             | select($ok | not)
             | "kind=Pod ns=\($m.namespace) name=\($m.name) container=\(.name) image=\(.image)"
           ][]'
           ```

        2. For pods managed by higher-level controllers (Deployment, StatefulSet, DaemonSet, Job, CronJob), edit the controller manifest to add requests (any machine with kubectl access). Example for a Deployment:
           ```bash theme={null}
           kubectl -n NAMESPACE edit deployment DEPLOYMENT_NAME
           ```
           In each affected container under `spec.template.spec.containers[]`, ensure a block like:
           ```yaml theme={null}
           resources:
             requests:
               cpu: "100m"
               memory: "128Mi"
           ```
           Save and exit to let Kubernetes roll out updated pods.

        3. For standalone Pods (no controller in `ownerReferences`), edit the Pod spec directly (any machine with kubectl access). Note this recreates the pod:
           ```bash theme={null}
           kubectl -n NAMESPACE get pod POD_NAME -o yaml > /tmp/pod-POD_NAME.yaml
           ```
           In `/tmp/pod-POD_NAME.yaml`, under each `spec.containers[]`, add:
           ```yaml theme={null}
           resources:
             requests:
               cpu: "100m"
               memory: "128Mi"
           ```
           Remove ephemeral runtime fields (`status`, `metadata.resourceVersion`, `metadata.uid`, `metadata.creationTimestamp`, `metadata.managedFields`) and apply:
           ```bash theme={null}
           kubectl -n NAMESPACE delete pod POD_NAME
           kubectl -n NAMESPACE apply -f /tmp/pod-POD_NAME.yaml
           ```

        4. If manifests are managed via GitOps or other IaC, make the same `resources.requests.cpu` and `resources.requests.memory` changes in the source YAML for each container, then let your deployment pipeline apply them. Do not rely on `kubectl edit` for these resources.

        5. Repeat steps 2–4 for all listed noncompliant pods until every container in each pod has both CPU and memory requests defined.

        6. Verify compliance (any machine with kubectl access):
           ```bash theme={null}
           kubectl get pods --all-namespaces -o json | jq -r '
             [ .items[]
             | select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
             | .metadata as $m
             | (.spec.nodeName // "") as $node
             | (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
             | ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
             | (.spec.containers // [])[]
             | ((.resources.requests.cpu != null) and (.resources.requests.memory != null)) as $ok
             | "kind=Pod ns=\($m.namespace) name=\($m.name) uid=\($m.uid) apiVersion=v1"
               + (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
               + (if $node   == ""   then "" else " node=\($node)" end)
               + (if $labels == ""   then "" else " labels=\($labels)" end)
               + (if $own    == null then "" else " owner=\($own.kind)/\($m.namespace)/\($own.name)/\($own.uid)" end)
               + " container=\(.name) image=\(.image)"
               + " requestsCpu=\(.resources.requests.cpu // "unset") requestsMemory=\(.resources.requests.memory // "unset")"
               + " is_compliant=\(if $ok then "true" else "false" end)"
             ] as $rows
             | if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
           ```
           Confirm the output is `is_compliant=true` or that all listed containers show `is_compliant=true`.
      </Accordion>

      <Accordion title="Using kubectl">
        On any machine with kubectl access:

        1. Identify non-compliant pods and their controllers

        ```bash theme={null}
        kubectl get pods --all-namespaces -o json | jq -r '
          [ .items[]
          | select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
          | .metadata as $m
          | ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
          | (.spec.containers // [])[]
          | ((.resources.requests.cpu != null) and (.resources.requests.memory != null)) as $ok
          | select($ok|not)
          | "ns=\($m.namespace) pod=\($m.name) ownerKind=\($own.kind // "Pod") ownerName=\($own.name // $m.name)"
          ] | unique[]'
        ```

        2. For each workload controller (Deployment/DaemonSet/StatefulSet/Job/CronJob), edit the manifest and add `resources.requests` for every container.

        Example for a Deployment:

        ```bash theme={null}
        kubectl -n NAMESPACE get deploy DEPLOYMENT_NAME -o yaml > /tmp/deploy-with-requests.yaml
        ```

        Edit `/tmp/deploy-with-requests.yaml` and, for each container under `spec.template.spec.containers`, add:

        ```yaml theme={null}
        resources:
          requests:
            cpu: "100m"
            memory: "128Mi"
        ```

        (Adjust values as appropriate for the application.)

        Apply the updated manifest:

        ```bash theme={null}
        kubectl apply -f /tmp/deploy-with-requests.yaml
        ```

        3. For standalone Pods (no controller), recreate them with requests set.

        Export, edit, and reapply:

        ```bash theme={null}
        kubectl -n NAMESPACE get pod POD_NAME -o yaml > /tmp/pod-with-requests.yaml
        ```

        In `/tmp/pod-with-requests.yaml`:

        * Remove the entire `metadata.uid`, `metadata.resourceVersion`, `metadata.creationTimestamp`, `metadata.managedFields`, `status` sections.
        * Under each `spec.containers[]`, add:

        ```yaml theme={null}
        resources:
          requests:
            cpu: "100m"
            memory: "128Mi"
        ```

        Then:

        ```bash theme={null}
        kubectl -n NAMESPACE delete pod POD_NAME
        kubectl apply -f /tmp/pod-with-requests.yaml
        ```

        4. Verification

        On any machine with kubectl access:

        ```bash theme={null}
        kubectl get pods --all-namespaces -o json | jq -r '
          [ .items[]
          | select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
          | .metadata as $m
          | (.spec.containers // [])[]
          | ((.resources.requests.cpu != null) and (.resources.requests.memory != null)) as $ok
          | select($ok|not)
          ] | if length==0 then "is_compliant=true" else . end'
        ```
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Fix CBP C1.9: Ensure all containers set CPU and memory requests.
        #
        # This script:
        #   - Finds non-exempt pods whose containers lack CPU and/or memory requests.
        #   - Patches the owning workload (Deployment/StatefulSet/DaemonSet/Job/CronJob/Pod)
        #     to set default requests where missing.
        #   - Is idempotent: it only adds missing requests, and leaves existing ones intact.
        #   - Verifies compliance using the provided audit command.
        #
        # NOTE:
        #   - Requires: bash, kubectl, jq, yq (v4).
        #   - Run on: any machine with kubectl access to the cluster and appropriate RBAC.
        #
        # Default request values (change if needed):
        DEFAULT_CPU_REQUEST="100m"
        DEFAULT_MEMORY_REQUEST="128Mi"

        set -euo pipefail

        # Ensure required tools are available
        for bin in kubectl jq yq; do
          if ! command -v "$bin" >/dev/null 2>&1; then
            echo "ERROR: Required binary '$bin' not found in PATH" >&2
            exit 1
          fi
        done

        echo "Discovering noncompliant containers..."

        # Get list of non-exempt pods/containers that are non-compliant
        NONCOMPLIANT_JSON=$(kubectl get pods --all-namespaces -o json | jq -c '
          [
            .items[]
            | select(.metadata.namespace as $n
                     | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
            | . as $pod
            | (.metadata.ownerReferences // []) as $owners
            | (.spec.containers // [])[]
            | . as $c
            | ((.resources.requests.cpu != null) and (.resources.requests.memory != null)) as $ok
            | select($ok | not)
            | {
                podNamespace: $pod.metadata.namespace,
                podName:      $pod.metadata.name,
                container:    $c.name,
                owner: (
                  [ $owners[]? | select(.controller) ] | first
                )
              }
          ]' )

        if [[ -z "$NONCOMPLIANT_JSON" || "$NONCOMPLIANT_JSON" == "[]" ]]; then
          echo "No noncompliant containers found; nothing to fix."
        else
          echo "$NONCOMPLIANT_JSON" | jq -r '
            .[] |
            "Noncompliant: ns=\(.podNamespace) pod=\(.podName) container=\(.container) ownerKind=\(.owner.kind // "Pod") ownerName=\(.owner.name // .podName)"
          '
        fi

        # Group by owning workload (or Pod if no owner)
        WORKLOADS_JSON=$(jq -c '
          map({
            ns: .podNamespace,
            kind: (.owner.kind // "Pod"),
            name: (.owner.name // .podName)
          })
          | unique
        ' <<<"$NONCOMPLIANT_JSON")

        if [[ "$WORKLOADS_JSON" == "[]" ]]; then
          echo "No owning workloads need patching."
        else
          echo
          echo "Will patch the following owning workloads to set default requests where missing:"
          echo "$WORKLOADS_JSON" | jq -r '.[] | "\(.ns) \(.kind) \(.name)"'
        fi

        patch_owner() {
          local ns="$1" kind="$2" name="$3"

          echo
          echo "Processing owner: kind=$kind ns=$ns name=$name"

          # Map owner kind to actual Kubernetes resource kind
          local resKind pathPrefix

          case "$kind" in
            Deployment|StatefulSet|DaemonSet|Job)
              resKind="$kind"
              pathPrefix=".spec.template.spec"
              ;;
            ReplicaSet)
              # Usually managed by a Deployment; skip to avoid racing the controller.
              echo "  Skipping ReplicaSet $ns/$name (managed by a higher-level controller)."
              return 0
              ;;
            CronJob)
              resKind="CronJob"
              pathPrefix=".spec.jobTemplate.spec.template.spec"
              ;;
            Pod)
              resKind="Pod"
              pathPrefix=".spec"
              ;;
            *)
              echo "  WARNING: Unsupported owner kind '$kind' for $ns/$name; skipping."
              return 0
              ;;
          esac

          # Fetch current YAML
          tmpfile=$(mktemp)
          if ! kubectl get "$resKind" "$name" -n "$ns" -o yaml >"$tmpfile" 2>/dev/null; then
            echo "  WARNING: Unable to fetch $resKind $ns/$name; it may have been deleted. Skipping."
            rm -f "$tmpfile"
            return 0
          fi

          # Build yq expression to set missing requests for containers and initContainers
          # This preserves existing values and only fills in unset fields.
          yq_expr='
            # containers
            '"$pathPrefix"'.containers // [] |
            select(length > 0) |
            path(.. | select(tag == "!!seq")) as $p |
            .'$pathPrefix'.containers |= (
              . // [] | map(
                .resources.requests |= (
                  . // {} |
                  (.cpu // "'"$DEFAULT_CPU_REQUEST"'") as $cpu |
                  (.memory // "'"$DEFAULT_MEMORY_REQUEST"'") as $mem |
                  . * {"cpu": $cpu, "memory": $mem}
                )
              )
            ) |

            # initContainers (if present)
            '"$pathPrefix"'.initContainers // [] |
            select(length > 0) |
            path(.. | select(tag == "!!seq")) as $p2 |
            .'$pathPrefix'.initContainers |= (
              . // [] | map(
                .resources.requests |= (
                  . // {} |
                  (.cpu // "'"$DEFAULT_CPU_REQUEST"'") as $cpu2 |
                  (.memory // "'"$DEFAULT_MEMORY_REQUEST"'") as $mem2 |
                  . * {"cpu": $cpu2, "memory": $mem2}
                )
              )
            )
          '

          patched=$(yq eval "$yq_expr" "$tmpfile") || {
            echo "  ERROR: Failed to patch YAML for $resKind $ns/$name with yq."
            rm -f "$tmpfile"
            return 1
          }

          # Apply the patch if there is any change
          if diff -q <(cat "$tmpfile") <(printf "%s\n" "$patched") >/dev/null 2>&1; then
            echo "  No changes needed for $resKind $ns/$name (already compliant or owner template unaffected)."
          else
            echo "  Applying patched manifest to $resKind $ns/$name ..."
            printf "%s\n" "$patched" | kubectl apply -f - >/dev/null
            echo "  Patch applied."
          fi

          rm -f "$tmpfile"
        }

        # Patch each owning workload
        echo "$WORKLOADS_JSON" | jq -c '.[]' | while read -r w; do
          ns=$(jq -r '.ns'   <<<"$w")
          kind=$(jq -r '.kind' <<<"$w")
          name=$(jq -r '.name' <<<"$w")
          patch_owner "$ns" "$kind" "$name"
        done

        echo
        echo "Waiting for updated pods to roll out (this may take a few minutes)..."
        # Simple wait: give controllers some time; can be tuned.
        sleep 30

        echo
        echo "Verification: running benchmark audit command..."
        kubectl get pods --all-namespaces -o json | jq -r '
          [ .items[]
          | select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
          | .metadata as $m
          | (.spec.nodeName // "") as $node
          | (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
          | ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
          | (.spec.containers // [])[]
          | ((.resources.requests.cpu != null) and (.resources.requests.memory != null)) as $ok
          | "kind=Pod ns=\($m.namespace) name=\($m.name) uid=\($m.uid) apiVersion=v1"
            + (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
            + (if $node   == ""   then "" else " node=\($node)" end)
            + (if $labels == ""   then "" else " labels=\($labels)" end)
            + (if $own    == null then "" else " owner=\($own.kind)/\($m.namespace)/\($own.name)/\($own.uid)" end)
            + " container=\(.name) image=\(.image)"
            + " requestsCpu=\(.resources.requests.cpu // "unset") requestsMemory=\(.resources.requests.memory // "unset")"
            + " is_compliant=\(if $ok then "true" else "false" end)"
          ] as $rows
          | if ($rows | length) == 0 then "is_compliant=true" else $rows[] end
        '
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
