-
Notifications
You must be signed in to change notification settings - Fork 0
Live-test plugin against cpln-customer-demos and fix three confirmed … #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,7 @@ | |
| "autoscaling" | ||
| ], | ||
| "skills": "./skills/", | ||
| "rules": "./rules/", | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can remove the rules call. I was hoping it would be called from the initial load as an additional reference but if it's being ignored, then we can remove it. |
||
| "mcpServers": "./.mcp.json", | ||
| "apps": "./.app.json", | ||
| "interface": { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,20 @@ The plugin auto-configures the Control Plane MCP Server. Your `CPLN_TOKEN` (prom | |
|
|
||
| **Never write a cpln command from memory.** Before constructing a command, consult `rules/cli-conventions.md` (command structure, shared flags, resource command map, hallucination traps) and `skills/cpln/SKILL.md` (setup, workflows, examples). Verify exact flag names with `cpln <command> --help` or the MCP suggest tool (`mcp__cpln__cpln_suggest`). | ||
|
|
||
| ## CLI Guardrails | ||
|
|
||
| These commands do not exist — never generate them: | ||
|
|
||
| - `cpln secret create` → use type-specific: `cpln secret create-opaque`, `create-aws`, `create-tls`, etc. | ||
| - `cpln apply` without `--file` → always: `cpln apply --file manifest.yaml` | ||
| - `cpln <resource> list` → use `cpln <resource> get` (no args = list all) | ||
|
|
||
| These are too destructive to run without explicit user confirmation in the conversation: | ||
|
|
||
| - `cpln gvc delete-all-workloads` — destroys every workload in the GVC | ||
| - `cpln volumeset shrink` — permanent data loss on the old volume | ||
| - Any `cpln <resource> delete` — surface the org, GVC, resource name, and blast radius before proceeding | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Turns out
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Quick correction, |
||
| ## Key Conventions | ||
|
|
||
| - CLI commands use `cpln` prefix (e.g., `cpln apply --file manifest.yaml`) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -146,6 +146,16 @@ This approach requires manual work to parameterize the converted output, but giv | |
|
|
||
| ## Docker Compose Migration (`cpln stack`) | ||
|
|
||
| > **Firewall default mismatch — read before writing native manifests.** | ||
| > `cpln stack` defaults external outbound to **open** for all services that expose ports. Native Control Plane workload manifests default external outbound to **blocked**. If you are writing CPLN manifests by hand (rather than using `cpln stack` directly), you must add explicit outbound rules for every external API, database, or service your workload calls — otherwise it silently cannot reach anything outside the platform. This is the most common failure mode for manual Docker Compose migrations. | ||
| > | ||
| > ```yaml | ||
| > firewallConfig: | ||
| > external: | ||
| > outboundAllowCIDR: | ||
| > - 0.0.0.0/0 # or restrict to specific CIDRs/hostnames | ||
| > ``` | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Small fix: the note says Suggested rewrite:
Everything else looks good to me. |
||
| ### Key Differences | ||
|
|
||
| 1. **Service URLs must be rewritten**: `http://service-name:port` → `http://workload-name.GVC_NAME.cpln.local:port` | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -126,7 +126,7 @@ Use `cpln://secret/NAME` to reference the full secret, or `cpln://secret/NAME.KE | |
|
|
||
| | Secret Type | Available Keys | Example | | ||
| |:---|:---|:---| | ||
| | opaque | `payload` (decoded if base64 runtime decode enabled), or omit key for raw JSON with `payload` + `encoding` | `cpln://secret/my-api-key.payload` | | ||
| | opaque | `payload` — **see encoding warning below** | `cpln://secret/my-api-key.payload` | | ||
| | dictionary | user-defined keys | `cpln://secret/db-config.DB_HOST` | | ||
| | userpass | `username`, `password` | `cpln://secret/creds.password` | | ||
| | tls | `key`, `cert`, `chain` | `cpln://secret/my-tls.cert` | | ||
|
|
@@ -137,6 +137,8 @@ Use `cpln://secret/NAME` to reference the full secret, or `cpln://secret/NAME.KE | |
| | nats-account | `accountId`, `privateKey` | `cpln://secret/my-nats.accountId` | | ||
| | any type | omit key for full secret as JSON | `cpln://secret/my-secret` | | ||
|
|
||
| **Opaque `.payload` encoding warning:** If the secret was created with base64 encoding (common when storing binary content — certs, keys, binary tokens — via the console or API), the `.payload` reference returns the base64-encoded string, not the decoded value. The workload receives it as a base64 string and typically fails with a cryptographic or parse error. To get the decoded value at runtime, the secret must have runtime decoding enabled (`encoding: base64` + runtime decode on the secret spec), or use the full secret reference (`cpln://secret/NAME`) and decode in application code. For plaintext secrets (API keys, connection strings, passwords), `.payload` works as expected. **Before injecting an opaque secret as `.payload`, ask the user: was this secret created with base64 encoding?** | ||
|
Comment on lines
130
to
+140
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This warning is inaccurate. Opaque Suggested replacement: Just so you know, with encoding: 'base64':
Important: |
||
|
|
||
| **As volume mount:** Export the workload, add a volume, and apply: | ||
|
|
||
| ```bash | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -118,6 +118,7 @@ Probe types: exactly one of `exec`, `grpc`, `tcpSocket`, `httpGet` (xor constrai | |
|
|
||
| | Error | Fix | | ||
| |:---|:---| | ||
| | `spec.containers[N].resources` present | Remove it — Control Plane does not use Kubernetes-style `resources.requests/limits`. Set `cpu` and `memory` directly on the container object: `cpu: 50m`, `memory: 128Mi`. This returns a 400 with `"resources" is not allowed`. | | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good, let's also mention here (and on that other similar note we have in another file) that |
||
| | Memory-to-CPU ratio exceeded | 1024Mi memory needs at least 128m CPU (ratio 8:1) | | ||
| | GPU with Capacity AI | Disable Capacity AI when using GPU | | ||
| | Concurrency on standard/stateful | Use rps, cpu, memory, latency, or keda instead | | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -113,7 +113,7 @@ bindings: | |
| ``` | ||
|
|
||
| **Constraints:** | ||
| - Each binding's permissions must be **sorted alphabetically and unique** (validation rule). | ||
| - Each binding's permissions must be **unique**. The API auto-sorts them alphabetically — you don't need to sort manually. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Duplicate permissions in the same binding will cause a validation error" is wrong. The API silently de-duplicates them on write, duplicates don't trigger an error, they just get dropped. Ordering is also auto-normalized: the API sorts the array on write regardless of input order. Suggested replacement:
Or maybe we don't mention this at all, it is not that important info to point out. |
||
| - A policy can have up to **50 bindings**, each with up to **200 principal links**. | ||
| - The same principal can appear in multiple bindings (different permission sets). | ||
|
|
||
|
|
@@ -278,6 +278,7 @@ bindings: | |
| ## Gotchas | ||
|
|
||
| - **Policies fail silently when wrong.** A typo in `targetKind`, a missing principal link, or an invalid permission name produces a policy that exists but grants nothing. Always verify with `cpln policy access-report POLICY_NAME` after creation. | ||
| - **Permission ordering doesn't matter — the API auto-sorts.** You do not need to sort permissions alphabetically in your manifests; the platform sorts them on write. Duplicate permissions in the same binding will cause a validation error. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Related to the comment above. |
||
| - **Built-in policies cannot be modified or deleted.** Origins `builtin` are read-only; create your own with `default` origin. | ||
| - **`reveal` (not `read`) is the permission for accessing secret values.** This is the most common permission-name mistake. | ||
| - **Identity links are GVC-scoped.** Use `//gvc/GVC/identity/NAME`, not `//identity/NAME`. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -170,7 +170,19 @@ spec: | |
|
|
||
| ### Event-Driven KEDA (Standard, Redis Queue) | ||
|
|
||
| **Prerequisite:** KEDA must be enabled on the GVC before any workload can use `metric: keda`. Applying a workload with `metric: keda` to a GVC without KEDA enabled will silently not scale — no error event, the workload just ignores queue depth. | ||
|
|
||
| ```yaml | ||
| # Step 1: Enable KEDA on the GVC (one-time setup) | ||
| kind: gvc | ||
| name: my-gvc | ||
| spec: | ||
| keda: | ||
| enabled: true | ||
| ``` | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is correct. One small enhancement worth adding: most real KEDA triggers (SQS, Pub/Sub, Azure queues, etc.) need cloud credentials, so kind: gvc
name: my-gvc
spec:
keda:
enabled: true
identityLink: //identity/keda-id # required for cloud-resource triggers
secrets: # optional, for TriggerAuthentication
- //secret/queue-creds |
||
| ```yaml | ||
| # Step 2: Configure the workload | ||
| kind: workload | ||
| name: queue-processor | ||
| spec: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -319,7 +319,7 @@ Full `spec.rolloutOptions` configuration: | |
|
|
||
| ### Critical Warnings | ||
|
|
||
| - If `sleep` is not available in **any** container, ALL containers receive SIGKILL immediately | ||
| - If `sleep` is not available in **any** container, ALL containers receive SIGKILL immediately — the entire grace period is skipped. This silently affects distroless images, scratch-based images, and some minimal Alpine builds. Verify with `cpln workload exec WORKLOAD --gvc GVC -- which sleep` before relying on the grace period. If `sleep` is absent, either add it to the image or configure an explicit preStop hook that does not depend on it. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Going back over this, I think part of what I originally wrote is also off and the additions stacked on top of it, sorry about that. The "ALL containers get SIGKILL immediately, the entire grace period is skipped" framing isn't accurate: if the preStop hook fails because That said, I don't think this is actually worth mentioning in the AI plugin. It's a narrow edge case (distroless/scratch on K8s < 1.33), the failure mode is subtle rather than catastrophic, let's remove completely. |
||
| - If a custom preStop hook throws an error in **any** container, ALL containers receive SIGKILL immediately | ||
|
|
||
| ### Custom PreStop Hook | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the official reference for the
plugin.json, it is documented thathooks/hooks.jsonis auto-loaded from the default location which is alsohooks/hooks.json. Are you sure the hooks were ignored when you installed and used the plugin?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I tested with Sonnet, it wasn't loading the hooks and was attempting to call the CLI directly which caused it to ignore the explicit hooks call. I added this to the plugin.json, reloaded the plugin and ran it again. It leveraged the new hooks after that.