Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ stackctl is a Go CLI tool (Cobra) for managing Kubernetes stack deployments. It
| `audit` | log, list, export | audit.go |
| `cleanup-policy` | list, get, create, update, delete, run | cleanup_policy.go |
| `favorite` | list, add, remove | favorite.go |
| `notification` | list, count, read, read-all, prefs (get/set) | notification.go |
| `notification` | list, count, read, read-all, prefs (get/set), channel (list, get, create, update, delete, events, subscriptions, subscribe, test, logs) | notification.go; channel: notification_channel.go |
| `user` | list, delete, disable, enable, reset-password, set-role | user.go |
| `orphaned` | list, delete — namespaces with the stack-manager label but no DB record | orphaned.go |
| `completion` | bash, zsh, fish, powershell | completion.go |
Expand Down
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ cli/ # Go module root (github.com/omattsson/stackctl/cl
cleanup_policy.go # cleanup-policy list/get/create/update/delete/run
favorite.go # favorite list/add/remove
notification.go # notification list/count/read/read-all/prefs
notification_channel.go # notification channel list/get/create/update/delete/events/subscriptions/subscribe/test/logs (webhook URL masked unless --show-url)
orphaned.go # orphaned list/delete
user.go # user list/delete/disable/enable/reset-password/set-role
values.go # --set value parsing helpers (key paths, scalar types) for stack and override commands
Expand Down Expand Up @@ -186,6 +187,7 @@ All API calls go to `/api/v1/*`. Key route groups:
- `/api/v1/users` — list/delete/disable/enable/password (admin). `PUT /:id/role` `{"role"}` (k8s-stack-manager v0.8.0+, `user set-role <user> <role>`, user by ID or username via `resolveUserID`): response `id`, `old_role`, `new_role`, `changed`; 403 own role / caller not admin, 409 SSO user / last admin, 400 invalid role; a plain 404 (no route) maps to the v0.8.0 upgrade hint (`setRoleError`)
- `/ws` — the server closes a socket with code 1008 and reason `session revoked` or `token expired`. `Client.Watch` returns a `WatchStream`; `Err()` gives a `*WSClosedError` ("Connection closed by the server: <reason>. Log in again."). `stack watch` exits non-zero on it; for `token expired` it calls `Client.RenewSession` (forced renewal, false without refresh token) and reconnects, at most once per 30 s (`stackWatchMinReconnect`), then with `--id` GETs each pending instance (`watchCatchUp`) so a terminal status in the gap counts. Ctrl-C during renewal or dial exits 0. Another connection loss: `--id` with pending instances exits non-zero ("connection lost before all instances reached a terminal status"), else a stderr note and exit 0. `StreamDeploymentLogs` returns the same `WSClosedError`; `--follow` (`followLogsCtx`, op deploy/stop/clean/rollback, "" for `stack logs -f`) uses the same `renewAfterClose`; it reads the instance after the first connect (the server sets deploying/stopping/cleaning before the 202; not for `logs -f`) and after each reconnect (`StreamOptions.AfterSubscribe`). When it cannot follow to the end it says that the operation continues on the server and points to `stack watch --id` / `stack status` (never "run again"); errors name the operation ("rollback failed (status error): ..."). Terminal statuses live in `client.IsTerminalStatus` (running, stopped, draft, error, partial, failed) and `client.IsFailedStatus` (error, partial, failed → exit 1)
- `/api/v1/admin/orphaned-namespaces` — `orphaned list [--details]`; `DELETE /:ns?confirm=<ns>` is needed for a namespace without the label managed-by=k8s-stack-manager (409 without it; `orphaned delete --confirm`). `test/contract/path_contract_test.go` checks every `/api/v1` literal of `pkg/client` and `cmd` against the vendored swagger
- `/api/v1/admin/notification-channels` (devops or admin) — `notification channel ...`. The responses carry the full `webhook_url` (it can hold a token): every output mode masks it to scheme and host (`maskWebhookURL`) unless `--show-url`. The server never returns the secret; the CLI reads it from `--secret-env NAME` or `--secret-stdin` only. The URL can also come from `--url-env NAME`; error texts never contain the URL. `PUT /:id` changes only the fields sent (`secret: ""` removes the secret; `filters` replaces all filters, `{}` removes them). `PUT /:id/subscriptions` replaces all event types (400 for an unknown one). `POST /:id/test` answers 200 with `success`; the command exits 1 when it is false. Create and update return `warnings` (unknown filter IDs) → stderr.
- `/api/v1/notifications/preferences` — `notification prefs set` warns for an event type not in `notificationEventTypes` (`notifier.AllEventTypes()` of k8s-stack-manager); v0.8.0+ servers return 400
- `/api/v1/stack-definitions` — CRUD + export/import
- `POST /:id/charts` and `DELETE /:id/charts/:chartId` (devops or admin; `definition add-chart`, `remove-chart`). A linked definition cannot lose a chart that the template requires (409 "Cannot delete required chart: X").
Expand Down
33 changes: 31 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,35 @@ stackctl user set-role alice devops
- a change when you are no longer an enabled admin (403);
- removing the admin role from the last enabled admin (409).

### Notification Channels

Outbound webhooks for stack events. Needs the devops or admin role. The commands take a channel name or ID.

```bash
# The event types, then a channel for failed deploys of PR stacks, in Teams format
stackctl notification channel events
# --url-env reads the URL from an environment variable: it stays out of the shell history
stackctl notification channel create --name pr-alerts --url-env TEAMS_WORKFLOW_URL --format teams \
--filter-name 'pr-*' --event deployment.failed

# Sign the requests (X-StackManager-Signature): the secret comes from an environment variable or stdin
STACKCTL_CHANNEL_SECRET=... stackctl notification channel update pr-alerts --secret-env STACKCTL_CHANNEL_SECRET

stackctl notification channel list
stackctl notification channel subscribe pr-alerts --event deployment.failed --event deployment.success
stackctl notification channel test pr-alerts
stackctl notification channel logs pr-alerts --limit 50
stackctl notification channel update pr-alerts --disable
stackctl notification channel delete pr-alerts
```

- The webhook URL can hold a token (for example the signature of a Teams workflow URL). The commands show only the scheme and the host, also with `-o json` and `-o yaml`. `--show-url` shows the full URL. Give the URL with `--url-env NAME` to keep it out of the shell history and the process list.
- The secret is never a flag value: use `--secret-env NAME` or `--secret-stdin` (hidden input on a terminal). The server does not return it. `update --clear-secret` removes it.
- `create --event` checks the event types before the channel is created. When the subscription still fails, the channel exists: the error gives the `subscribe` command.
- `update` changes only the given flags. A `--filter-*` flag replaces all filters; `--clear-filters` removes them. `subscribe` replaces all subscriptions; `--none` removes them.
- `create` and `update` write the server warnings to stderr, for example a filter ID that does not exist.
- `test` exits with code 1 when the webhook does not accept the test event.

### Scripting Examples

```bash
Expand Down Expand Up @@ -598,7 +627,7 @@ These groups have no section above. Run `stackctl <group> --help` for the flags.
- `analytics`: `overview`, `templates`, `users`
- `cleanup-policy`: `list`, `get`, `create`, `update`, `delete`, `run`
- `favorite` (aliases `favorites`, `fav`): `list`, `add`, `remove`
- `notification` (alias `notifications`): `list`, `count`, `read`, `read-all`, `prefs get`, `prefs set`
- `notification` (alias `notifications`): `list`, `count`, `read`, `read-all`, `prefs get`, `prefs set`, `channel ...` (see Notification Channels)
- `cluster`: `list`, `get`, `create`, `update`, `delete`, `set-default`, `test-connection`, `health`, `nodes`, `namespaces`, `utilization`, `quota get|set|delete`, `shared-values list|set|delete`
- `git`: `providers`, `branches`, `validate`
- `template`: also `clone`, `unpublish`, `versions list|get|diff`
Expand Down Expand Up @@ -694,7 +723,7 @@ cli/
stack.go stack_inspect.go stack_followers.go stack_action.go ttl.go
template.go definition.go override.go bulk.go
cluster.go git.go orphaned.go user.go apikey.go audit.go
analytics.go cleanup_policy.go favorite.go notification.go
analytics.go cleanup_policy.go favorite.go notification.go notification_channel.go chart_changes.go
testdata/ # golden files
pkg/
client/ # HTTP client, auth renewal, WebSocket
Expand Down
Loading
Loading