Skip to content

Support extra DCA RC clients - #55606

Draft
clamoriniere wants to merge 15 commits into
mainfrom
dd/clamoriniere/support-extra-dca-rc-clients-202608281556
Draft

Support extra DCA RC clients#55606
clamoriniere wants to merge 15 commits into
mainfrom
dd/clamoriniere/support-extra-dca-rc-clients-202608281556

Conversation

@clamoriniere

@clamoriniere clamoriniere commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Allows the Cluster Agent to run several Remote Configuration clients in one process, each with its own endpoint, API key, TUF roots and cache database.

Extra clients are declared under cluster_agent.remote_configuration.additional_clients. The client name selects the subsystem it serves, and with it the RC products it owns — autoscaling, kubeactions, private_action_runner or apm_instrumentation. Any product not claimed by an extra client keeps using the default client and the default cache, so existing deployments are unaffected. Clients are created lazily, only when a product they own is actually requested.

cluster_agent:
  remote_configuration:
    additional_clients:
      autoscaling:
        rc_dd_url: https://config.datacat.com
        site: datacat.com
        api_key: ENC[k8s_secret@datadog/datadog-secret/datacat-api-key]

The setting is not published in the generated datadog.yaml documentation: it is intended for internal testing, not for customer configuration.

datadog-cluster-agent status reports each client, so a misconfigured extra client is visible rather than silent:

Remote Configuration
====================

Endpoint: https://config.datadoghq.com.
Organization enabled: True
API Key: Authorized
Last error: None

Additional clients
------------------
  - autoscaling
      Endpoint: https://config.datacat.com
      Organization enabled: True
      API Key: Authorized
      Last error: None

A new Autoscaling status section reports workload autoscaling health: the number of DatadogPodAutoscalers, which RC client serves it, and per product the last received config version and update time.

Autoscaling
===========

  Workload
  --------
  DatadogPodAutoscalers: 2
  Leader: true
  Remote Config connection: Receiving updates
  Remote Config client: autoscaling

  CONTAINER_AUTOSCALING_SETTINGS
    Last config version: 2
    Last update: 2026-08-28T22:59:53Z (5m56s ago)
    Configs in last update: 1
    Updates received: 1

  CONTAINER_AUTOSCALING_VALUES
    Last config version: 41
    Last update: 2026-08-28T23:05:48Z (1s ago)
    Configs in last update: 1
    Updates received: 7

Both samples are taken from a Cluster Agent whose autoscaling client targets a different Remote Configuration backend than its default client, with the extra endpoint anonymized.

Motivation

The Cluster Agent assumed a single Remote Configuration client per process, which made endpoint and API key ownership process-wide: every service watched the top-level api_key, status expvars were shared, and instances would have reused the same cache database.

Running autoscaling against a different Remote Configuration backend than the rest of the Cluster Agent requires those resources to be per-instance.

Describe how you validated your changes

Unit tests covering spec parsing and validation, product routing, lazy client creation, API key isolation, per-instance status, and the new status sections.

Also validated end to end on a local kind cluster, with the autoscaling client pointed at the staging Remote Configuration backend while the default client stayed on production: both clients authenticated independently, kept separate cache databases, and the autoscaling client received live configuration.

Additional Notes

Configuration is rejected at startup rather than degrading silently when it cannot work: process-level products (AGENT_CONFIG, AGENT_TASK) on an extra client, a product claimed by two clients, products used by one subsystem split across clients, colliding or path-like cache database filenames, and a client named Remote Config, which is reserved for the default client's status entry.


PR by Bits - View session in Datadog

Comment @DataDog to request changes

🤖 PR description and code assisted by Claude:claude-opus-5

Co-authored-by: clamoriniere <cedric.lamoriniere@datadoghq.com>
@datadog-official

datadog-official Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

View session in Datadog

Bits Code status: ✅ Done

CI Auto-fix: Disabled | Enable

Comment @DataDog to request changes

@datadog-datadog-prod-us1-2

Copy link
Copy Markdown
Contributor

I can only run on private repositories.

@dd-octo-sts dd-octo-sts Bot added the internal Identify a non-fork PR label Aug 28, 2026
@github-actions github-actions Bot added the long review PR is complex, plan time to review it label Aug 28, 2026

@github-actions github-actions Bot left a comment

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.

AI review by Codex (OpenAI) - workflow run

Patch is incorrect. Additional clients can use the wrong TUF director root, and cache collision validation compares unnormalized paths.

Comment thread cmd/cluster-agent/subcommands/start/remote_config.go Outdated
if spec.DatabaseFileName == "" {
spec.DatabaseFileName = fmt.Sprintf("remote-config-%s.db", safeRemoteConfigInstanceName(spec.Name))
}
if owner, found := databaseFileOwners[spec.DatabaseFileName]; found {

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.

Collision detection compares raw filenames, but NewService later resolves them with path.Join(run_path, filename). Values such as x/../remote-config.db or two differently normalized paths therefore bypass this check and share a cache, allowing one service to corrupt or clear another's state. Validate basename-only filenames or compare cleaned resolved paths.

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.

it is fixed now

Co-authored-by: clamoriniere <cedric.lamoriniere@datadoghq.com>
@datadog-official

datadog-official Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

🎯 Code Coverage (details)
Patch Coverage: 60.05%
Overall Coverage: 52.74% (+0.00%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: bcc073b | Docs | View more details | Give us feedback!

@dd-octo-sts

dd-octo-sts Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Files inventory check summary

File checks results against ancestor c2fb6573:

Results for datadog-agent_7.84.0~devel.git.587.bcc073b.pipeline.134076030-1_amd64.deb:

No change detected

Results for datadog-iot-agent_7.84.0~devel.git.587.bcc073b.pipeline.134076030-1_amd64.deb:

No change detected

Co-authored-by: clamoriniere <cedric.lamoriniere@datadoghq.com>
@cit-pr-commenter-54b7da

cit-pr-commenter-54b7da Bot commented Aug 28, 2026

Copy link
Copy Markdown

Regression Detector

Regression Detector Results

Metrics dashboard
Target profiles
Job ID: 354573e0-544d-495c-a10d-199d08a67c71

Baseline: c2fb657
Comparison: bcc073b
Diff

Optimization Goals: ✅ No significant changes detected

Fine details of change detection per experiment

perf experiment goal Δ mean % Δ mean % CI trials links
dsd_uds_10mb_3k_timestamped_contexts_cpu % cpu utilization +1.29 [+1.02, +1.56] 1 Logs
quality_gate_logs % cpu utilization +0.48 [-0.40, +1.35] 1 Logs bounds checks dashboard
quality_gate_idle_all_features memory utilization +0.16 [+0.13, +0.19] 1 Logs bounds checks dashboard
quality_gate_security_mean_fs_load memory utilization +0.16 [+0.12, +0.20] 1 Logs bounds checks dashboard
quality_gate_security_idle memory utilization +0.15 [+0.10, +0.20] 1 Logs bounds checks dashboard
quality_gate_idle memory utilization +0.14 [+0.10, +0.19] 1 Logs bounds checks dashboard
dsd_uds_10mb_3k_timestamped_contexts_memory memory utilization +0.11 [-0.10, +0.33] 1 Logs
quality_gate_security_no_fs_load memory utilization -0.05 [-0.13, +0.03] 1 Logs bounds checks dashboard
quality_gate_private_action_runner memory utilization -0.50 [-0.62, -0.39] 1 Logs bounds checks dashboard
quality_gate_metrics_logs memory utilization -0.54 [-0.78, -0.31] 1 Logs bounds checks dashboard

Bounds Checks: ✅ Passed

perf experiment bounds_check_name replicates_passed observed_value links
quality_gate_idle intake_connections 10/10 4 = 4 bounds checks dashboard
quality_gate_idle memory_usage 10/10 174.18MiB ≤ 179MiB bounds checks dashboard
quality_gate_idle total_bytes_received 10/10 753.64KiB ≤ 819.20KiB bounds checks dashboard
quality_gate_idle_all_features intake_connections 10/10 4 = 4 bounds checks dashboard
quality_gate_idle_all_features memory_usage 10/10 522.78MiB ≤ 537MiB bounds checks dashboard
quality_gate_idle_all_features total_bytes_received 10/10 1.15MiB ≤ 1.25MiB bounds checks dashboard
quality_gate_logs intake_connections 10/10 19 ≤ 40 bounds checks dashboard
quality_gate_logs memory_usage 10/10 209.74MiB ≤ 220MiB bounds checks dashboard
quality_gate_logs missed_bytes 10/10 0B = 0B bounds checks dashboard
quality_gate_logs total_bytes_received 10/10 263.50MiB ≤ 292MiB bounds checks dashboard
quality_gate_metrics_logs cpu_usage 10/10 372.75 ≤ 2000 bounds checks dashboard
quality_gate_metrics_logs intake_connections 10/10 20 ≤ 40 bounds checks dashboard
quality_gate_metrics_logs memory_usage 10/10 426.34MiB ≤ 455MiB bounds checks dashboard
quality_gate_metrics_logs missed_bytes 10/10 0B = 0B bounds checks dashboard
quality_gate_metrics_logs total_bytes_received 10/10 0.94GiB ≤ 1.04GiB bounds checks dashboard
quality_gate_private_action_runner memory_usage 10/10 72.03MiB ≤ 75MiB bounds checks dashboard
quality_gate_security_idle cpu_usage 10/10 28.43 ≤ 100 bounds checks dashboard
quality_gate_security_idle memory_usage 10/10 328.73MiB ≤ 355MiB bounds checks dashboard
quality_gate_security_mean_fs_load cpu_usage 10/10 64.25 ≤ 200 bounds checks dashboard
quality_gate_security_mean_fs_load memory_usage 10/10 305.23MiB ≤ 335MiB bounds checks dashboard
quality_gate_security_no_fs_load cpu_usage 10/10 22.64 ≤ 100 bounds checks dashboard
quality_gate_security_no_fs_load memory_usage 10/10 308.86MiB ≤ 345MiB bounds checks dashboard

Explanation

Confidence level: 90.00%
Effect size tolerance: |Δ mean %| ≥ 5.00%

Performance changes are noted in the perf column of each table:

  • ✅ = significantly better comparison variant performance
  • ❌ = significantly worse comparison variant performance
  • ➖ = no significant change in performance

A regression test is an A/B test of target performance in a repeatable rig, where "performance" is measured as "comparison variant minus baseline variant" for an optimization goal (e.g., ingress throughput). Due to intrinsic variability in measuring that goal, we can only estimate its mean value for each experiment; we report uncertainty in that value as a 90.00% confidence interval denoted "Δ mean % CI".

For each experiment, we decide whether a change in performance is a "regression" -- a change worth investigating further -- if all of the following criteria are true:

  1. Its estimated |Δ mean %| ≥ 5.00%, indicating the change is big enough to merit a closer look.

  2. Its 90.00% confidence interval "Δ mean % CI" does not contain zero, indicating that if our statistical model is accurate, there is at least a 90.00% chance there is a difference in performance between baseline and comparison variants.

  3. Its configuration does not mark it "erratic".

CI Pass/Fail Decision

Passed. All Quality Gates passed.

  • quality_gate_security_idle, bounds check cpu_usage: 10/10 replicas passed. Gate passed.
  • quality_gate_security_idle, bounds check memory_usage: 10/10 replicas passed. Gate passed.
  • quality_gate_idle, bounds check total_bytes_received: 10/10 replicas passed. Gate passed.
  • quality_gate_idle, bounds check memory_usage: 10/10 replicas passed. Gate passed.
  • quality_gate_idle, bounds check intake_connections: 10/10 replicas passed. Gate passed.
  • quality_gate_metrics_logs, bounds check intake_connections: 10/10 replicas passed. Gate passed.
  • quality_gate_metrics_logs, bounds check memory_usage: 10/10 replicas passed. Gate passed.
  • quality_gate_metrics_logs, bounds check missed_bytes: 10/10 replicas passed. Gate passed.
  • quality_gate_metrics_logs, bounds check cpu_usage: 10/10 replicas passed. Gate passed.
  • quality_gate_metrics_logs, bounds check total_bytes_received: 10/10 replicas passed. Gate passed.
  • quality_gate_idle_all_features, bounds check memory_usage: 10/10 replicas passed. Gate passed.
  • quality_gate_idle_all_features, bounds check intake_connections: 10/10 replicas passed. Gate passed.
  • quality_gate_idle_all_features, bounds check total_bytes_received: 10/10 replicas passed. Gate passed.
  • quality_gate_security_no_fs_load, bounds check memory_usage: 10/10 replicas passed. Gate passed.
  • quality_gate_security_no_fs_load, bounds check cpu_usage: 10/10 replicas passed. Gate passed.
  • quality_gate_private_action_runner, bounds check memory_usage: 10/10 replicas passed. Gate passed.
  • quality_gate_logs, bounds check intake_connections: 10/10 replicas passed. Gate passed.
  • quality_gate_logs, bounds check memory_usage: 10/10 replicas passed. Gate passed.
  • quality_gate_logs, bounds check missed_bytes: 10/10 replicas passed. Gate passed.
  • quality_gate_logs, bounds check total_bytes_received: 10/10 replicas passed. Gate passed.
  • quality_gate_security_mean_fs_load, bounds check cpu_usage: 10/10 replicas passed. Gate passed.
  • quality_gate_security_mean_fs_load, bounds check memory_usage: 10/10 replicas passed. Gate passed.

datadog-bits and others added 2 commits August 28, 2026 18:32
Co-authored-by: clamoriniere <cedric.lamoriniere@datadoghq.com>
@dd-octo-sts

dd-octo-sts Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Static quality checks

✅ Please find below the results from static quality gates
Comparison made with ancestor c2fb657
📊 Static Quality Gates Dashboard
🔗 SQG Job

Successful checks

Info

Quality gate Change Size (prev → curr → max)
agent_deb_amd64 +33.06 KiB (0.00% increase, -1.02% of buffer) 761.675 → 761.707 → 764.840
agent_deb_amd64_fips +21.06 KiB (0.00% increase, -0.95% of buffer) 713.924 → 713.945 → 716.080
agent_heroku_amd64 +8.03 KiB (0.00% increase, -0.13% of buffer) 312.967 → 312.974 → 319.080
agent_msi +26.0 KiB (0.00% increase, -0.20% of buffer) 648.347 → 648.373 → 661.050
agent_rpm_amd64 +33.06 KiB (0.00% increase, -1.02% of buffer) 761.658 → 761.691 → 764.810
agent_rpm_amd64_fips +21.06 KiB (0.00% increase, -0.95% of buffer) 713.908 → 713.928 → 716.080
agent_rpm_arm64 +25.06 KiB (0.00% increase, -3.23% of buffer) 737.552 → 737.576 → 738.310
agent_rpm_arm64_fips +25.03 KiB (0.00% increase, -2.03% of buffer) 693.146 → 693.171 → 694.350
agent_suse_amd64 +33.06 KiB (0.00% increase, -1.02% of buffer) 761.658 → 761.691 → 764.810
agent_suse_amd64_fips +21.06 KiB (0.00% increase, -0.95% of buffer) 713.908 → 713.928 → 716.080
agent_suse_arm64 +25.06 KiB (0.00% increase, -3.23% of buffer) 737.552 → 737.576 → 738.310
agent_suse_arm64_fips +25.03 KiB (0.00% increase, -2.03% of buffer) 693.146 → 693.171 → 694.350
docker_agent_amd64 +25.03 KiB (0.00% increase, -7.91% of buffer) 820.691 → 820.715 → 821.000
docker_agent_arm64 +25.03 KiB (0.00% increase, -6.80% of buffer) 821.620 → 821.645 → 821.980
docker_agent_jmx_amd64 +25.03 KiB (0.00% increase, -7.97% of buffer) 1011.603 → 1011.628 → 1011.910
docker_agent_jmx_arm64 +25.03 KiB (0.00% increase, -6.65% of buffer) 1001.312 → 1001.337 → 1001.680
docker_cluster_agent_amd64 +56.25 KiB (0.03% increase, -6.30% of buffer) 211.258 → 211.312 → 212.130
docker_cluster_agent_arm64 +64.24 KiB (0.03% increase, -7.64% of buffer) 224.398 → 224.461 → 225.220
docker_dogstatsd_amd64 +4.0 KiB (0.01% increase, -0.44% of buffer) 39.545 → 39.549 → 40.440
iot_agent_deb_amd64 +4.03 KiB (0.01% increase, -0.40% of buffer) 46.568 → 46.572 → 47.550
iot_agent_deb_arm64 +8.03 KiB (0.02% increase, -0.77% of buffer) 43.204 → 43.212 → 44.220
iot_agent_deb_armhf +8.02 KiB (0.02% increase, -0.78% of buffer) 44.015 → 44.022 → 45.020
iot_agent_rpm_amd64 +4.03 KiB (0.01% increase, -0.40% of buffer) 46.568 → 46.572 → 47.550
iot_agent_suse_amd64 +4.03 KiB (0.01% increase, -0.40% of buffer) 46.567 → 46.571 → 47.550
9 successful checks with minimal change (< 2 KiB)
Quality gate Current Size
docker_cws_instrumentation_amd64 7.439 MiB
docker_cws_instrumentation_arm64 6.877 MiB
docker_dogstatsd_arm64 37.623 MiB
docker_host_profiler_amd64 306.862 MiB
docker_host_profiler_arm64 318.167 MiB
dogstatsd_deb_amd64 30.290 MiB
dogstatsd_deb_arm64 28.302 MiB
dogstatsd_rpm_amd64 30.290 MiB
dogstatsd_suse_amd64 30.290 MiB

The secrets status template ended with `{{- end -}}`, whose trailing `-}}`
swallowed the file's final newline, so the section emitted no trailing
newline at all. The status renderer only appends a single newline after each
section and printHeader writes its `===` rule immediately, so the next
section's header landed flush against the last line of the secrets output.

This was latent: secrets was the last rendered section, so nothing ever
followed it. Every other status template already ends with a newline.

Assisted-by: Claude:claude-opus-5
Per-instance remote configuration status was already exported under
remoteConfigStatus.instances, but the status templates only rendered the
default client. A Cluster Agent with extra RC clients showed no sign they
existed, and a failing extra client was invisible to `status`.

Render every instance, skipping the default one so it is not listed twice.
Also record each instance's endpoint URL, which is the point of extra
clients: they can target a different backend than the default. Userinfo is
stripped from the URL before it reaches the status output, since rc_dd_url
is user supplied and status output is routinely pasted into tickets.

Assisted-by: Claude:claude-opus-5
There was no way to see, from `datadog-cluster-agent status`, whether
workload autoscaling was receiving anything over remote configuration.

Report the number of DatadogPodAutoscalers, leader state, and per RC
product the last received config version, last update time and age, configs
in the last update, cumulative update count, and last apply error. The
update is recorded before the configs are processed, so a config that fails
to apply still shows as received and its error is surfaced separately
rather than looking like a connectivity problem.

Three states are distinguished so the section is never ambiguous: disabled
in config, enabled but not started (StartWorkloadAutoscaling failing is
logged, not fatal), and running.

The section is named "Autoscaling" because status sections are ordered
alphabetically with only "collector" special cased; this places it after
"Autodiscovery" and leaves the group for cluster autoscaling to join later.
Only the workload products are reported for now.

The status also names the RC client serving the autoscaling products, using
the same instance key as the Remote Configuration section so the two can be
cross-referenced.

Assisted-by: Claude:claude-opus-5
Each extra client was rendered as its own free-standing block, which read as
a series of unrelated stanzas rather than one collection. Group them under
an "Additional clients" heading and render one indented list entry per
client, so the number of extra clients is obvious at a glance.

The default client is now filtered out by the provider rather than by a name
comparison inside the templates. That removes the duplicated magic string
from both templates and lets the heading appear only when at least one extra
client exists.

Assisted-by: Claude:claude-opus-5
Extra clients reported status under "cluster-agent:<name>", so a client
configured as "autoscaling" showed up as "cluster-agent:autoscaling" in both
the Remote Configuration and Autoscaling status sections. The prefix carried
no information the section heading did not already give.

Report them under the configured name instead. The prefix did guarantee that
an extra client could never collide with the default client's status key, so
reject "Remote Config" as a client name: without the prefix such a client
would silently share the default client's status entry, with the two
overwriting each other's fields and no error anywhere.

Assisted-by: Claude:claude-opus-5
Every additional client had to spell out the Remote Configuration products it
owned, but the only useful value was always the full product set of the
subsystem the client serves. Drop the "products" setting and derive the set
from the client name instead, so an entry needs only an endpoint and a key.

Names match the configuration that enables each subsystem and cover exactly
the products that subsystem resolves through ClientForProducts. An
unrecognised name is rejected: the name is the only thing selecting products,
so a client that owned nothing would never be used.

The autoscaling set includes the cluster autoscaling product on purpose.
command.go resolves all enabled autoscaling products in one call, so leaving
CLUSTER_AUTOSCALING_VALUES on the default client would split the subsystem
across two clients as soon as autoscaling.cluster.enabled is turned on.

Products with no ClientForProducts consumer are deliberately excluded.
Additional clients are created lazily when one of their products is
requested, so an unconsumed product would leave that product subscribed
nowhere at all. K8S_INJECTION_DD is the current example and stays on the
default client. A test pins the sets to that consumer list so the two cannot
drift apart.

Assisted-by: Claude:claude-opus-5
@clamoriniere clamoriniere added the changelog/no-changelog No changelog entry needed label Aug 28, 2026
Follow-up cleanup now that the feature works, with no behaviour change.

Export DefaultStatusInstance from pkg/config/remote/service and use it
everywhere. The "Remote Config" literal was repeated across four files, two of
them carrying "keep in sync" comments. The rcType argument to NewService keeps
its own literal: it is a log prefix, not a status key, and only happens to
share the string.

Drop the two options the default RC service passed that already matched the
service defaults, along with the constant and variable that only existed to
feed them. What remains is the case that differs: when the key comes from
remote_configuration.api_key, watch that setting for runtime updates instead
of the process-wide api_key.

Delete validateAdditionalRemoteConfigProducts. Products now come only from
curated presets, so it could never fire, and its messages still named the
removed "products" setting. The invariants it checked are asserted against the
preset table by TestRemoteConfigClientPresetsMatchConsumers.

Build the product routing map once instead of keeping extraProductOwners
alongside byProduct with the same key set.

Delete safeRemoteConfigInstanceName. Client names are now a closed set of
preset keys, all filename safe, so it can no longer change anything.

Assisted-by: Claude:claude-opus-5
@dd-octo-sts

dd-octo-sts Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Go Package Import Differences

Baseline: c2fb657
Comparison: bcc073b

binaryosarchchange
process-agentlinuxamd64
+6, -0
+github.com/DataDog/datadog-agent/pkg/config/remote/api
+github.com/DataDog/datadog-agent/pkg/config/remote/service
+github.com/DataDog/datadog-agent/pkg/config/remote/uptane
+github.com/DataDog/datadog-agent/pkg/proto/msgpgo
+go.etcd.io/bbolt
+go.etcd.io/bbolt/errors
process-agentlinuxarm64
+6, -0
+github.com/DataDog/datadog-agent/pkg/config/remote/api
+github.com/DataDog/datadog-agent/pkg/config/remote/service
+github.com/DataDog/datadog-agent/pkg/config/remote/uptane
+github.com/DataDog/datadog-agent/pkg/proto/msgpgo
+go.etcd.io/bbolt
+go.etcd.io/bbolt/errors
process-agentwindowsamd64
+7, -0
+github.com/DataDog/datadog-agent/pkg/config/remote/api
+github.com/DataDog/datadog-agent/pkg/config/remote/service
+github.com/DataDog/datadog-agent/pkg/config/remote/uptane
+github.com/DataDog/datadog-agent/pkg/proto/msgpgo
+github.com/DataDog/datadog-agent/pkg/trace/log
+go.etcd.io/bbolt
+go.etcd.io/bbolt/errors
process-agentdarwinamd64
+7, -0
+github.com/DataDog/datadog-agent/pkg/config/remote/api
+github.com/DataDog/datadog-agent/pkg/config/remote/service
+github.com/DataDog/datadog-agent/pkg/config/remote/uptane
+github.com/DataDog/datadog-agent/pkg/proto/msgpgo
+github.com/DataDog/datadog-agent/pkg/trace/log
+go.etcd.io/bbolt
+go.etcd.io/bbolt/errors
process-agentdarwinarm64
+7, -0
+github.com/DataDog/datadog-agent/pkg/config/remote/api
+github.com/DataDog/datadog-agent/pkg/config/remote/service
+github.com/DataDog/datadog-agent/pkg/config/remote/uptane
+github.com/DataDog/datadog-agent/pkg/proto/msgpgo
+github.com/DataDog/datadog-agent/pkg/trace/log
+go.etcd.io/bbolt
+go.etcd.io/bbolt/errors
heroku-process-agentlinuxamd64
+7, -0
+encoding/base32
+github.com/DataDog/datadog-agent/pkg/config/remote/api
+github.com/DataDog/datadog-agent/pkg/config/remote/service
+github.com/DataDog/datadog-agent/pkg/config/remote/uptane
+github.com/DataDog/datadog-agent/pkg/proto/msgpgo
+go.etcd.io/bbolt
+go.etcd.io/bbolt/errors

This change was also adjusting the default and MRF remote config services:
pointing their API key watcher at the setting their key actually comes from,
and giving MRF its own status entry.

Both are improvements to pre-existing behaviour, not requirements of
additional clients. The API key watcher is hardcoded to "api_key" today, so
the MRF service reacts to the wrong setting and ignores updates to its own.
The status expvars were package-level globals that every service instance
wrote to, so MRF has always overwritten the default client's entry; leaving it
on the shared entry keeps exactly that behaviour.

Dropping both makes this change strictly additive for existing deployments and
leaves the two files identical to main. Each is worth fixing on its own, where
it can be reviewed as the behaviour change it is.

Assisted-by: Claude:claude-opus-5
Additional clients exposed an "api_key_setting" so their key could be read
from, and followed at, another config path. Nothing used it: every client
sets api_key directly, with ENC[] handling secrets, and the only caller passed
an empty string, which the service read as "do not follow updates".

Drop the setting and express what the feature actually needs. WithoutAPIKeyUpdates
replaces WithAPIKeyUpdateSetting: a client given its own static key must not
have it replaced when the process-wide api_key changes. Which setting the
watcher follows is no longer configurable, so the callback goes back to its
original comparison, and the unrelated trimming of updated keys goes with it.

Also drops a TrimSpace in WithStatusInstance that could never fire, since both
callers pass an already-trimmed value.

Assisted-by: Claude:claude-opus-5
The Cluster Agent kept its own "remote-config.db" literal to seed the cache
filename collision map, duplicating the service package's default.

The name is not configurable: there is no setting for it, and the default
client never calls WithDatabaseFileName, so the compile-time default is the
whole story. Export it and have both sides use it.

That map is what stops an additional client from claiming the default client's
cache, so the duplication was a latent bug: changing the service default would
have left the Cluster Agent seeding the map with a stale name, and an extra
client could then quietly share the default cache.

Assisted-by: Claude:claude-opus-5
@clamoriniere clamoriniere added the qa/done QA done before merge and regressions are covered by tests label Aug 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bits AI changelog/no-changelog No changelog entry needed internal Identify a non-fork PR long review PR is complex, plan time to review it qa/done QA done before merge and regressions are covered by tests team/container-platform The Container Platform Team team/fleet-automation team/remote-config

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants