Skip to content

Fix Karpenter IAM policy size limit (CASCL-1645) - #3315

Open
clamoriniere wants to merge 2 commits into
mainfrom
dd/clamoriniere/cascl-1645-karpenter-policy-size-fix-20260730
Open

Fix Karpenter IAM policy size limit (CASCL-1645)#3315
clamoriniere wants to merge 2 commits into
mainfrom
dd/clamoriniere/cascl-1645-karpenter-policy-size-fix-20260730

Conversation

@clamoriniere

@clamoriniere clamoriniere commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

Splits the Karpenter controller's IAM managed policy (KarpenterControllerPolicy in cmd/kubectl-datadog/autoscaling/cluster/apply/assets/karpenter.yaml) into two managed policies — KarpenterControllerPolicy and a new KarpenterControllerPolicy2 — and attaches both to the controller role in dd-karpenter.yaml and dd-karpenter-fargate.yaml.

Motivation

Customers with long EKS cluster names hit ServiceLimitExceeded: Cannot exceed quota for PolicySize: 6144 when the kubectl datadog autoscaling cluster install/update CloudFormation stack tries to create KarpenterControllerPolicy (CASCL-1645). The ${ClusterName} parameter (plus ARNs that embed it) is interpolated ~17 times across the single policy document, so the rendered, whitespace-stripped policy already sits at ~90% of IAM's 6,144-character managed-policy limit before substitution — pushing cluster names as short as ~27-35 characters over the hard, non-raisable IAM quota.

Splitting the policy's 17 statements across two managed policies keeps each one well under the limit. I verified this against AWS EKS's actual maximum cluster-name length (100 characters, per the CreateCluster API's Name field docs) and, as an extra margin, against the Kubernetes generic resource-name maximum (253 characters):

Cluster name length Worst-case Policy A Worst-case Policy B vs 6,144 limit
27 (today's failure point) ~3,001-3,283 OK
46 ~3,126-3,416 OK
100 (real EKS max) ~3,666-3,816 ~3,614-3,794 OK, ~2,300+ chars of margin
253 (K8s name-length fallback) ~5,196-5,346 ~4,685-4,865 Still OK

I also checked IAM's separate 128-character managed-policy name limit (distinct from the document-size limit above): both KarpenterControllerPolicy-${ClusterName} and KarpenterControllerPolicy2-${ClusterName} stay under it at the true 100-char EKS maximum, though with only 1-2 characters of headroom. TestKarpenterControllerPolicyNameLength guards this so a future rename to something longer doesn't silently reintroduce a similar failure via the policy name instead of its document.

A follow-up ticket (CASCL-1646) tracks a separate, tighter constraint discovered while verifying against the true 100-char EKS max: KarpenterNodeRole-${ClusterName} is an IAM role name (64-char limit), which now becomes the new effective ceiling (~46 chars) after this fix. That ticket calls out explicitly that its fix must remain backward compatible with update/uninstall on stacks created before it, since it would need to change resource naming, not just policy content.

Changes

  • cmd/kubectl-datadog/autoscaling/cluster/apply/assets/karpenter.yaml: split the 17-statement KarpenterControllerPolicy PolicyDocument into KarpenterControllerPolicy (8 statements, unchanged name) and a new KarpenterControllerPolicy2 (9 statements), balanced by how many ${ClusterName}-driven tokens each statement carries (not just statement count) so both halves have comparable headroom.
  • cmd/kubectl-datadog/autoscaling/cluster/apply/assets/dd-karpenter.yaml and dd-karpenter-fargate.yaml: add a second ManagedPolicyArns entry for KarpenterControllerPolicy2-${ClusterName} alongside the existing one.
  • cmd/kubectl-datadog/autoscaling/cluster/apply/run.go: updated a comment mentioning the (now two) managed policies.
  • test/e2e/tests/autoscaling_suite/eks_test.go: updated the comment explaining why the E2E stack name is kept short, now referencing the two tickets and the remaining KarpenterNodeRole naming constraint.
  • cmd/kubectl-datadog/autoscaling/cluster/apply/karpenter_cfn_test.go (new): regression tests that:
    • TestKarpenterControllerPolicySize: render both embedded PolicyDocuments with the longest realistic cluster name (100 chars, the real EKS max) and AWS partition/region strings, and assert each stays under IAM's 6,144-char document-size limit.
    • TestKarpenterControllerPolicyNameLength: assert both managed policy names (KarpenterControllerPolicy-${ClusterName} and KarpenterControllerPolicy2-${ClusterName}) stay under IAM's separate 128-char name-length limit at the same 100-char cluster-name maximum.
    • TestKarpenterControllerPolicyReferencedByBothStacks: assert both dd-karpenter.yaml/dd-karpenter-fargate.yaml attach both policy ARNs (guarding the additive, backward-compatible nature of the change).

Backward compatibility (update/uninstall of pre-existing stacks)

Verified that stacks created before this change remain fully manageable:

  • Update: KarpenterControllerPolicy's logical ID and ManagedPolicyName (KarpenterControllerPolicy-${ClusterName}) are unchanged, so CloudFormation performs an in-place PolicyDocument update (no replacement). KarpenterControllerPolicy2 is a genuinely new resource added to the same, already-existing CFN stack — CloudFormation creates it as part of the same stack update. On the mode-specific stacks, ManagedPolicyArns gains a second entry on the existing Role/KarpenterIRSARole resource, which CloudFormation updates in place (AttachRolePolicy) without replacing the role. apply.Run always fully updates the mode-independent karpenter.yaml stack (and waits for completion) before updating the mode-specific stack that references the new policy ARN, so the new policy always exists by the time it's referenced.
  • Uninstall: aws.DeleteStack (cmd/kubectl-datadog/autoscaling/cluster/common/aws/cloudformation.go) deletes a stack purely by name via the CloudFormation API — it has no dependency on which resources exist inside the stack, so it deletes all resources present (including the new KarpenterControllerPolicy2, if it exists) with zero code changes required. uninstall.go still deletes the mode-specific stack (which detaches the extra managed policy) before the mode-independent stack (which deletes it), preserving correct teardown ordering. An uninstall against a stack that never had update applied to it still works too, since deletion never reads the embedded YAML templates.
  • No Go code in run.go, update.go, or uninstall.go needed to change to support this — the change is confined to the embedded CloudFormation template assets, which is why no logical-ID renames were made.

Describe your test plan

  • go build ./cmd/kubectl-datadog/... — succeeds.
  • go test ./cmd/kubectl-datadog/autoscaling/... — all packages pass, including the three new tests in karpenter_cfn_test.go described above.
  • go vet ./cmd/kubectl-datadog/autoscaling/... — clean.
  • gofmt -l on all touched Go files — clean.
  • GOWORK=off go build ./tests/autoscaling_suite/... and go vet ./tests/autoscaling_suite/... in the test/e2e module — succeed after the comment update.
  • Verified all three CloudFormation templates still parse correctly (Python yaml.safe_load with a permissive !Sub/intrinsic-tag constructor), confirming both KarpenterControllerPolicy and KarpenterControllerPolicy2 show up as top-level resources.
  • Independently verified the size math with a Python simulation against the actual edited YAML (rendering the real JSON policy documents with worst-case region/partition/account substitutions across us-east-1, ap-southeast-1, us-gov-west-1/aws-us-gov, and cn-north-1/aws-cn) before writing the equivalent Go regression tests.
  • No manual kubectl datadog autoscaling cluster run against a real AWS account was performed (would require live AWS/EKS access not available in this environment); the Go-level tests plus the CFN update/delete semantics reasoning above are the available verification for the update/uninstall backward-compatibility claim.

Additional Notes

Filed CASCL-1646 to track the deeper KarpenterNodeRole/IRSA role/SQS queue naming-length constraints as separate follow-up work, since fixing those requires a naming-scheme change that is not automatically backward compatible the way this policy split is.

Minimum Agent Versions

Not applicable — this only changes the kubectl-datadog plugin's Karpenter CloudFormation assets, not the Datadog Agent or Cluster Agent.

Checklist

  • PR has at least one valid label: bug, enhancement, refactoring, documentation, tooling, and/or dependencies
  • PR has a milestone or the qa/skip-qa label
  • All commits are signed (see: signing commits)

NOTE: some AI-generated links and images were removed to ensure safety.


PR by Bits - View session in Datadog

Comment @DataDog to request changes

Co-authored-by: clamoriniere <cedric.lamoriniere@datadoghq.com>
@clamoriniere
clamoriniere requested review from a team as code owners July 30, 2026 20:46
@datadog-datadog-us1-prod

datadog-datadog-us1-prod Bot commented Jul 30, 2026

Copy link
Copy Markdown

View session in Datadog

Bits Code status: ✅ Done

CI Auto-fix: Waiting | Disable

Comment @DataDog to request changes

@datadog-official

Copy link
Copy Markdown

I can only run on private repositories.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9f77c2b76c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +177 to +180
KarpenterControllerPolicy2:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: !Sub "KarpenterControllerPolicy2-${ClusterName}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid shrinking the live policy before attaching the split policy

On updates of an existing installation, createCloudFormationStacks applies KarpenterCfn and waits for it before applying the mode-specific dd stack, so moving these statements into a policy that is only attached later means the live controller role keeps just KarpenterControllerPolicy after this stack update. If the later dd stack update is slow or fails, Karpenter loses the EC2/Describe/SQS/instance-profile permissions moved here and can no longer provision or process interruptions until a rerun succeeds; stage the new attachment before shrinking the original policy, or make the transition atomic.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DataDog fix this

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can only run on private repositories.

@datadog-datadog-us1-prod

datadog-datadog-us1-prod Bot commented Jul 30, 2026

Copy link
Copy Markdown

Pipelines  Code Coverage

🎯 Code Coverage (details)
Patch Coverage: 100.00%
Overall Coverage: 47.43% (+0.00%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 1e7e7e1 | Docs | Datadog PR Page | Give us feedback!

@clamoriniere clamoriniere added the bug Something isn't working label Jul 30, 2026
@clamoriniere clamoriniere added this to the v1.29.0 milestone Jul 30, 2026
@AlexanderYastrebov

Copy link
Copy Markdown
Contributor

This is related to aws/karpenter-provider-aws#7874 and aws/karpenter-provider-aws#8690
I am wondering if we can reuse stock Karpenter definitions to simplify future upgrades.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants