Skip to content

feat(opampextension): apply pushed RemoteConfig via disk-write + restart (#381) - #383

Merged
zzylol merged 1 commit into
mainfrom
b1-opamp-apply-remote-config
May 17, 2026
Merged

zzylol merged 1 commit into
mainfrom
b1-opamp-apply-remote-config

Conversation

@zzylol

@zzylol zzylol commented May 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #381 — the residual half of PR #350 where the agent connects to OpAMP but ignores incoming AgentRemoteConfig messages.

The patch adds a new RemoteConfigPath string (mapstructure:"remote_config_path") field to the opampextension Config. When empty, the extension stays "report-only" (upstream behavior preserved). When set, onMessage now dispatches msg.RemoteConfig to a new processRemoteConfig method, and Start advertises the AcceptsRemoteConfig capability — without this the OpAMP client library at client/internal/receivedprocessor.go:83 silently drops incoming RemoteConfig.

processRemoteConfig picks the first non-empty ConfigFile body (preferring "" / "asap-otel" keys), YAML-validates it, writes directly to disk (NOT write-tmp+rename — Linux returns EBUSY on renameat2 for Docker bind-mounted files), reports RemoteConfigStatuses_APPLIED, sleeps 200ms to flush the status, and os.Exit(0). Docker restart=unless-stopped / systemd Restart=always brings the collector back up loading the freshly-written YAML. The builder-config.yaml change adds path: ./extension/opampextension under the existing gomod: line so OCB resolves opampextension from the patch overlay — mirrors the pattern every other patched processor uses.

Test plan

  • bash build_asap_otel.sh --skip-patches builds clean
  • Patched binary contains "Applied OpAMP RemoteConfig" string
  • docker build -f deploy/docker/Dockerfile.asap-otel produces a valid image
  • End-to-end smoke test at /mydata/mvp-smoke-test/run_smoke.sh: restartCount goes from 0 → 1+, host agent.yaml is overwritten with the controller's emitted runtime YAML (size changes from 5345 → 4053 bytes)

Companion PR

ASAPQuery-backend b1-5-controller-emit-literal-env-vars resolves env vars literally in the controller's gorillas3 emit so the agent doesn't crash-loop on ${VAR:-default} after applying.

🤖 Generated with Claude Code

…art (ASAPCollector#381)

Patches the upstream opampextension's `onMessage` callback to handle
`msg.RemoteConfig` — closes the residual half of PR #350.

Background. The agent connects to the controller's OpAMP server,
receives `AgentRemoteConfig` messages, but the upstream extension's
onMessage handler only processes `AgentIdentification` and
`CustomMessage`. RemoteConfig is silently dropped. Result: the
controller's elaborate typed-stage-split runtime YAML (routing
connector + 5 sketch processors + gorillas3) is generated, pushed,
and ignored — every MVP demo since runs the agent's static
bootstrap YAML, the central blocker tracked in this repo's #381.

Patch (overlay at `opentelemetry-collector-contrib-patch/extension/opampextension/`):

  * `config.go` — new `RemoteConfigPath string` field (mapstructure
    key `remote_config_path`). When empty, the extension stays
    "report-only" (upstream behavior preserved). When set, the
    extension applies pushed configs by writing to that path.
  * `opamp_agent.go`:
      - `onMessage` now dispatches `msg.RemoteConfig` to a new
        `processRemoteConfig` method when both `msg.RemoteConfig`
        and `cfg.RemoteConfigPath` are non-nil/non-empty.
      - `Start` advertises `AcceptsRemoteConfig` capability when
        `RemoteConfigPath` is set. The OpAMP client library at
        `client/internal/receivedprocessor.go:83` silently drops
        `RemoteConfig` without this capability — that was the
        first dead-end on debug.
      - `processRemoteConfig` picks the first non-empty
        ConfigFile body (preferring `""` / `"asap-otel"` keys),
        YAML-validates it, writes directly to disk (NOT
        write-tmp+rename — Linux returns EBUSY on `renameat2`
        for Docker bind-mounted files), reports
        `RemoteConfigStatuses_APPLIED`, sleeps 200ms to flush
        the status, and `os.Exit(0)`. Docker `restart=unless-stopped`
        / systemd `Restart=always` is expected to bring the
        collector back up loading the freshly-written YAML.

Non-code files (`auth.go`, `factory.go`, `logger.go`, etc.) are
verbatim copies of the upstream extension. OCB's per-extension
module replace requires the WHOLE module directory under the
replace path; we can't just override two files.

builder-config.yaml: adds `path: ./extension/opampextension`
beneath the existing `gomod:` line so OCB redirects to the patch
overlay. Mirrors the pattern every other patched processor uses
(ddsketchprocessor, kllprocessor, …).

End-to-end verification (single-node MVP smoke test under
/mydata/mvp-smoke-test/):

  * Before patch: agent stays on bootstrap YAML; `restartCount=0`;
    backend sees sketches keyed by full wire-attr fingerprint.
  * After patch: agent applies pushed config and exits;
    `restartCount=1+`; host-side mounted `agent.yaml` is
    overwritten with the controller-emitted typed-stage-split
    runtime YAML.

The agent's post-restart behavior surfaces downstream issues
(gorillas3 S3 bucket not provisioned in smoke setup, memory
limiter trips on the 5-sketch-pipeline workload) — those are
separate blockers, not in scope for this PR. The OpAMP-apply
half of #381 is what this PR closes.

Companion: ASAPQuery-backend `b1-5-controller-emit-literal-env-vars`
branch — switches the controller's gorillas3 YAML emit from
bash-style `${VAR:-default}` (rejected by OTel's confmap URI
parser) to literal values resolved from the controller's env at
emit time, and threads `remote_config_path: /etc/otel/config.yaml`
into every emitted opamp extension block so OpAMP-apply persists
across the restart loop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@zzylol
zzylol merged commit 739ce0a into main May 17, 2026
@zzylol
zzylol deleted the b1-opamp-apply-remote-config branch May 17, 2026 23:21
zzylol added a commit that referenced this pull request May 18, 2026
…locks (#391)

PR #383 (and #389 follow-up) added the `RemoteConfigPath` field to the
patched opampextension: when set, the extension applies pushed
AgentRemoteConfig by writing the YAML to disk and exiting, so a
restart-policy-enabled supervisor (Docker `restart: unless-stopped`,
systemd, k8s) brings the agent back up with the new config. The
mvp-smoke-test agent uses this field via its overlay-mounted
`agent.yaml`, but the 13 stock agent yamls in
`deploy/mvp-singlenode/configs/asap-otel-agent-*.yaml` still ship
without it, so OpAMP-apply silently no-ops in stock single-node deploys
(the controller's push is received, the extension reports back, but
nothing is persisted across the implied restart — the next boot reloads
the same static bootstrap yaml).

This change adds `remote_config_path: /etc/otel/config.yaml` (the same
path the collector boots from via `--config=/etc/otel/config.yaml`, and
the same convention the smoke-test uses) to the `extensions.opamp:`
block of every stock yaml that already declares one.

Files touched (13):
  - asap-otel-agent-allsketches.yaml
  - asap-otel-agent-b0-prometheus.yaml
  - asap-otel-agent-b0-victoriametrics.yaml
  - asap-otel-agent-b1-serf-prometheus.yaml
  - asap-otel-agent-b3-delta-direct.yaml
  - asap-otel-agent-b5-gorilla-prometheus.yaml
  - asap-otel-agent-b5-gorilla-victoriametrics.yaml
  - asap-otel-agent-b6-asap-single-sketch.yaml
  - asap-otel-agent-b6-gorilla-s3.yaml
  - asap-otel-agent-cms-direct.yaml
  - asap-otel-agent-cs-direct.yaml
  - asap-otel-agent-hll-direct.yaml
  - asap-otel-agent-kll-direct.yaml

The 6 stock yamls WITHOUT an `extensions.opamp:` block
(`b0a-raw-stream`, `b0b-raw-batched`, `b2-full`, `b3-delta`,
`b4-tunable`, `kafka-fragment`) are intentionally left alone — adding
`remote_config_path` would require also wiring up `extensions:` +
`service.extensions:`, which is out of scope for this sweep.

OPERATIONAL CAVEAT (flagged inline + in each yaml's comment): the
stock `agents-N{1,10,100}.yml` compose overlays mount
`/etc/otel/config.yaml` read-only (`:ro` suffix on the bind mount) and
do NOT set a restart policy on the agent service. As-shipped, the
extension's disk-write will fail with EROFS and the agent process will
exit without coming back. To actually exercise OpAMP-apply persistence
in a stock deploy, the operator must additionally:

  1. Drop `:ro` from the agent's config volume mount (so the
     opampextension can write back to the same path the collector
     boots from), and
  2. Add `restart: unless-stopped` (or equivalent) to the agent
     service so the container respawns after the extension exits.

The smoke-test's `mvp-smoke-test/compose/smoke-overlay.yml` does both
via `volumes: !override` + `restart: unless-stopped` — that is the
reference pattern. The compose-side wiring update is deferred to a
follow-up so this PR stays narrowly scoped to the agent yaml schema.

YAML validity verified with `python3 -c "import yaml; ..."` over all
19 stock agent yamls; the 13 modified files parse with
`extensions.opamp.remote_config_path == "/etc/otel/config.yaml"`.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MVP demo: agent doesn't apply OpAMP-pushed runtime config (PR #350 residual)

1 participant