Skip to content

feat(data): add optional notify block to the campaign schema - #1841

Open
martien-wdy wants to merge 1 commit into
split/7-tools-and-examplefrom
feat/campaign-notify-block
Open

feat(data): add optional notify block to the campaign schema#1841
martien-wdy wants to merge 1 commit into
split/7-tools-and-examplefrom
feat/campaign-notify-block

Conversation

@martien-wdy

Copy link
Copy Markdown
Contributor

Problem

Fleet operators want to be notified when a campaign episode is committed, but the campaign schema has no way for a plan author to declare that intent. Any notification mechanism must not add device-side network behavior: devices already upload the committed manifest, and the cloud is the right place to act.

Context

The device manifest does not carry the campaign plan wholesale; it carries only trigger.campaign_name and trigger.campaign_revision through EpisodeTrigger. The uploader (buildEpisodeManifest in go/internal/agent/services/data_transfer_worker.go) marshals the full device manifest into the cloud EpisodeManifest.attributes_json, so anything on the manifest reaches the cloud verbatim, but a new plan block does not ride along on its own. It needs one field of plumbing from the plan onto the trigger.

Solution

Campaign version 1 gains an optional notify: block:

notify:
  on: episode_committed
  • Schema and validation (go/internal/agent/data/campaign.go): CampaignNotify follows the same declaration pattern as the existing upload: and export: blocks. episode_committed is the only supported value for on:; anything else is rejected at deploy-time validation with the named sentinel error ErrUnsupportedNotifyOn, identifiable with errors.Is.
  • Unknown keys inside notify: warn at deployment instead of failing it. The rest of the document rejects unknown fields via the YAML decoder's known-fields check, but this block is read by the cloud rather than the device, so a newer cloud-side key must not brick deployment to an older device. A custom UnmarshalYAML collects the unknown keys and DeployCampaign appends a warning naming each one.
  • Manifest carriage (go/internal/agent/data/model.go, go/internal/agent/services/data_service.go): the block is copied verbatim from the plan onto EpisodeTrigger.Notify when a campaign triggers, and the trigger flows unchanged into the committed manifest. The cloud reads it at attributes_json -> trigger -> notify -> {"on": "episode_committed"}.
  • The notify block is device-inert: it only rides in the committed manifest attributes, and the cloud ingest service acts on it (a companion cloud pull request exists). The device never opens a network connection because of it.
  • The block feeds the campaign revision digest; plans without it keep their existing revisions (omitempty on a nil pointer).
  • Docs: docs/clients/wendy-cli/commands/data.md documents the block, and the annotated Examples/WendyDataCampaign/campaign.yaml shows it with a comment.

Tests

New unit tests in go/internal/agent/data/campaign_notify_test.go cover: the block round-tripping into the manifest JSON at trigger.notify.on (and staying absent when the plan has no notify block), rejection of unsupported on: values via errors.Is(err, ErrUnsupportedNotifyOn), the deploy-time warning naming unknown keys, durable storage with the plan, and the revision hash changing when notify is added.

Verified with CC=/usr/bin/clang go build ./go/..., go test ./go/internal/agent/data/... ./go/internal/agent/services/... (all passing), gofmt and go vet clean on the changed packages.

Campaign version 1 gains an optional notify block:

  notify:
    on: episode_committed

The block is device-inert. It is validated at deploy time (episode_committed
is the only supported event, rejected with ErrUnsupportedNotifyOn otherwise),
stored with the plan, hashed into the campaign revision, and copied verbatim
onto the episode trigger so it rides in the committed manifest JSON at
trigger.notify. The cloud ingest service, which receives that JSON as
attributes_json, decides whether to notify; the device never opens a network
connection because of the block.

Unknown keys inside notify warn at deployment instead of failing it, unlike
the rest of the document: the cloud side may understand keys an older agent
does not, and a newer plan must not brick deployment to an older device.
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.

1 participant