Skip to content

Commit 663f89b

Browse files
committed
Merge origin/main into proposal/0004-send-message-ruby
2 parents e232b38 + 90d638a commit 663f89b

38 files changed

Lines changed: 4660 additions & 32 deletions
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# 0004 Python Checklist
2+
3+
## Baseline
4+
5+
- [x] Plugin work lives under `extended/plugins/send-message-python/`.
6+
- [x] The subtree stands alone.
7+
- [x] No committed Slack credential appears anywhere.
8+
- [x] The plugin owns Kubernetes reads and Slack calls.
9+
10+
## Phase 0: pin the contract
11+
12+
- [x] Re-read `proposal.md`.
13+
- [x] Re-read `spec.md`.
14+
- [x] Re-read the `send-message` slice in proposal `0002`.
15+
- [x] Keep scope to Slack only.
16+
17+
## Phase 1: create the tiny repo
18+
19+
- [x] Create `extended/plugins/send-message-python/`.
20+
- [x] Add runtime source.
21+
- [x] Add image build.
22+
- [x] Add `plugin.yaml`.
23+
- [x] Add CRD manifests.
24+
- [x] Add RBAC manifests.
25+
- [x] Add smoke assets.
26+
27+
## Phase 2: implement the runtime
28+
29+
- [x] Implement `POST /api/v1/step.execute`.
30+
- [x] Enforce bearer auth from `/var/run/kargo/token`.
31+
- [x] Read `MessageChannel`.
32+
- [x] Read `ClusterMessageChannel`.
33+
- [x] Read referenced `Secret`.
34+
- [x] Send plaintext Slack payloads.
35+
- [x] Send encoded Slack payloads.
36+
- [x] Return `slack.threadTS`.
37+
38+
## Phase 3: test the contract
39+
40+
- [x] Add auth tests.
41+
- [x] Add channel lookup tests.
42+
- [x] Add Secret lookup tests.
43+
- [x] Add plaintext payload tests.
44+
- [x] Add encoded payload tests.
45+
- [x] Add XML decode tests.
46+
- [x] Add Slack failure tests.
47+
48+
## Phase 4: smoke
49+
50+
- [x] Add plugin-owned `smoke/smoke_test.py`.
51+
- [x] Keep smoke orchestration in Python, not shell.
52+
- [x] Build the image.
53+
- [x] Load it into kind.
54+
- [x] Install CRDs and RBAC.
55+
- [x] Install StepPlugin `ConfigMap`.
56+
- [x] Create local-only test Secret.
57+
- [x] Create test `MessageChannel`.
58+
- [x] Run a `Stage` with `uses: send-message`.
59+
- [x] Assert `Succeeded`.
60+
- [x] Assert non-empty `slack.threadTS`.
61+
62+
## Phase 5: mandatory radical simplification pass 1
63+
64+
- [x] Ask "can I make this look easier by deleting a dependency?"
65+
- [x] Ask "can I merge files without making the contract harder to read?"
66+
- [x] Ask "am I using a framework just because it is familiar?"
67+
- [x] Delete anything that fails those checks.
68+
69+
## Phase 6: mandatory radical simplification pass 2
70+
71+
- [x] Re-run tests and smoke from a green tree.
72+
- [x] Ask "can I make this radically simpler?"
73+
- [x] Remove abstractions that only serve style.
74+
- [x] Remove helpers that only save a few lines.
75+
- [x] Stop only when the repo still reads like a small third-party plugin.
76+
77+
Refactor pass notes:
78+
- Split the runtime into small package files for:
79+
- app flow
80+
- HTTP entrypoint
81+
- Kubernetes and Slack clients
82+
- payload decoding and shaping
83+
- Split smoke support out of the entrypoint into `smoke/lib.py`.
84+
- Re-ran unit tests after refactor.
85+
- Re-ran `py_compile` across all Python files after refactor.
86+
- Re-ran isolated kind smoke through `extended/tests/e2e_stepplugins.sh`.
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# 0004 Implementation Checklist
2+
3+
Update this as implementation teaches us things.
4+
5+
## Baseline
6+
7+
- [x] Plugin work lives under `extended/plugins/send-message`.
8+
- [x] The plugin subtree does not rely on repo resources outside itself.
9+
- [x] Slack credentials stay local-only and out of git.
10+
- [x] The host does not own channel lookup, Secret reads, or Slack API calls.
11+
12+
## Phase 0: read and pin the seams
13+
14+
- [x] Read proposal 0004 again before starting code.
15+
- [x] Re-read the `send-message` section in proposal 0002 spec.
16+
- [x] Read the current StepPlugin host runtime and e2e harness seams.
17+
- [x] Record any host-side gap found during that read.
18+
19+
## Phase 1: create the standalone subtree
20+
21+
- [x] Create `extended/plugins/send-message/`.
22+
- [x] Add plugin-owned runtime source there.
23+
- [x] Add plugin-owned image build there.
24+
- [x] Add plugin-owned tests there.
25+
- [x] Add plugin-owned CRD manifests there.
26+
- [x] Add plugin-owned RBAC manifests there.
27+
- [x] Add plugin-owned smoke assets or smoke helper there.
28+
29+
## Phase 2: implement the runtime
30+
31+
- [x] Implement `POST /api/v1/step.execute`.
32+
- [x] Enforce bearer-token auth from `/var/run/kargo/token`.
33+
- [x] Return `403` on bad auth.
34+
- [x] Reject unsupported methods cleanly.
35+
- [x] Parse the v1 step-config subset.
36+
- [x] Resolve `MessageChannel` from the Project namespace.
37+
- [x] Resolve `ClusterMessageChannel` from the system-resources namespace.
38+
- [x] Resolve referenced Secrets from the correct namespace.
39+
- [x] Read Slack token from Secret key `apiKey`.
40+
- [x] Send the Slack message from the plugin.
41+
- [x] Return `slack.threadTS` output.
42+
43+
## Phase 3: own the OSS CRDs and RBAC
44+
45+
- [x] Add Slack-only `MessageChannel` CRD manifest.
46+
- [x] Add Slack-only `ClusterMessageChannel` CRD manifest.
47+
- [x] Keep the API group `ee.kargo.akuity.io/v1alpha1`.
48+
- [x] Add plugin-owned RBAC manifests for channel and Secret reads.
49+
- [x] Keep RBAC setup out of host code unless a real host gap is proven.
50+
51+
## Phase 4: tests inside the subtree
52+
53+
- [x] Add runtime tests for auth.
54+
- [x] Add runtime tests for namespaced channel lookup.
55+
- [x] Add runtime tests for cluster-scoped channel lookup.
56+
- [x] Add runtime tests for referenced Secret lookup.
57+
- [x] Add runtime tests for Slack request shaping.
58+
- [x] Add runtime tests for `slack.channelID` override.
59+
- [x] Add runtime tests for `slack.threadTS` override and output.
60+
- [x] Add runtime tests for missing channel or Secret failures.
61+
- [x] Add runtime tests for Slack API failure handling.
62+
63+
## Phase 5: local smoke path
64+
65+
- [x] Build the plugin image from `extended/plugins/send-message`.
66+
- [x] Load the image into the local kind cluster.
67+
- [x] Keep kube access on an isolated `KUBECONFIG`.
68+
- [x] Keep the user's existing kube context untouched.
69+
- [x] Install plugin CRDs and RBAC.
70+
- [x] Generate and install the StepPlugin `ConfigMap`.
71+
- [x] Inject a local-only Slack token into a cluster `Secret`.
72+
- [x] Create a test `MessageChannel` or `ClusterMessageChannel`.
73+
- [x] Run a `Stage` with `uses: send-message`.
74+
- [x] Prove promotion success in-cluster.
75+
- [x] Prove `slack.threadTS` output is populated.
76+
- [x] Verify the message appeared in the target Slack channel.
77+
78+
## Phase 6: repo harness integration
79+
80+
- [x] Extend `extended/tests/e2e_stepplugins.sh` to support the real
81+
`send-message` smoke path.
82+
- [x] Keep the committed harness credential-free.
83+
- [x] Gate Slack smoke on a local env var for the token.
84+
- [x] Keep any non-`extended/` edit to a tiny hook only, if one is needed.
85+
86+
## Phase Post-Green: Minimize Diff Of Files Outside ./extended Against Kargo Upstream
87+
88+
- [x] Fetch `upstream`.
89+
- [x] Review every edited file outside `extended/`, if any, against
90+
`upstream/main`.
91+
- [x] Move more logic behind `extended/` helpers if that shrinks the outside
92+
diff safely.
93+
- [x] Re-run matching tests after each cleanup pass.
94+
- [x] Stop only when no obvious outside-`extended/` shrink remains.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# 0004 Implementation Notes
2+
3+
- Plugin subtree:
4+
- `extended/plugins/send-message/`
5+
- standalone `go.mod`
6+
- standalone `Dockerfile`
7+
- standalone `plugin.yaml`
8+
- standalone CRDs and RBAC under `manifests/`
9+
- standalone smoke helper under `smoke/`
10+
11+
- Runtime contract implemented in the plugin:
12+
- `POST /api/v1/step.execute`
13+
- bearer auth from `/var/run/kargo/token`
14+
- direct Kubernetes API reads from projected service-account credentials
15+
- direct Slack API call from the plugin
16+
- encoded `json`, `yaml`, and `xml` payload support
17+
18+
- V1 scope shipped:
19+
- `send-message`
20+
- `MessageChannel`
21+
- `ClusterMessageChannel`
22+
- Slack only
23+
- no SMTP
24+
25+
- Secret and channel rules implemented:
26+
- `secretRef.name` resolves in the Project namespace for `MessageChannel`
27+
- `secretRef.name` resolves in the system-resources namespace for
28+
`ClusterMessageChannel`
29+
- Slack token key is `apiKey`
30+
- `spec.slack.channelID` is required in CRD schema
31+
32+
- Smoke harness knobs:
33+
- `STEPPLUGIN_SEND_MESSAGE_SMOKE=true`
34+
- `STEPPLUGIN_SEND_MESSAGE_CHANNEL_ID=<channel-id>`
35+
- `STEPPLUGIN_SEND_MESSAGE_SLACK_API_KEY=<token>`
36+
- Plugin-local smoke entrypoint:
37+
- `extended/plugins/send-message/smoke/smoke-test.sh`
38+
- repo harness calls that script rather than owning the full orchestration
39+
40+
- Host gap found during smoke:
41+
- the StepPlugin auth-token mount was not readable by non-root sidecars
42+
- fix was in `extended/pkg/stepplugin/agent/command_bridge.go`
43+
- auth directory mode is now `0755`
44+
- auth file mode is now `0444`
45+
46+
- Smoke-result note:
47+
- plugin response includes `output.slack.threadTS`
48+
- Promotion state observed in-cluster stored the value under
49+
`status.state.step-1.slack.threadTS`
50+
- the smoke harness accepts either that path or
51+
`status.stepExecutionMetadata[0].output.slack.threadTS`
52+
53+
- Encoded-message note:
54+
- when `encodingType` is set, `config.slack.channelID` and
55+
`config.slack.threadTS` are ignored
56+
- encoded body uses Slack-native field names such as `channel` and
57+
`thread_ts`
58+
- if encoded body omits `channel`, plugin fills it from channel resource
59+
`spec.slack.channelID`
60+
61+
- XML note:
62+
- no public exact upstream XML example was found
63+
- this implementation treats XML as a Kargo-owned alternate serialization of
64+
the same Slack payload object used for `json` and `yaml`
65+
- root name is ignored
66+
- repeated sibling names become arrays
67+
68+
- Non-plugin repo fix found during smoke:
69+
- `pkg/cli/cmd/promote/promote.go` assumed wrapped REST payloads
70+
- local smoke showed direct-object and direct-list payloads
71+
- decoder helpers now accept both shapes

0 commit comments

Comments
 (0)