-
Notifications
You must be signed in to change notification settings - Fork 0
fix: resolve CLI bugs found during GA validation #31
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
Open
shreemaan-abhishek
wants to merge
6
commits into
master
Choose a base branch
from
ga-readiness-validation-fixes
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
7876178
fix(ssl): require --cert/--key for flag-based ssl update
shreemaan-abhishek 5f62950
fix(plugin-metadata): emit proper yaml for get -o yaml
shreemaan-abhishek 874ef09
fix(plugin): honor --output format in plugin get
shreemaan-abhishek 440f67a
fix(stream-route): add missing --name field to create
shreemaan-abhishek 76c0920
fix(config): reject unsupported service_templates section
shreemaan-abhishek 1a6e0f7
docs: add GA readiness test plan and report
shreemaan-abhishek File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,213 @@ | ||
| # a7 GA Test Plan — Local API7 EE 3.9.12 | ||
|
|
||
| Execution plan for **Task #3 (real CLI smoke tests)** and **Task #4 (complete E2E coverage)** | ||
| from the GA Readiness Handoff. Run against a locally deployed API7 EE. | ||
|
|
||
| ## Scope | ||
|
|
||
| - **In scope**: management-plane validation of the `a7` CLI — CRUD round-trips, declarative | ||
| config, output formats, error handling, and confirmation that unsupported resources are | ||
| removed/blocked. | ||
| - **Out of scope**: real gateway data-plane traffic forwarding. Per the handoff, traffic | ||
| tests stay in the gateway repository. The separately-running gateway container is only | ||
| needed to keep the EE deployment healthy; we do **not** assert on proxied traffic here. | ||
|
|
||
| ## Local environment (assumed already running) | ||
|
|
||
| | Component | How it runs | Notes | | ||
| |---|---|---| | ||
| | API7 EE dashboard / control-plane + dp-manager + PostgreSQL | docker compose | the management API `a7` talks to | | ||
| | API7 gateway | standalone docker container | connected to the dp-manager; health only | | ||
|
|
||
| ### 0. Pre-flight — capture connection facts | ||
|
|
||
| Fill these in before doing anything else: | ||
|
|
||
| ``` | ||
| A7_ADMIN_URL = https://localhost:7443 # control-plane HTTPS port — confirm | ||
| A7_TOKEN = a7ee-<your-access-token> # access token from the dashboard UI / API | ||
| A7_GATEWAY_GROUP = default # resolved to a UUID at runtime | ||
| ``` | ||
|
|
||
| Confirm before proceeding: | ||
|
|
||
| 1. **Reachability** — `curl -k -H "X-API-KEY: $A7_TOKEN" $A7_ADMIN_URL/api/gateway_groups` | ||
| returns `200` with a non-empty `list`. | ||
| 2. **Version is 3.9.12** — check the dashboard image tag in `docker compose ps` / | ||
| `docker inspect`, or the dashboard `/api/version` endpoint. Record the exact version. | ||
| If it is **not** 3.9.12, note the deviation in the report — the handoff specifically | ||
| wants 3.9.12 validated. | ||
| 3. **Gateway is registered & healthy** — the standalone gateway container shows up under | ||
| the gateway group (dashboard UI or `GET /api/gateway_groups/{id}/instances`). This only | ||
| needs to be healthy; we are not testing traffic through it. | ||
|
|
||
| ## Build | ||
|
|
||
| ```bash | ||
| cd ~/Desktop/repos/xisipa/a7 | ||
| make build # -> ./bin/a7 | ||
| ./bin/a7 version # record the version string in the report | ||
| ``` | ||
|
|
||
| All manual steps below use `./bin/a7`. The automated suite (Phase A) builds its own binary. | ||
|
|
||
| --- | ||
|
|
||
| ## Phase A — Run the existing automated E2E suite | ||
|
|
||
| Establishes a known-good baseline against the local 3.9.12 instance before manual work. | ||
|
|
||
| ```bash | ||
| cd ~/Desktop/repos/xisipa/a7 | ||
| export A7_ADMIN_URL="https://localhost:7443" | ||
| export A7_TOKEN="a7ee.xxxxxxxx" | ||
| export A7_GATEWAY_GROUP="default" | ||
| # A7_GATEWAY_URL and HTTPBIN_URL intentionally left UNSET — data-plane tests are skipped. | ||
| make test-e2e | ||
| ``` | ||
|
|
||
| **Expected**: suite passes, or fails only in ways explained by known gaps. For every | ||
| failure, decide: real bug, missing/incorrect test, or environment issue. Record each one. | ||
|
|
||
| > The standard `test/e2e/docker-compose.yml` is **not** used here — you already have a | ||
| > local deployment. Only the env vars above are needed. | ||
|
|
||
| --- | ||
|
|
||
| ## Phase B — Manual CRUD smoke tests (per resource) | ||
|
|
||
| For **each** resource below, run the handoff's round-trip pattern with `./bin/a7`: | ||
|
|
||
| 1. `create` (both flag-based and `-f file` where supported) | ||
| 2. `get` — assert the returned config matches what was sent | ||
| 3. `list` — assert the new resource appears | ||
| 4. `update` — change a field | ||
| 5. `get` again — assert the update took effect | ||
| 6. `export` (where supported) — assert valid YAML/JSON | ||
| 7. `delete` (try with and without `--force`) | ||
| 8. `get` / `list` — assert it is gone | ||
|
|
||
| Also exercise, at least once per resource: `-o json`, `-o yaml`, and default table output; | ||
| plus one deliberate error case (bad ID, missing `--gateway-group`, malformed `-f` file) and | ||
| confirm the error message is clear and actionable. | ||
|
|
||
| **Use unique resource IDs** (`a7-ga-<resource>-<timestamp>`) and clean up everything, even | ||
| on a local instance — it keeps reruns deterministic. | ||
|
|
||
| ### Resource checklist | ||
|
|
||
| | Resource | Command | create/get/list/update/delete | export | Notes | | ||
| |---|---|:--:|:--:|---| | ||
| | context | `a7 context` | ☐ | — | create/use/list/current/delete; no gateway group needed | | ||
| | gateway-group | `a7 gateway-group` | ☐ | — | control-plane `/api/*` | | ||
| | service | `a7 service` | ☐ | ☐ | inline upstream — **node array format only** | | ||
| | route | `a7 route` | ☐ | ☐ | requires `--service-id`; route is service-centered | | ||
| | consumer | `a7 consumer` | ☐ | ☐ | | | ||
| | credential | `a7 credential` | ☐ | — | nested under a consumer | | ||
| | ssl | `a7 ssl` | ☐ | ☐ | **shared/global** — unique IDs, careful cleanup | | ||
| | secret | `a7 secret` | ☐ | — | **shared/global** — unique IDs, careful cleanup | | ||
| | global-rule | `a7 global-rule` | ☐ | ☐ | **shared/global** — affects whole gateway group | | ||
| | plugin | `a7 plugin` | ☐ (list/get only) | — | read-only: list plugins, get schema | | ||
| | plugin-metadata | `a7 plugin-metadata` | ☐ (no list) | — | **shared/global**; keyed by plugin name | | ||
| | proto | `a7 proto` | ☐ | ☐ | protobuf definitions | | ||
| | stream-route | `a7 stream-route` | ☐ | ☐ | **confirmed exposed** by the CP — must work | | ||
|
|
||
| > **Shared/global resources** (ssl, secret, global-rule, plugin-metadata) are visible | ||
| > gateway-group-wide. On a shared environment they collide between CI runs; even locally, | ||
| > assert you only touch IDs you created and never delete pre-existing resources. | ||
|
|
||
| ### Inline upstream format (API7 EE) | ||
|
|
||
| `nodes` must be an **array of objects**. The APISIX map form is rejected. | ||
|
|
||
| ```yaml | ||
| upstream: | ||
| type: roundrobin | ||
| nodes: | ||
| - host: 127.0.0.1 | ||
| port: 8080 | ||
| weight: 1 | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Phase C — Verify unsupported resources are blocked | ||
|
|
||
| Confirm the resources removed in PR #21 (and the one pending in Task #2) are gone or clearly | ||
| rejected — not present as broken commands. | ||
|
|
||
| | Resource | Expected behavior | | ||
| |---|---| | ||
| | `a7 upstream ...` | command does not exist (unknown command error) | | ||
| | `a7 consumer-group ...` | command does not exist | | ||
| | `a7 service-template ...` | command does not exist | | ||
| | `a7 plugin-config ...` | **after Task #2**: command does not exist | | ||
| | `a7 stream-route ...` | **works** — full CRUD (verified exposed by the control plane) | | ||
|
|
||
| Declarative config — these top-level sections must be **rejected with a clear error**: | ||
|
|
||
| ```yaml | ||
| upstreams: [...] # -> validation error | ||
| consumer_groups: [...] # -> validation error | ||
| service_templates: [...] # -> validation error | ||
| plugin_configs: [...] # -> validation error (after Task #2) | ||
| ``` | ||
|
|
||
| > Caveat: the control plane still accepts `plugin_configs` *inside batch config-validation | ||
| > payloads*. The rejection applies to `a7`'s standalone command and top-level declarative | ||
| > section, not to that internal validation path. | ||
|
|
||
| --- | ||
|
|
||
| ## Phase D — Declarative config | ||
|
|
||
| Run the full declarative workflow end to end: | ||
|
|
||
| 1. `a7 config dump` — export the live gateway-group config to a YAML file. Assert it is | ||
| valid YAML and contains only supported resource sections. | ||
| 2. `a7 config validate -f dump.yaml` — assert it passes for a known-good file, and fails | ||
| with a clear message for a file containing an unsupported section (see Phase C). | ||
| 3. `a7 config diff -f dump.yaml` — assert "no diff" against the just-dumped state; then | ||
| edit a field and assert the diff is reported correctly. | ||
| 4. `a7 config sync -f dump.yaml` — apply changes. Verify `--dry-run` first if available, | ||
| then a real sync, then `diff` again to confirm convergence. | ||
|
|
||
| Confirm all four work with **service-centered** API7 EE resources (no standalone upstreams). | ||
|
|
||
| --- | ||
|
|
||
| ## Bug-handling protocol | ||
|
|
||
| Per the handoff: **when a bug is found, add or update E2E coverage before fixing it.** | ||
|
|
||
| 1. Reproduce with the real binary; capture exact command, output, and HTTP status. | ||
| 2. Write or update a `test/e2e/<resource>_test.go` case that fails for the same reason. | ||
| 3. Fix the code. | ||
| 4. Re-run that test plus `make test-e2e` to confirm green. | ||
| 5. Do **not** change code purely to satisfy a test if it contradicts real API7 EE behavior. | ||
|
|
||
| --- | ||
|
|
||
| ## Reporting | ||
|
|
||
| Record results in a table and attach it to the GA tracking issue / PR: | ||
|
|
||
| | Phase | Resource / area | Result | Bug? | E2E test added | Notes | | ||
| |---|---|---|---|---|---| | ||
| | A | automated suite | | | | | | ||
| | B | service | | | | | | ||
| | B | route | | | | | | ||
| | ... | ... | | | | | | ||
| | C | unsupported resources | | | | | | ||
| | D | config dump/validate/diff/sync | | | | | | ||
|
|
||
| Also capture: exact API7 EE version tested, `a7 version` string, date, and any environment | ||
| deviations from "API7 EE 3.9.12". | ||
|
|
||
| ## Exit criteria | ||
|
|
||
| - Phase A suite green against the local instance. | ||
| - Phase B round-trip passes for every resource in the checklist. | ||
| - Phase C confirms all unsupported resources are absent/blocked and `stream-route` works. | ||
| - Phase D confirms declarative config works with service-centered resources. | ||
| - Every bug found has a corresponding E2E test and a fix (or a tracked follow-up). | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| # a7 GA Test Report | ||
|
|
||
| Execution of [`docs/ga-test-plan.md`](./ga-test-plan.md) against a locally deployed API7 EE. | ||
|
|
||
| ## Environment | ||
|
|
||
| | Item | Value | | ||
| |---|---| | ||
| | Date | 2026-05-14 | | ||
| | API7 EE version | **3.9.12** (image `api7/api7-ee-3-integrated:v3.9.12`; `/api/version` → `v3.9.12`) | | ||
| | `a7 version` | `7b67da5` (then `7b67da5-dirty` after the fixes in this report) | | ||
| | Admin URL | `https://localhost:7443` | | ||
| | Gateway group | `default` | | ||
| | Gateway instance | `api7-ee-gateway-1` — `status: Healthy`, `compatibility: Compatible` | | ||
| | Deviations | (1) `a7 plugin-config` command still present — Task #2 not yet done. (2) Data-plane traffic tests not run (out of scope per plan; `A7_GATEWAY_URL`/`HTTPBIN_URL` unset). | | ||
|
|
||
| ## Summary | ||
|
|
||
| All four phases executed. **5 real bugs found, all fixed with E2E + unit coverage.** Final automated suite: **128 passed / 10 skipped / 0 failed** (skips are the intentionally-unset data-plane and gateway-group-CRUD tests). | ||
|
|
||
| ## Results | ||
|
|
||
| | Phase | Resource / area | Result | Bug? | E2E test added | Notes | | ||
| |---|---|---|---|---|---| | ||
| | A | automated suite | PASS | — | — | 122→128 pass, 10 skip, 0 fail. Skips = data-plane traffic + gateway-group CRUD (intentionally disabled). | | ||
| | B | context | PASS | — | — | create/list/use/current/delete, json output, error case. | | ||
| | B | gateway-group | PASS | — | — | full CRUD; non-interactive `delete` needs `--force`. | | ||
| | B | service | PASS | — | — | create (file+flags), get, list, update, export. Map-form upstream nodes correctly rejected (400). | | ||
| | B | route | PASS | — | — | create (file+flags), get, list, update, export, delete. `get`/`delete` take id only; `update` uses `--uri`. | | ||
| | B | consumer | PASS | — | — | CRUD + export. MINOR: file `description:` key silently dropped (accepted key is `desc`). | | ||
| | B | credential | PASS | — | — | CRUD via server-returned id. MINOR: `create [id]` positional is treated as `name` (id is server-generated). | | ||
| | B | ssl | **FIXED** | **BUG-1** | `TestSSL_UpdateFlagsRequireCertAndKey` | flag-based `ssl update` silently lost partial updates. | | ||
| | B | secret | PASS | — | — | CRUD (file+flags); id format is `provider/id`. | | ||
| | B | global-rule | PASS | — | — | CRUD (file+flags), export; id must equal the plugin name. MINOR: flag `--id` is required but its value is ignored by EE. | | ||
| | B | plugin | **FIXED** | **BUG-3** | `TestPlugin_GetYAML` | `plugin get -o yaml` emitted JSON. list/get otherwise fine. | | ||
| | B | plugin-metadata | **FIXED** | **BUG-2** | `TestPluginMetadata_GetYAML` | `plugin-metadata get -o yaml` emitted a byte-array. CRUD otherwise fine. | | ||
| | B | proto | PASS | — | — | CRUD (file+flags), export. MINOR: `--desc` / `desc:` silently dropped. | | ||
| | B | stream-route | **FIXED** | **BUG-4** | `TestStreamRoute_CreateWithFlags`, `TestStreamRoute_CreateRequiresName` | `create` had no `--name` flag; EE requires `name`. CRUD via `-f` worked. | | ||
| | C | unsupported commands | PASS | — | — | `upstream` / `consumer-group` / `service-template` removed. `plugin-config` still present (Task #2 pending) — gives a clear runtime error. `stream-route` works. | | ||
| | C | declarative unsupported sections | **FIXED** | **BUG-5** | `TestConfigValidate_RejectsUnsupportedServiceTemplates` | `upstreams`/`consumer_groups` rejected; `service_templates` was silently accepted. `plugin_configs` rejection is expected-pending per plan. | | ||
| | D | config dump/validate/diff/sync | PASS | — | — | dump → valid YAML, supported sections only. validate/diff/sync round-trip converges; `--dry-run` applies nothing; post-sync diff clean; route update verified persisted. | | ||
|
|
||
| ## Bugs found & fixed | ||
|
|
||
| Per the bug-handling protocol, each bug was reproduced with the real binary, given failing E2E coverage, fixed, and re-verified. | ||
|
|
||
| ### BUG-1 — `ssl update` silently lost partial flag-based updates | ||
| `a7 ssl update <id> --sni new` (without `--cert`/`--key`) exited 0 and echoed the new SNI, but the server was never updated. Root cause: the command does a GET-merge-PUT, but API7 EE never returns `cert`/`key` on GET, so the merged PUT dropped the certificate material and the EE ignored it. | ||
| **Fix:** `pkg/cmd/ssl/update/update.go` now requires `--cert` and `--key` for flag-based updates (consistent with `ssl create`), with a clear error pointing to the `-f` path. `-f` updates with a full definition are unaffected. | ||
|
|
||
| ### BUG-2 — `plugin-metadata get -o yaml` emitted a byte-array | ||
| The YAML output was a list of integers. Root cause: `pkg/cmd/plugin-metadata/get/get.go` passed `json.RawMessage` (a `[]byte`) to the YAML encoder. | ||
| **Fix:** pass the decoded `map` to the exporter. | ||
|
|
||
| ### BUG-3 — `plugin get -o yaml` emitted JSON | ||
| Root cause: `pkg/cmd/plugin/get/get.go` only special-cased `-o json`; every other value fell through to a hardcoded JSON encoder. | ||
| **Fix:** honor any explicit `--output` value via the exporter. | ||
|
|
||
| ### BUG-4 — `stream-route create` had no `--name` flag | ||
| API7 EE 3.9.12 requires `name` on stream routes, but the create command exposed no `--name` flag, making flag-based creation impossible. | ||
| **Fix:** added a `Name` field to `api.StreamRoute`, a `--name` flag, and required-field validation on both the flag and `-f` paths. | ||
|
|
||
| ### BUG-5 — `config validate` silently accepted `service_templates` | ||
| A declarative file with a top-level `service_templates:` section validated as "Config is valid". Root cause: `api.ConfigFile` had no field for it, so the section was dropped on unmarshal. | ||
| **Fix:** added a `ServiceTemplates` field and an explicit rejection in both `config validate` and `configutil.ValidateSupportedSections` (used by `diff`/`sync`), mirroring `upstreams`/`consumer_groups`. | ||
|
|
||
| ## Minor observations (not fixed — low severity / by-design) | ||
|
|
||
| - **consumer**: `-f` file with a `description:` key is silently ignored; the accepted key is `desc`. | ||
| - **credential**: `create [id]` help text is misleading — the positional arg becomes the `name`; the `id` is server-generated. This is codified by `TestCredential_CreateWithPositionalID`, so it is current intended behavior. | ||
| - **global-rule**: flag-based `create` requires `--id`, but API7 EE forces the id to equal the single plugin key, so the `--id` value is effectively ignored. File-based create errors clearly on a mismatch. | ||
| - **proto**: `--desc` and a `desc:` file field are silently dropped. | ||
| - **stream-route / EE behavior**: API7 EE rejects stream routes bound to a service it has classified as HTTP. Binding to a `type: stream` service works reliably. `a7` surfaces the EE error cleanly; this is EE-side behavior, not an `a7` bug. | ||
| - **service**: the EE's schema-mismatch error (e.g. map-form upstream nodes) is correct and actionable but very verbose (dumps the full JSON schema). | ||
| - **tooling**: `golangci-lint run ./...` reports spurious `undefined: yaml (typecheck)` on files that correctly import `gopkg.in/yaml.v3` (including untouched files). `go build`, `go vet`, and `go test` are all clean — this is a pre-existing lint-environment issue, unrelated to these changes. | ||
|
|
||
| ## Exit criteria | ||
|
|
||
| | Criterion | Status | | ||
| |---|---| | ||
| | Phase A suite green against the local instance | ✅ 128 pass / 10 skip / 0 fail | | ||
| | Phase B round-trip passes for every resource | ✅ all 13 resources pass (ssl/plugin/plugin-metadata/stream-route after fixes) | | ||
| | Phase C confirms unsupported resources absent/blocked and `stream-route` works | ✅ — except `plugin-config` command still present (Task #2 pending, tracked) | | ||
| | Phase D declarative config works with service-centered resources | ✅ dump/validate/diff/sync all verified | | ||
| | Every bug has a corresponding E2E test and a fix | ✅ 5/5 fixed with E2E + unit coverage | | ||
|
|
||
| ## Follow-ups | ||
|
|
||
| - **Task #2** (remove the `plugin-config` standalone command) is still outstanding. Once done, Phase C also expects the declarative `plugin_configs` top-level section to be rejected — the `service_templates` rejection added here is the template for that change. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Add a language tag to the fenced block to satisfy markdown linting.
The block starting at Line 26 is missing a fence language (
MD040).Suggested fix
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 26-26: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents