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
9 changes: 5 additions & 4 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,21 @@ stackctl is a Go CLI tool (Cobra) for managing Kubernetes stack deployments. It
| Group | Subcommands | File |
|-------|------------|------|
| `config` | set, get, list, use-context, current-context, delete-context | config.go |
| `login`/`logout`/`whoami` | top-level; supports password + OIDC loopback flow | login.go |
| `login`/`logout`/`whoami` | top-level; supports password + OIDC loopback flow; `logout --all` ends every session of the user | login.go |
| `auth` | register | login.go |
| `stack` | list, get, create, update, deploy, stop, clean, delete, status, logs, clone, extend, values, compare, history, rollback, history-values, watch, preview, pods, recent, follow, unfollow, action (list, run, log) | stack.go; preview/pods/recent: stack_inspect.go; follow/unfollow: stack_followers.go; action: stack_action.go; TTL and stop-time helpers: ttl.go |
| `template` | list, get, create, update, delete, clone, instantiate, quick-deploy, publish, unpublish, update-chart, add-chart, remove-chart, versions (list, get, diff) | template.go; add-chart/remove-chart: chart_changes.go |
| `definition` | list, get, create, update, delete, export, import, update-chart, add-chart, remove-chart, check-upgrade, upgrade, link-template | definition.go; add-chart/remove-chart/check-upgrade/upgrade/link-template: chart_changes.go |
| `override` | list, get, set, unset, delete, branch (list/set/delete), quota (get/set/delete) | override.go |
| `override` | list, get, set, unset, delete, branch (list/get/set/delete), quota (get/set/delete) | override.go |
| `bulk` | deploy, stop, clean, delete, template (publish, unpublish, delete) | bulk.go |
| `git` | providers, branches, validate | git.go |
| `cluster` | list, get, create, update, delete, set-default, test-connection, health, nodes, namespaces, utilization, quota (get/set/delete), shared-values (list/set/delete) | cluster.go |
| `cluster` | list, get, create, update, delete, set-default, test-connection, health, nodes, namespaces, utilization, quota (get/set/delete), shared-values (list/set/update/delete) | cluster.go |
| `analytics` | overview, templates, users | analytics.go |
| `apikey` | list, create, revoke | apikey.go |
| `audit` | log, list, export | audit.go |
| `cleanup-policy` | list, get, create, update, delete, run | cleanup_policy.go |
| `favorite` | list, add, remove | favorite.go |
| `favorite` | list, add, remove, check | favorite.go |
| `dashboard` | top-level | dashboard.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 |
Expand Down
13 changes: 9 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ cli/ # Go module root (github.com/omattsson/stackctl/cl
root.go # Root cobra command, global flags, config loading, flag > env > config (resolveAPIURL, resolveAPIKey)
config.go # config set/get/list/use-context/current-context/delete-context
version.go # Version info (build-time ldflags)
login.go # login, logout, whoami, auth register
login.go # login, logout (--all: logout-all first), whoami, auth register
token.go # Token helpers (save/load/delete session, fresh token for plugins)
stack.go # stack list/get/create/update/deploy/stop/clean/delete/status/logs/clone/extend/values/compare/history/rollback/history-values/watch
stack_inspect.go # stack preview/pods/recent (deploy-preview, live pods and events, recent)
Expand All @@ -36,15 +36,16 @@ cli/ # Go module root (github.com/omattsson/stackctl/cl
template.go # template list/get/create/update/clone/publish/unpublish/versions/instantiate/quick-deploy/delete
definition.go # definition list/get/create/update/delete/export/import/update-chart
chart_changes.go # definition add-chart/remove-chart/check-upgrade/upgrade/link-template, template add-chart/remove-chart
override.go # override list/get/set/unset/delete, branch overrides, quota overrides (quota set merges with GET, --replace)
override.go # override list/get/set/unset/delete, branch overrides (list/get/set/delete), quota overrides (quota set merges with GET, --replace)
bulk.go # bulk deploy/stop/clean/delete, bulk template publish/unpublish/delete (--ids flag or positional args)
git.go # git providers/branches/validate
cluster.go # cluster list/get/create/update/delete/set-default/test-connection/health/nodes/namespaces/utilization, quota get/set/delete, shared-values list/set/delete
cluster.go # cluster list/get/create/update/delete/set-default/test-connection/health/nodes/namespaces/utilization, quota get/set/delete, shared-values list/set/update/delete (update: GET list, merge, PUT the full record)
analytics.go # analytics overview/templates/users
apikey.go # apikey list/create/revoke
audit.go # audit log list/export
cleanup_policy.go # cleanup-policy list/get/create/update/delete/run
favorite.go # favorite list/add/remove
favorite.go # favorite list/add/remove/check
dashboard.go # dashboard (GET /api/v1/dashboard: failing, expiring, recent deployments, clusters)
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
Expand Down Expand Up @@ -187,6 +188,10 @@ 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
- `POST /api/v1/auth/logout-all` — `logout --all`: uses the session token of the context when a token file exists (also when an API key is set), else the API key; a 401 gets one forced `RenewSession` and one retry. On failure the command stops and keeps the token file; on success the normal logout follows.
- `GET /api/v1/dashboard` — `dashboard`: covers all users; `failing_instances` has status error only, `expiring_soon` the next hour. For the role `user`, `clusters` has ID and name only (no health): the table hides clusters without `health_status`.
- `GET /api/v1/favorites/check?entity_type=&entity_id=` — `favorite check` prints yes or no (exit 0).
- `PUT /api/v1/clusters/:id/shared-values/:valueId` replaces the full record (no GET for one entry): `shared-values update` reads the list, merges and sends name, description, values and priority. `--set` re-marshals the stored YAML (comments and key order are lost; a stderr note says so).
- `/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
Expand Down
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ Set `STACKCTL_USERNAME` and `STACKCTL_PASSWORD`, or use the flags `--username` a
STACKCTL_USERNAME=alice STACKCTL_PASSWORD=... stackctl login
stackctl whoami # show the current user
stackctl logout # end the session and remove the token file
stackctl logout --all # end every session of your user (all devices), for example after a lost laptop
```

`stackctl auth register` creates a user. Its flags are `--username`, `--display-name`, `--role`, `--service-account` and `--password-stdin`. Only an admin can set `--role` and `--service-account`.
Expand All @@ -192,7 +193,7 @@ The server rotates the refresh token on each renewal. stackctl holds a lock on `

An SSO login (`stackctl login --sso`) gets a token without a refresh token. By default, the server sets its life time to 24 hours (`JWT_EXPIRATION`). stackctl does not renew it. API keys have no session.

`stackctl logout` sends the access token and the refresh token to the server, which ends the session, and then removes the token file. If the server cannot be reached, stackctl prints a warning and still removes the file.
`stackctl logout` sends the access token and the refresh token to the server, which ends the session, and then removes the token file. If the server cannot be reached, stackctl prints a warning and still removes the file. `stackctl logout --all` first asks the server to revoke all refresh tokens of your user (the user of the login session, else of the API key); when that call fails, the command stops and keeps the token file, so you can try again.

### Precedence

Expand Down Expand Up @@ -443,6 +444,7 @@ stackctl override delete my-app my-chart

# Per-chart branch overrides
stackctl override branch set my-app my-chart feature/hotfix
stackctl override branch get my-app my-chart # exit code 1 when the chart has no branch override

# Quota overrides
stackctl override quota get my-app
Expand Down Expand Up @@ -609,6 +611,9 @@ stackctl cluster get 5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e
stackctl cluster shared-values list 5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e
stackctl cluster shared-values set 5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e --name "local-dev-defaults" --file values.yaml
stackctl cluster shared-values set 5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e --name "local-dev-defaults" --set persistence.storageClass=local-path --priority 10
# Update an entry (ID or name): the ID and the merge order stay; --set changes single keys, --file replaces the values
stackctl cluster shared-values update 5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e local-dev-defaults --set persistence.storageClass=fast
stackctl cluster shared-values update 5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e local-dev-defaults --priority 20
stackctl cluster shared-values delete 5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e 6e7f8a9b-0c1d-4e2f-8a3b-4c5d6e7f8a9b
```

Expand All @@ -626,12 +631,13 @@ These groups have no section above. Run `stackctl <group> --help` for the flags.
- `apikey`: `list`, `create`, `revoke`
- `analytics`: `overview`, `templates`, `users`
- `cleanup-policy`: `list`, `get`, `create`, `update`, `delete`, `run`
- `favorite` (aliases `favorites`, `fav`): `list`, `add`, `remove`
- `favorite` (aliases `favorites`, `fav`): `list`, `add`, `remove`, `check` (prints `yes` or `no`)
- `dashboard`: failing stacks (status error), stacks that expire within the next hour, recent deployments and (devops and admin) cluster health. It covers the stacks of all users. `-q` prints the IDs of the failing and expiring stacks
- `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`
- `cluster`: `list`, `get`, `create`, `update`, `delete`, `set-default`, `test-connection`, `health`, `nodes`, `namespaces`, `utilization`, `quota get|set|delete`, `shared-values list|set|update|delete`
- `git`: `providers`, `branches`, `validate`
- `template`: also `clone`, `unpublish`, `versions list|get|diff`
- `override`: also `list`, `unset`, `branch list|set|delete`, `quota get|set|delete`
- `override`: also `list`, `unset`, `branch list|get|set|delete`, `quota get|set|delete`
- `user`: `list`, `delete`, `disable`, `enable`, `reset-password`, `set-role`
- `orphaned`: `list`, `delete`
- `server`: `info` (server URL, ping, default TTL and TTL limit; no login needed)
Expand Down Expand Up @@ -723,7 +729,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 notification_channel.go chart_changes.go
analytics.go cleanup_policy.go favorite.go notification.go notification_channel.go chart_changes.go dashboard.go
testdata/ # golden files
pkg/
client/ # HTTP client, auth renewal, WebSocket
Expand Down
Loading
Loading