Skip to content

[TON-742] Support managed Agent installation and removal - #3268

Open
fanny-jiang wants to merge 20 commits into
mainfrom
fanny/TON-723/operator-addon-lifecycle
Open

[TON-742] Support managed Agent installation and removal#3268
fanny-jiang wants to merge 20 commits into
mainfrom
fanny/TON-723/operator-addon-lifecycle

Conversation

@fanny-jiang

@fanny-jiang fanny-jiang commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds opt-in support for managed Datadog Agent installation and removal. The Operator reads installation requests from a ConfigMap, creates or removes the DatadogAgent and Windows AgentProfile resources, and reports progress and completion through Remote Config.

The new flow coordinates with existing Fleet configuration updates so the two operations do not overwrite each other's state.

Motivation

The EKS instrumenter needs a reliable way to install the Agent and confirm that uninstall has completed before removing the Datadog Operator add-on.

Additional Notes

The feature is disabled by default. EKS is the first supported provider, while the managed-installation flow remains provider-neutral and extensible. Existing DatadogAgent and DatadogAgentInternal reconciliation behavior is unchanged.

Minimum Agent Versions

  • Agent: N/A
  • Cluster Agent: N/A

Describe your test plan

  • CGO_ENABLED=0 make ci-test
  • CGO_ENABLED=0 make lint

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)

@datadog-datadog-prod-us1

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

Copy link
Copy Markdown

Code Coverage

🎯 Code Coverage (details)
Patch Coverage: 85.54%
Overall Coverage: 48.98% (+2.08%)

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

@fanny-jiang fanny-jiang added the enhancement New feature or request label Jul 16, 2026
@fanny-jiang
fanny-jiang changed the base branch from fanny/TON-723/operator-lifecycle-prerequisites to main July 16, 2026 20:11
@fanny-jiang fanny-jiang changed the title Add EKS add-on Agent lifecycle handling Add managed Agent lifecycle handling for EKS add-ons Jul 16, 2026
@fanny-jiang fanny-jiang changed the title Add managed Agent lifecycle handling for EKS add-ons Add managed Agent installation handling for EKS add-ons Jul 16, 2026
@fanny-jiang fanny-jiang changed the title Add managed Agent installation handling for EKS add-ons Add managed Agent installation intent handling Jul 17, 2026
@fanny-jiang fanny-jiang added this to the v1.29.0 milestone Jul 20, 2026
@fanny-jiang fanny-jiang changed the title Add managed Agent installation intent handling Support managed Agent installation and removal Jul 20, 2026
@fanny-jiang

Copy link
Copy Markdown
Contributor Author

@codex review

@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: d7f0b3296d

ℹ️ 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 thread cmd/main.go Outdated
@fanny-jiang

Copy link
Copy Markdown
Contributor Author

@codex review

@fanny-jiang
fanny-jiang marked this pull request as ready for review July 20, 2026 18:45
@fanny-jiang
fanny-jiang requested a review from a team July 20, 2026 18:45
@fanny-jiang
fanny-jiang requested a review from a team as a code owner July 20, 2026 18:45

@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: b92399a8e7

ℹ️ 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 thread pkg/fleet/managed_agent_installation_windows_profile.go

@gpalmz gpalmz 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.

Really solid, thoroughly tested PR. Approving. Left a few comments - a couple are MED questions worth a look (the credential-guard casing bypass, the promote-reported-as-failed coupling, and the partial-stuck edge), the rest are minor.

Comment thread pkg/fleet/managed_agent_installation.go Outdated
if _, ok := global["credentials"]; ok {
return fmt.Errorf("config must not contain spec.global.credentials")
}
for _, field := range []string{"clusterAgentToken", "clusterAgentTokenSecret"} {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

encoding/json matches struct fields case-insensitively, so a config with capitalized keys (e.g. {"Spec":{"Global":{"ClusterAgentToken":"..."}}}) slips past this case-sensitive map scan and still decodes into the typed struct. buildFleetDatadogAgentSpec overwrites Credentials but not ClusterAgentToken/ClusterAgentTokenSecret, so those would survive into the created DDA. Since it's already decoded into a typed struct, could we reject on the decoded fields instead (non-nil Global.Credentials/ClusterAgentToken/ClusterAgentTokenSecret)? That closes the casing gap and drops the duplicate parse.

func (d *Daemon) finishPendingOperation(ctx context.Context, task pendingOperation, resultErr error) {
if resultErr == nil && task.intent == pendingIntentPromote {
if err := d.persistManagedAgentInstallationStableConfig(ctx, task.nsn, task.experimentID, task.resultVersion); err != nil {
resultErr = fmt.Errorf("persist promoted managed Agent installation config: %w", err)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

If the reconciler has already promoted the experiment but persistManagedAgentInstallationStableConfig fails hard (not a conflict), this flips resultErr and reports the promote as failed to RC, even though the cluster is in the promoted phase - so Fleet may re-drive a successful operation. Is the re-drive idempotent here, or would it be safer to report the promote DONE and retry the config-persist separately (best-effort/async) rather than gating the task result on it? Same coupling on the daemon_operations.go fast path.

Comment thread pkg/fleet/managed_agent_installation.go Outdated
return fmt.Errorf("hash live DatadogAgent spec: %w", err)
}
if dda.Annotations[fleetConfigHashAnnotation] != liveHash {
return &stateDoesntMatchError{msg: fmt.Sprintf("DatadogAgent %s/%s spec changed after Fleet config %q was accepted", dda.Namespace, dda.Name, configID)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Small durability edge: if the process dies after Create but before recordFleetDatadogAgentSpecHash commits, the DDA is left partial with fleetConfigHashAnnotation = hash(preSpec) while the live spec is the post-admission spec. On retry, this annotation != liveHash check fails terminally ("spec changed after Fleet config was accepted") and the install never self-heals. This only bites if a DDA .spec is mutated on admission/defaulting - is that possible here? If so, could the replay path re-record the accepted hash (like the create path does) when the spec still passes DeepDerivative, rather than hard-failing?

Comment thread pkg/fleet/daemon.go Outdated
}
if fleetOwned != nil && unmanaged != nil {
return &stateDoesntMatchError{msg: fmt.Sprintf(
"Fleet-managed DatadogAgent %s/%s coexists with unmanaged DatadogAgent %s/%s",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ddas.Items is pre-filtered to the canonical target above, so the multiple-owned / active-experiment / owned+unmanaged-coexistence branches here can't fire (activeExperiment is also unused). Not a deletion risk, but it means the coexistence guard doesn't actually guard. Classify before the filter, or drop the dead branches?

if d.managedAgentInstallationUpdates == nil {
return
}
time.AfterFunc(delay, d.requestManagedAgentInstallationRetry)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

time.AfterFunc here isn't tracked or stopped on ctx.Done(), so after shutdown the timer still fires into requestManagedAgentInstallationRetry (whose consumer has exited). Harmless per-timer, but they can accumulate across leader churn - worth gating the callback on a context check or stopping the timers on Stop.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The callback only performs a non-blocking send, and all delays are at most 8 seconds; manager shutdown terminates the process. I'd prefer to keep this scoped and defer timer tracking.

@fanny-jiang fanny-jiang changed the title Support managed Agent installation and removal [TON-742] Support managed Agent installation and removal Jul 21, 2026
}

func (d *Daemon) installManagedAgentInstallationIntentForwarder(ctx context.Context) error {
informer, err := d.cache.GetInformer(ctx, &corev1.ConfigMap{})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Managed install can be enabled while the cache watches a different namespace. The feature hard-codes managed resources to datadog-agent in pkg/fleet/managed_agent_installation.go:52-56, but cache namespaces still come from DD_AGENT_WATCH_NAMESPACE/WATCH_NAMESPACE in pkg/config/config.go:245-249. The intent/credential informers in pkg/fleet/managed_agent_installation_intent.go:87-119 will miss updates if that namespace is not watched

@fanny-jiang fanny-jiang modified the milestones: v1.29.0, v1.30.0 Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants