[CASCL-1304] kubectl-datadog: enrich dd-cluster-info ConfigMap#2980
Conversation
The dd-cluster-info ConfigMap (introduced by #2945) now records: - the running Karpenter installation (version, namespace, ownership) under a new `autoscaling` parent that also groups the existing clusterAutoscaler entry and a new eksAutoMode entry, - a `managedByDatadog` flag per node-management entity (Fargate profile, Karpenter NodePool), so a future migration tool can distinguish Datadog-managed entities to keep from legacy ones to drain. Detection helpers `FindKarpenterInstallation` and `IsEKSAutoModeEnabled` move from `install/guess/` to new `common/karpenter/` and `common/eksautomode/` packages so the clusterinfo classifier can reuse them. A generic `commonk8s.FindFirstDeployment` factors out the shared pager+predicate scan, and `commonk8s.ExtractDeploymentVersion` factors out the controller-image-tag → label fallback used by both detectors. Karpenter NodePool ownership uses the broader `autoscaling.datadoghq.com/created` label only (vs. uninstall's AND-pair with `app.kubernetes.io/managed-by: kubectl-datadog`) so NodePools managed by the Datadog cluster agent are also preserved by the migration tool. Datadog-managed NodePools with no nodes yet (typical right after install) are seeded into the snapshot with an empty Nodes list so the migration tool sees the destination NodePools exist. Fargate profile ownership reads tags via EKS DescribeFargateProfile; the `managed-by: kubectl-datadog` tag is propagated automatically from the CloudFormation stack tags, so no infrastructure change is needed. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #2980 +/- ##
==========================================
+ Coverage 40.77% 40.84% +0.07%
==========================================
Files 332 334 +2
Lines 28204 28298 +94
==========================================
+ Hits 11500 11559 +59
- Misses 15929 15960 +31
- Partials 775 779 +4
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report in Codecov by Sentry.
🚀 New features to boost your workflow:
|
This comment has been minimized.
This comment has been minimized.
kubectl-datadog: enrich dd-cluster-info ConfigMap
…ich-dd-cluster-info # Conflicts: # cmd/kubectl-datadog/autoscaling/cluster/apply/run.go # cmd/kubectl-datadog/autoscaling/cluster/apply/run_test.go # cmd/kubectl-datadog/autoscaling/cluster/common/eksautomode/eksautomode.go # cmd/kubectl-datadog/autoscaling/cluster/common/eksautomode/eksautomode_test.go # cmd/kubectl-datadog/autoscaling/cluster/common/karpenter/karpenter.go # cmd/kubectl-datadog/autoscaling/cluster/common/karpenter/karpenter_test.go # cmd/kubectl-datadog/autoscaling/cluster/guess/eksautomode.go # cmd/kubectl-datadog/autoscaling/cluster/guess/eksautomode_test.go # cmd/kubectl-datadog/autoscaling/cluster/guess/karpenter.go # cmd/kubectl-datadog/autoscaling/cluster/guess/karpenter_test.go # cmd/kubectl-datadog/autoscaling/cluster/install/guess/eksautomode.go # cmd/kubectl-datadog/autoscaling/cluster/install/guess/eksautomode_test.go # cmd/kubectl-datadog/autoscaling/cluster/install/guess/karpenter.go # cmd/kubectl-datadog/autoscaling/cluster/install/guess/karpenter_test.go
The merge of main into the feature branch (commit 87c6e46) brought in PR #2976's `update.go`, which referenced `guess.FindKarpenterInstallation` — the symbol our PR moved to `karpenter.FindInstallation`. The resolution updated all other call sites but missed this one, so the build broke on the merge commit. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
@codex review |
|
Codex Review: Didn't find any major issues. Swish! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
The pager-driven scan in commonk8s.FindFirstDeployment yields *appsv1.Deployment values out of EachListItem, but the predicate parameter was a value type — forcing a several-hundred-byte struct copy on every iteration. Switch the predicate signature (and ExtractDeploymentVersion) to take a pointer, eliminating the copy in the per-iteration callback. Container-level predicates (isControllerContainer, isClusterAutoscalerContainer) keep value semantics because slices.ContainsFunc requires func(E) bool for []E. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Collapse the three predicate-driven tests (NoMatch, ReturnsFirstMatch, ShortCircuits) into a single table that pins both the returned Deployment and the predicate-call count per row. The short-circuit invariant becomes a column rather than a dedicated test, and the row shape made it cheap to add three previously-uncovered cases (empty cluster, last deployment matches, multiple matches → first wins). PropagatesListError stays separate: its setup (PrependReactor) and assertions (ErrorIs) diverge from the predicate-counting flow. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The Datadog ownership tag (`managed-by: kubectl-datadog`) is written on CloudFormation stacks by `aws.buildTags` and propagated by CFN to the EKS Fargate profile resources. The classifier in clusterinfo reads those propagated tags to flag Fargate profiles as ManagedByDatadog. Both ends previously embedded the same string literals; a rename in buildTags would have silently broken classification. Hoist the pair to exported constants `aws.ManagedByTag` / `aws.ManagedByTagValue` and reference them from both writer and reader. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…its own package Mirror the karpenter package's shape — Installation type + FindInstallation function + private matchesDeployment / matchesContainer predicates — for the cluster-autoscaler detector, which had stayed inline in clusterinfo. Both detectors now answer "is the X controller running on this cluster?" with the same surface and the same package layout, eliminating the asymmetry that was masking the duplicated fingerprint pattern. The unit-level coverage (label variants, image-substring match, version extraction with image-tag → label fallback, scaled-to-zero treated as absent) moves to the new common/clusterautoscaler/clusterautoscaler_test.go table-driven tests. classify_test.go keeps a single integration smoke test verifying the snapshot wires the detection through. karpenter.go's predicates are renamed for symmetry: matchesController → matchesDeployment, isControllerContainer → matchesContainer. Both packages now expose the same internal vocabulary. clusterautoscaler.Installation has no IsOwn / InstalledBy / InstallerVersion fields, on purpose: kubectl-datadog never installs cluster-autoscaler, only detects it. The asymmetry with karpenter.Installation reflects an actual behavioural asymmetry. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The kubectl-datadog binary already imports `karpv1 "sigs.k8s.io/karpenter/pkg/apis/v1"` (via cluster/k8s/nodepool.go and cluster/common/clients/clients.go which registers `karpv1.NodePool` and `karpv1.NodePoolList` on the controller-runtime scheme). The justification for going through `unstructured.UnstructuredList` in `enrichKarpenterOwnership` was therefore wrong — replace it with a typed `karpv1.NodePoolList` and drop the unstructured import. The fake controller-runtime client in classify_test.go is updated to mirror the production scheme (typed NodePool/NodePoolList registration). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
`range` over a nil or empty map is a no-op in Go, so the early-return guard in enrichFargateOwnership was dead code. The function only reads the bucket (it never initialises it), so no side effect is at risk. The analogous guard in enrichKarpenterOwnership stays — it prevents creating an empty NodeManagerKarpenter bucket when no Datadog NodePools exist, which is a load-bearing semantic. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Struct-field alignment fix that go fmt picked up — the dd-gitlab check_formatting job runs `make fmt && git diff --exit-code`. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
Here is what the ConfigMap looks like on my test cluster: $ kubectl --namespace dd-karpenter get cm/dd-cluster-info -o yamlkind: ConfigMap
apiVersion: v1
metadata:
creationTimestamp: "2026-04-30T12:55:29Z"
labels:
app.kubernetes.io/managed-by: kubectl-datadog
name: dd-cluster-info
namespace: dd-karpenter
resourceVersion: "3020393"
uid: 999ba842-ecd6-497e-baf0-1c8f84d141b2
data:
cluster-info: |
apiVersion: v1
clusterName: lenaic-karpenter-test
generatedAt: 2026-05-07T15:44:30.94252014Z
nodeManagement:
asg:
eksctl-lenaic-karpenter-test-nodegroup-ng-bar-NodeGroup-T1wB42epyGs3:
nodes:
- ip-10-11-234-8.eu-west-3.compute.internal
eksManagedNodeGroup:
ng:
nodes:
- ip-10-11-232-97.eu-west-3.compute.internal
- ip-10-11-233-88.eu-west-3.compute.internal
ng-foo:
nodes:
- ip-10-11-234-21.eu-west-3.compute.internal
karpenter:
dd-karpenter-2lg5e:
nodes:
- ip-10-11-234-147.eu-west-3.compute.internal
- ip-10-11-234-202.eu-west-3.compute.internal
- ip-10-11-234-243.eu-west-3.compute.internal
- ip-10-11-234-83.eu-west-3.compute.internal
managedByDatadog: true
dd-karpenter-gbhyy:
nodes: []
managedByDatadog: true
autoscaling:
clusterAutoscaler:
present: false
karpenter:
present: true
namespace: dd-karpenter
name: karpenter
version: 1.12.0
managedByDatadog: true
installerVersion: v0.7.0
eksAutoMode:
enabled: false |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2bd2955137
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| NodeManagement map[NodeManager]map[string]NodeManagerEntry `yaml:"nodeManagement"` | ||
| Autoscaling Autoscaling `yaml:"autoscaling"` |
There was a problem hiding this comment.
Bump the cluster-info API version
This changes the persisted nodeManagement wire shape from entity → []string to entity → {nodes, managedByDatadog} and also moves autoscaler data under autoscaling, but the payload still advertises apiVersion: v1. Any migration reader that branches on apiVersion (as the constant comment says future readers should) cannot distinguish old and new ConfigMaps, so it can try to decode a new snapshot as the old v1 schema or vice versa. Please bump the schema version when emitting this incompatible shape.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Thanks for flagging. We're keeping apiVersion: v1 deliberately:
- The
dd-cluster-infoConfigMap was introduced very recently (PR [CASCL-1304] Snapshot node managers in ConfigMap during install #2945) and no version ofkubectl-datadoghas been released since. There is therefore no shipped binary in the wild writing the old shape. - No consumer of the ConfigMap exists yet either — it was added precisely to drive a follow-up migration tool that doesn't exist today.
grep -r "ConfigMapDataKey\|dd-cluster-info" --include='*.go'returns only the writer.
Since both the writer and the (future) reader will land in the same released kubectl-datadog version, there's no realistic scenario where two shapes can coexist in the field. We'll bump APIVersion the day a released consumer needs to disambiguate.
Per the Slack discussion with Cedric on PR #2980, surface the cluster identifier in the snapshot so a downstream tool can locate the cluster unambiguously (the ARN embeds the AWS account, region and short name). Classify gains a single EKS DescribeCluster call up front, parses the ARN with aws/arn.Parse to derive the region, and populates two new fields on ClusterInfo (`clusterArn` / `region`, both `omitempty`). The EKSDescriber interface grows to expose DescribeCluster alongside the existing DescribeFargateProfile so the test fake can substitute both. Best-effort: a DescribeCluster error or a malformed ARN logs a warning and leaves the fields empty rather than failing the snapshot — same contract as the other autoscaling detectors. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…ich-dd-cluster-info # Conflicts: # cmd/kubectl-datadog/autoscaling/cluster/apply/run_test.go
Continue the redistribution of `guess/` started by PRs #2980 and #3000: move every helper that operates on an EKS cluster object or the EKS API into a dedicated `common/eks/` package. After this commit, `common/eks/` contains: - authmode.go (was guess/clusterauthmode.go) - podidentityagent.go (was guess/ekspodidentityagent.go) - oidcprovider.go (was guess/oidcprovider.go) - privatesubnets.go (was guess/privatesubnets.go) Import alias `commoneks` is used to avoid colliding with the existing `github.com/aws/aws-sdk-go-v2/service/eks` (aliased `eks`). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…rn (#3012) * kubectl-datadog: extract EKS helpers to common/eks/ Continue the redistribution of `guess/` started by PRs #2980 and #3000: move every helper that operates on an EKS cluster object or the EKS API into a dedicated `common/eks/` package. After this commit, `common/eks/` contains: - authmode.go (was guess/clusterauthmode.go) - podidentityagent.go (was guess/ekspodidentityagent.go) - oidcprovider.go (was guess/oidcprovider.go) - privatesubnets.go (was guess/privatesubnets.go) Import alias `commoneks` is used to avoid colliding with the existing `github.com/aws/aws-sdk-go-v2/service/eks` (aliased `eks`). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * kubectl-datadog: consolidate aws-auth into common/awsauth/ `common/aws/aws-auth.go` was misnamed: it manipulates a Kubernetes ConfigMap (`kube-system/aws-auth`), not an AWS-SDK resource. Together with `guess/aws-auth.go` (read-only presence check), it belongs in a dedicated `common/awsauth/` package: both files operate on the same ConfigMap. Function names lose the now-redundant `AwsAuth` prefix: - `IsAwsAuthConfigMapPresent` -> `IsConfigMapPresent` - `EnsureAwsAuthRole` -> `EnsureRole` - `RemoveAwsAuthRole` -> `RemoveRole` `common/aws/` now contains only cloudformation.go (pure AWS-SDK). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * kubectl-datadog: fold clustername into common/clients/ The only caller of `guess.GetClusterNameFromKubeconfig` is the wrapper `clients.GetClusterNameFromKubeconfig` in `common/clients/clients.go`. Move the helper next to its caller as an unexported function `clusterNameFromKubeconfig`. The public API of `common/clients/` is unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * kubectl-datadog: carve out karpenter package Consolidate everything Karpenter-shaped into `common/karpenter/`, alongside the existing detection helper: - Intermediate model: NodePoolsSet, EC2NodeClass, NodePool, MetadataOptions, BlockDeviceMapping (from guess/nodepoolsset.go) - Producers from EKS node groups: GetNodeGroupsProperties (from guess/nodegroupproperties.go -> fromnodegroups.go) - Producers from running k8s Nodes: GetNodesProperties (from guess/nodesproperties.go -> fromnodes.go) - CR builders: CreateOrUpdateEC2NodeClass, CreateOrUpdateNodePool (from cluster/k8s/{ec2nodeclass,nodepool}.go) `cluster/k8s/` is gone (it was misnamed: those files build Karpenter CRDs, they aren't generic k8s primitives). `common/k8s/` keeps its narrow scope: generic object CRUD + deployment-finder. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * kubectl-datadog: awsauth: extract ConfigMap name/namespace constants `kube-system` and `aws-auth` each appeared 5 times across the three functions. Promote them to package-level constants so the package's single-ConfigMap scope is explicit at a glance. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
…2980) * [CASCL-1304] kubectl-datadog: enrich dd-cluster-info ConfigMap The dd-cluster-info ConfigMap (introduced by #2945) now records: - the running Karpenter installation (version, namespace, ownership) under a new `autoscaling` parent that also groups the existing clusterAutoscaler entry and a new eksAutoMode entry, - a `managedByDatadog` flag per node-management entity (Fargate profile, Karpenter NodePool), so a future migration tool can distinguish Datadog-managed entities to keep from legacy ones to drain. Detection helpers `FindKarpenterInstallation` and `IsEKSAutoModeEnabled` move from `install/guess/` to new `common/karpenter/` and `common/eksautomode/` packages so the clusterinfo classifier can reuse them. A generic `commonk8s.FindFirstDeployment` factors out the shared pager+predicate scan, and `commonk8s.ExtractDeploymentVersion` factors out the controller-image-tag → label fallback used by both detectors. Karpenter NodePool ownership uses the broader `autoscaling.datadoghq.com/created` label only (vs. uninstall's AND-pair with `app.kubernetes.io/managed-by: kubectl-datadog`) so NodePools managed by the Datadog cluster agent are also preserved by the migration tool. Datadog-managed NodePools with no nodes yet (typical right after install) are seeded into the snapshot with an empty Nodes list so the migration tool sees the destination NodePools exist. Fargate profile ownership reads tags via EKS DescribeFargateProfile; the `managed-by: kubectl-datadog` tag is propagated automatically from the CloudFormation stack tags, so no infrastructure change is needed. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [CASCL-1304] kubectl-datadog: fix missed call site after merge of #2976 The merge of main into the feature branch (commit 87c6e46) brought in PR #2976's `update.go`, which referenced `guess.FindKarpenterInstallation` — the symbol our PR moved to `karpenter.FindInstallation`. The resolution updated all other call sites but missed this one, so the build broke on the merge commit. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [CASCL-1304] kubectl-datadog: pass *Deployment to predicates The pager-driven scan in commonk8s.FindFirstDeployment yields *appsv1.Deployment values out of EachListItem, but the predicate parameter was a value type — forcing a several-hundred-byte struct copy on every iteration. Switch the predicate signature (and ExtractDeploymentVersion) to take a pointer, eliminating the copy in the per-iteration callback. Container-level predicates (isControllerContainer, isClusterAutoscalerContainer) keep value semantics because slices.ContainsFunc requires func(E) bool for []E. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [CASCL-1304] kubectl-datadog: table-drive FindFirstDeployment tests Collapse the three predicate-driven tests (NoMatch, ReturnsFirstMatch, ShortCircuits) into a single table that pins both the returned Deployment and the predicate-call count per row. The short-circuit invariant becomes a column rather than a dedicated test, and the row shape made it cheap to add three previously-uncovered cases (empty cluster, last deployment matches, multiple matches → first wins). PropagatesListError stays separate: its setup (PrependReactor) and assertions (ErrorIs) diverge from the predicate-counting flow. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [CASCL-1304] kubectl-datadog: centralize managed-by tag constants The Datadog ownership tag (`managed-by: kubectl-datadog`) is written on CloudFormation stacks by `aws.buildTags` and propagated by CFN to the EKS Fargate profile resources. The classifier in clusterinfo reads those propagated tags to flag Fargate profiles as ManagedByDatadog. Both ends previously embedded the same string literals; a rename in buildTags would have silently broken classification. Hoist the pair to exported constants `aws.ManagedByTag` / `aws.ManagedByTagValue` and reference them from both writer and reader. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [CASCL-1304] kubectl-datadog: extract cluster-autoscaler detector to its own package Mirror the karpenter package's shape — Installation type + FindInstallation function + private matchesDeployment / matchesContainer predicates — for the cluster-autoscaler detector, which had stayed inline in clusterinfo. Both detectors now answer "is the X controller running on this cluster?" with the same surface and the same package layout, eliminating the asymmetry that was masking the duplicated fingerprint pattern. The unit-level coverage (label variants, image-substring match, version extraction with image-tag → label fallback, scaled-to-zero treated as absent) moves to the new common/clusterautoscaler/clusterautoscaler_test.go table-driven tests. classify_test.go keeps a single integration smoke test verifying the snapshot wires the detection through. karpenter.go's predicates are renamed for symmetry: matchesController → matchesDeployment, isControllerContainer → matchesContainer. Both packages now expose the same internal vocabulary. clusterautoscaler.Installation has no IsOwn / InstalledBy / InstallerVersion fields, on purpose: kubectl-datadog never installs cluster-autoscaler, only detects it. The asymmetry with karpenter.Installation reflects an actual behavioural asymmetry. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [CASCL-1304] kubectl-datadog: list NodePools via the typed client The kubectl-datadog binary already imports `karpv1 "sigs.k8s.io/karpenter/pkg/apis/v1"` (via cluster/k8s/nodepool.go and cluster/common/clients/clients.go which registers `karpv1.NodePool` and `karpv1.NodePoolList` on the controller-runtime scheme). The justification for going through `unstructured.UnstructuredList` in `enrichKarpenterOwnership` was therefore wrong — replace it with a typed `karpv1.NodePoolList` and drop the unstructured import. The fake controller-runtime client in classify_test.go is updated to mirror the production scheme (typed NodePool/NodePoolList registration). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [CASCL-1304] kubectl-datadog: drop redundant empty-bucket guard `range` over a nil or empty map is a no-op in Go, so the early-return guard in enrichFargateOwnership was dead code. The function only reads the bucket (it never initialises it), so no side effect is at risk. The analogous guard in enrichKarpenterOwnership stays — it prevents creating an empty NodeManagerKarpenter bucket when no Datadog NodePools exist, which is a load-bearing semantic. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [CASCL-1304] kubectl-datadog: gofmt clusterautoscaler_test.go Struct-field alignment fix that go fmt picked up — the dd-gitlab check_formatting job runs `make fmt && git diff --exit-code`. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [CASCL-1304] kubectl-datadog: record cluster ARN and AWS region Per the Slack discussion with Cedric on PR #2980, surface the cluster identifier in the snapshot so a downstream tool can locate the cluster unambiguously (the ARN embeds the AWS account, region and short name). Classify gains a single EKS DescribeCluster call up front, parses the ARN with aws/arn.Parse to derive the region, and populates two new fields on ClusterInfo (`clusterArn` / `region`, both `omitempty`). The EKSDescriber interface grows to expose DescribeCluster alongside the existing DescribeFargateProfile so the test fake can substitute both. Best-effort: a DescribeCluster error or a malformed ARN logs a warning and leaves the fields empty rather than failing the snapshot — same contract as the other autoscaling detectors. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
…rn (#3012) * kubectl-datadog: extract EKS helpers to common/eks/ Continue the redistribution of `guess/` started by PRs #2980 and #3000: move every helper that operates on an EKS cluster object or the EKS API into a dedicated `common/eks/` package. After this commit, `common/eks/` contains: - authmode.go (was guess/clusterauthmode.go) - podidentityagent.go (was guess/ekspodidentityagent.go) - oidcprovider.go (was guess/oidcprovider.go) - privatesubnets.go (was guess/privatesubnets.go) Import alias `commoneks` is used to avoid colliding with the existing `github.com/aws/aws-sdk-go-v2/service/eks` (aliased `eks`). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * kubectl-datadog: consolidate aws-auth into common/awsauth/ `common/aws/aws-auth.go` was misnamed: it manipulates a Kubernetes ConfigMap (`kube-system/aws-auth`), not an AWS-SDK resource. Together with `guess/aws-auth.go` (read-only presence check), it belongs in a dedicated `common/awsauth/` package: both files operate on the same ConfigMap. Function names lose the now-redundant `AwsAuth` prefix: - `IsAwsAuthConfigMapPresent` -> `IsConfigMapPresent` - `EnsureAwsAuthRole` -> `EnsureRole` - `RemoveAwsAuthRole` -> `RemoveRole` `common/aws/` now contains only cloudformation.go (pure AWS-SDK). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * kubectl-datadog: fold clustername into common/clients/ The only caller of `guess.GetClusterNameFromKubeconfig` is the wrapper `clients.GetClusterNameFromKubeconfig` in `common/clients/clients.go`. Move the helper next to its caller as an unexported function `clusterNameFromKubeconfig`. The public API of `common/clients/` is unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * kubectl-datadog: carve out karpenter package Consolidate everything Karpenter-shaped into `common/karpenter/`, alongside the existing detection helper: - Intermediate model: NodePoolsSet, EC2NodeClass, NodePool, MetadataOptions, BlockDeviceMapping (from guess/nodepoolsset.go) - Producers from EKS node groups: GetNodeGroupsProperties (from guess/nodegroupproperties.go -> fromnodegroups.go) - Producers from running k8s Nodes: GetNodesProperties (from guess/nodesproperties.go -> fromnodes.go) - CR builders: CreateOrUpdateEC2NodeClass, CreateOrUpdateNodePool (from cluster/k8s/{ec2nodeclass,nodepool}.go) `cluster/k8s/` is gone (it was misnamed: those files build Karpenter CRDs, they aren't generic k8s primitives). `common/k8s/` keeps its narrow scope: generic object CRUD + deployment-finder. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * kubectl-datadog: awsauth: extract ConfigMap name/namespace constants `kube-system` and `aws-auth` each appeared 5 times across the three functions. Promote them to package-level constants so the package's single-ConfigMap scope is explicit at a glance. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
What does this PR do?
Enriches the
dd-cluster-infoConfigMap (introduced by #2945) so a future migration tool can:managedByDatadogflag.autoscalingparent that also groups the existingclusterAutoscalerentry and a neweksAutoModeentry.As a side-effect, two detection helpers (
FindKarpenterInstallation,IsEKSAutoModeEnabled) move frominstall/guess/to newcommon/karpenter/andcommon/eksautomode/packages so theclusterinfoclassifier can reuse them. A genericcommonk8s.FindFirstDeploymentfactors out the shared pager+predicate scan, andcommonk8s.ExtractDeploymentVersionfactors out the controller-image-tag → label fallback used by both the Karpenter and Cluster Autoscaler detectors.Motivation
Follow-up to #2945 (CASCL-1304). The original snapshot only captured nodes grouped by their owning manager; the future migration tool also needs to tell Datadog-managed managers (to keep) from legacy ones (to drain), to know whether Karpenter is already running, and to know whether EKS auto-mode is active so it can short-circuit when there is no migration to drive.
Additional Notes
dd-cluster-infoexists yet (grep -r "dd-cluster-info\|ConfigMapDataKey" --include='*.go'returns only the writer).APIVersionstays atv1.autoscaling.datadoghq.com/createdlabel alone — notuninstall.go's AND-pair withapp.kubernetes.io/managed-by: kubectl-datadog. The cluster agent creates NodePools with only thecreatedlabel, and the migration tool must preserve them too. This divergence is deliberate.EKS.DescribeFargateProfile. The expectedmanaged-by: kubectl-datadogtag is propagated automatically from the CloudFormation stack tags written bycommon/aws/cloudformation.go, so no infrastructure change is needed.nodeslist so the migration tool sees the destination NodePools exist.DescribeFargateProfile, NodePool list, Discovery for auto-mode) is tolerated — transient errors / missing CRDs log a warning and leave entries unflagged rather than failing the snapshot. The call site (recordClusterInfo) was already best-effort before this PR.Minimum Agent Versions
Describe your test plan
Automated coverage added in this PR:
TestClassify_KarpenterNodePoolOwnership: kubectl-datadog (both labels) + cluster agent (createdlabel only) + Datadog NodePool with no nodes yet + foreign NodePool.TestClassify_KarpenterNodePoolOwnership_NoCRD: tolerant ofmeta.IsNoMatchErrorwhen the Karpenter CRD is not installed.TestClassify_FargateProfileOwnership/TestClassify_FargateProfileOwnership_DescribeError: tag-based detection + AWS API error fallback.TestClassify_KarpenterDetection: version extraction from controller image tag,ManagedByDatadog/InstallerVersionfrom sentinel labels.TestClassify_EKSAutoMode: discovery API exposesnodeclasses→Enabled: true.TestPersist_YAMLShape: pins lowerCamelCase wire keys against thegopkg.in/yaml.v3lower-case-by-default footgun.TestFindFirstDeployment_*: covers the new generic helper.Manual test plan on a sandbox EKS cluster:
kubectl datadog autoscaling cluster installsucceeds.kubectl get cm -n dd-karpenter dd-cluster-info -o yamlcontains:autoscaling.karpenter.{present, version, managedByDatadog, installerVersion}autoscaling.eksAutoMode.enablednodeManagement.fargate."dd-karpenter-<cluster>".managedByDatadog: truenodeManagement.karpenterwithmanagedByDatadog: true, even with no node landed on it yet.managedByDatadog: true.Checklist
enhancement,refactoringqa/skip-qalabel