diff --git a/.ai/specs/dcm-cli.spec.md b/.ai/specs/dcm-cli.spec.md index 4077992..02d6fd4 100644 --- a/.ai/specs/dcm-cli.spec.md +++ b/.ai/specs/dcm-cli.spec.md @@ -3,12 +3,13 @@ ## 1. Overview The DCM CLI (`dcm`) is a Go-based command-line tool for interacting with the -DCM (Data Center Management) control plane. It communicates exclusively through -the API Gateway (KrakenD on port 9080) to reach the Policy Manager, Catalog -Manager, and Service Provider Resource Manager backends. The CLI uses generated -clients from `policy-manager/pkg/client`, `catalog-manager/pkg/client`, and -`service-provider-manager/pkg/client` (oapi-codegen generated) as Go module -dependencies. +DCM (Data Center Management) control plane. It communicates directly with the +control-plane monolith on port 8080. The CLI uses generated clients from +[control-plane/pkg](https://github.com/dcm-project/control-plane/tree/main/pkg) +([policy/client](https://github.com/dcm-project/control-plane/tree/main/pkg/policy/client), +[catalog/client](https://github.com/dcm-project/control-plane/tree/main/pkg/catalog/client), +[sp/client](https://github.com/dcm-project/control-plane/tree/main/pkg/sp/client)) +(oapi-codegen generated) as a Go module dependency. **Version scope (v1alpha1):** @@ -28,20 +29,19 @@ dependencies. **Out of scope (v1alpha1):** -- Authentication and authorization (no auth in v1alpha1 API Gateway) +- Authentication and authorization (no auth in v1alpha1 control-plane API) - Interactive/wizard-style resource creation - Watch/streaming operations - Plugin/extension system - Offline mode or local caching - Bulk operations - Resource diff/dry-run -- Health check command for API Gateway connectivity +- Health check command for control-plane connectivity **Reference documents:** - [DCM CLI Specification](.ai/specs/dcm-cli.spec.md) -- Policy Manager OpenAPI: `api/v1alpha1/openapi.yaml` in dcm-policy-manager -- Catalog Manager OpenAPI: `api/v1alpha1/openapi.yaml` in dcm-catalog-manager +- Control-plane OpenAPI: `api/*/v1alpha1/openapi.yaml` in dcm-project/control-plane - [AEP Standards](https://aep.dev/) - API Enhancement Proposals - RFC 7807 - Problem Details for HTTP APIs - RFC 7396 - JSON Merge Patch @@ -51,20 +51,16 @@ dependencies. ## 2. Architecture ``` -┌─────────┐ ┌──────────────────┐ ┌──────────────────┐ -│ │ │ │ │ Policy Manager │ -│ dcm │─────────────▶│ API Gateway │──────▶│ (port 8080) │ -│ CLI │ HTTP / HTTPS │ (KrakenD 9080) │ └──────────────────┘ -│ │ │ │ ┌──────────────────┐ -└─────────┘ │ │──────▶│ Catalog Manager │ - │ │ │ (port 8080) │ - │ │ └──────────────────┘ - │ │ ┌──────────────────┐ - │ │──────▶│ SP Resource Mgr │ - └──────────────────┘ │ (port 8080) │ - └──────────────────┘ +┌─────────┐ ┌──────────────────────────────────────────┐ +│ │ │ control-plane monolith │ +│ dcm │─────────────▶│ (port 8080, /api/v1alpha1/*) │ +│ CLI │ HTTP / HTTPS │ policy · catalog · sp (in-process) │ +└─────────┘ └──────────────────────────────────────────┘ ``` +Policy evaluation and placement run in-process; there is no CLI-facing HTTP +route for `policies:evaluateRequest` or `/resources`. + ``` dcm-cli/ ├── cmd/dcm/main.go ← Entry point, root command setup @@ -127,7 +123,7 @@ Topics 1, 2, and 3 can be delivered in parallel. Topics 4-7 depend on all three foundation topics. Topic 8 depends only on Topic 1. > **Note:** Command tests use `net/http/httptest` to mock generated client HTTP -> calls. No real API Gateway is needed for unit tests. +> calls. No live control-plane is needed for unit tests. --- @@ -150,7 +146,7 @@ Out of scope: shell autocompletion, plugin system, interactive prompts. | REQ-CLI-020 | The CLI MUST define a root command `dcm` with global flags | MUST | | | REQ-CLI-030 | The root command MUST register all subcommand groups: `policy`, `catalog`, `sp`, `version`, `completion` | MUST | | | REQ-CLI-040 | The `catalog` command MUST register subcommand groups: `service-type`, `item`, `instance` | MUST | | -| REQ-CLI-050 | Global flags MUST include `--api-gateway-url`, `--output`/`-o`, `--timeout`, `--config`, `--tls-ca-cert`, `--tls-client-cert`, `--tls-client-key`, `--tls-skip-verify` | MUST | | +| REQ-CLI-050 | Global flags MUST include `--control-plane-url`, `--output`/`-o`, `--timeout`, `--config`, `--tls-ca-cert`, `--tls-client-cert`, `--tls-client-key`, `--tls-skip-verify` | MUST | | | REQ-CLI-060 | The CLI MUST exit with code 0 on success, 1 on runtime errors, 2 on usage errors | MUST | | | REQ-CLI-070 | The entry point (`cmd/dcm/main.go`) MUST bootstrap the root command and execute it | MUST | | @@ -161,7 +157,7 @@ Out of scope: shell autocompletion, plugin system, interactive prompts. - **Validates:** REQ-CLI-020, REQ-CLI-050 - **Given** the CLI is invoked - **When** `dcm --help` is run -- **Then** the global flags `--api-gateway-url`, `--output`/`-o`, `--timeout`, `--config`, `--tls-ca-cert`, `--tls-client-cert`, `--tls-client-key`, and `--tls-skip-verify` MUST be listed +- **Then** the global flags `--control-plane-url`, `--output`/`-o`, `--timeout`, `--config`, `--tls-ca-cert`, `--tls-client-cert`, `--tls-client-key`, and `--tls-skip-verify` MUST be listed ##### AC-CLI-030: Subcommand registration @@ -222,9 +218,9 @@ profile/context support. |----|-------------|----------|-------| | REQ-CFG-010 | The CLI MUST load configuration from the config file at `~/.dcm/config.yaml` by default | MUST | | | REQ-CFG-020 | The config file path MUST be overridable via `--config` flag or `DCM_CONFIG` environment variable | MUST | | -| REQ-CFG-030 | The CLI MUST support environment variables: `DCM_API_GATEWAY_URL`, `DCM_OUTPUT_FORMAT`, `DCM_TIMEOUT`, `DCM_CONFIG`, `DCM_TLS_CA_CERT`, `DCM_TLS_CLIENT_CERT`, `DCM_TLS_CLIENT_KEY`, `DCM_TLS_SKIP_VERIFY` | MUST | | +| REQ-CFG-030 | The CLI MUST support environment variables: `DCM_CONTROL_PLANE_URL`, `DCM_OUTPUT_FORMAT`, `DCM_TIMEOUT`, `DCM_CONFIG`, `DCM_TLS_CA_CERT`, `DCM_TLS_CLIENT_CERT`, `DCM_TLS_CLIENT_KEY`, `DCM_TLS_SKIP_VERIFY` | MUST | | | REQ-CFG-040 | Configuration precedence MUST be: CLI flags > environment variables > config file > built-in defaults | MUST | | -| REQ-CFG-050 | Built-in defaults MUST be: `api-gateway-url=http://localhost:9080`, `output-format=table`, `timeout=30`, `tls-ca-cert=""`, `tls-client-cert=""`, `tls-client-key=""`, `tls-skip-verify=false` | MUST | | +| REQ-CFG-050 | Built-in defaults MUST be: `control-plane-url=http://localhost:8080`, `output-format=table`, `timeout=30`, `tls-ca-cert=""`, `tls-client-cert=""`, `tls-client-key=""`, `tls-skip-verify=false` | MUST | | | REQ-CFG-060 | The CLI MUST use Viper for configuration management | MUST | | | REQ-CFG-070 | The CLI MUST NOT fail if the config file does not exist; defaults MUST be used | MUST | | @@ -232,7 +228,7 @@ profile/context support. | Config Key | Env Var | Flag | Default | Description | |------------|---------|------|---------|-------------| -| api-gateway-url | DCM_API_GATEWAY_URL | --api-gateway-url | http://localhost:9080 | API Gateway base URL | +| control-plane-url | DCM_CONTROL_PLANE_URL | --control-plane-url | http://localhost:8080 | Control plane base URL | | output-format | DCM_OUTPUT_FORMAT | --output / -o | table | Output format (table, json, yaml) | | timeout | DCM_TIMEOUT | --timeout | 30 | Request timeout in seconds | | - | DCM_CONFIG | --config | ~/.dcm/config.yaml | Config file path | @@ -246,9 +242,9 @@ profile/context support. ##### AC-CFG-010: Config file loading - **Validates:** REQ-CFG-010 -- **Given** a config file exists at `~/.dcm/config.yaml` with `api-gateway-url: http://custom:9080` +- **Given** a config file exists at `~/.dcm/config.yaml` with `control-plane-url: http://custom:8080` - **When** the CLI is invoked without flags or env vars -- **Then** the API Gateway URL MUST be `http://custom:9080` +- **Then** the control-plane URL MUST be `http://custom:8080` ##### AC-CFG-020: Custom config file path @@ -260,25 +256,25 @@ profile/context support. ##### AC-CFG-030: Environment variable override - **Validates:** REQ-CFG-030 -- **Given** `DCM_API_GATEWAY_URL=http://env:9080` is set -- **And** the config file has `api-gateway-url: http://file:9080` -- **When** the CLI is invoked without `--api-gateway-url` -- **Then** the API Gateway URL MUST be `http://env:9080` +- **Given** `DCM_CONTROL_PLANE_URL=http://env:8080` is set +- **And** the config file has `control-plane-url: http://file:8080` +- **When** the CLI is invoked without `--control-plane-url` +- **Then** the control-plane URL MUST be `http://env:8080` ##### AC-CFG-040: CLI flag override - **Validates:** REQ-CFG-040 -- **Given** `DCM_API_GATEWAY_URL=http://env:9080` is set -- **And** the config file has `api-gateway-url: http://file:9080` -- **When** `dcm --api-gateway-url http://flag:9080 policy list` is invoked -- **Then** the API Gateway URL MUST be `http://flag:9080` +- **Given** `DCM_CONTROL_PLANE_URL=http://env:8080` is set +- **And** the config file has `control-plane-url: http://file:8080` +- **When** `dcm --control-plane-url http://flag:8080 policy list` is invoked +- **Then** the control-plane URL MUST be `http://flag:8080` ##### AC-CFG-050: Built-in defaults - **Validates:** REQ-CFG-050 - **Given** no config file exists and no env vars are set - **When** the CLI resolves configuration -- **Then** `api-gateway-url` MUST be `http://localhost:9080` +- **Then** `control-plane-url` MUST be `http://localhost:8080` - **And** `output-format` MUST be `table` - **And** `timeout` MUST be `30` - **And** `tls-ca-cert` MUST be `""` @@ -398,7 +394,7 @@ None - independently deliverable. Implement the `dcm policy` command group with CRUD subcommands: `create`, `list`, `get`, `update`, `delete`. Each command uses the generated Policy -Manager client to communicate with the API Gateway. +Manager client to communicate with the control plane. Out of scope: policy validation/dry-run, policy diff, bulk policy operations. @@ -1228,7 +1224,7 @@ Depends on Topic 1 (CLI Framework). Implement the `dcm sp provider` command group with read-only subcommands: `list` and `get`. Providers are service providers registered with the Service Provider Manager. The CLI provides read-only access to these resources via the top-level -generated SP Manager client (`service-provider-manager/pkg/client`). +generated SP Manager client ([pkg/sp/client/provider](https://github.com/dcm-project/control-plane/tree/main/pkg/sp/client/provider)). Out of scope: SP provider create/update/delete (managed via other flows), SP provider health check. @@ -1241,7 +1237,7 @@ SP provider health check. | REQ-SPP-020 | `dcm sp provider list` MUST display SP providers in the configured output format | MUST | | | REQ-SPP-030 | `dcm sp provider get` MUST accept a `PROVIDER_ID` positional argument and display the SP provider | MUST | | | REQ-SPP-040 | Missing `PROVIDER_ID` argument for `get` MUST result in a usage error (exit code 2) | MUST | | -| REQ-SPP-050 | All SP provider commands MUST use the generated SP Manager client (`service-provider-manager/pkg/client`) | MUST | | +| REQ-SPP-050 | All SP provider commands MUST use the generated SP Manager client ([pkg/sp/client/provider](https://github.com/dcm-project/control-plane/tree/main/pkg/sp/client/provider)) | MUST | | #### Table Output Columns @@ -1330,10 +1326,10 @@ Formatting). | REQ-XC-ERR-010 | API errors MUST be parsed from RFC 7807 Problem Details format and displayed in a human-readable format | MUST | | | REQ-XC-ERR-020 | For table output, API errors MUST display: `Error: - `, `Status: <STATUS>`, `Detail: <DETAIL>` | MUST | | | REQ-XC-ERR-030 | For JSON/YAML output, API errors MUST display the full Problem Details object | MUST | | -| REQ-XC-ERR-040 | Connection errors (cannot reach API Gateway) MUST be displayed with a clear error message and exit code 1 | MUST | | +| REQ-XC-ERR-040 | Connection errors (cannot reach control plane) MUST be displayed with a clear error message and exit code 1 | MUST | | | REQ-XC-ERR-050 | Timeout errors MUST be displayed with a clear error message and exit code 1 | MUST | | | REQ-XC-ERR-060 | Configuration errors MUST result in exit code 1 | MUST | | -| REQ-XC-ERR-070 | If an API error response does not conform to RFC 7807 (e.g., a raw 502 from the API Gateway), the CLI MUST display the HTTP status code and response body as a plain error message and exit with code 1 | MUST | | +| REQ-XC-ERR-070 | If an API error response does not conform to RFC 7807 (e.g., a raw 502 from the control plane), the CLI MUST display the HTTP status code and response body as a plain error message and exit with code 1 | MUST | | #### Acceptance Criteria @@ -1359,7 +1355,7 @@ Formatting). ##### AC-XC-ERR-030: Connection error - **Validates:** REQ-XC-ERR-040 -- **Given** the API Gateway is unreachable +- **Given** the control plane is unreachable - **When** any command is invoked - **Then** the CLI MUST display a connection error message - **And** exit with code 1 @@ -1367,7 +1363,7 @@ Formatting). ##### AC-XC-ERR-040: Non-RFC-7807 error response - **Validates:** REQ-XC-ERR-070 -- **Given** the API Gateway returns a non-RFC-7807 response (e.g., a raw 502 Bad Gateway with HTML or plain text body) +- **Given** the control plane returns a non-RFC-7807 response (e.g., a raw 502 Bad Gateway with HTML or plain text body) - **When** the CLI processes the error - **Then** the CLI MUST display the HTTP status code and response body as a plain error message - **And** exit with code 1 @@ -1420,25 +1416,25 @@ Formatting). | ID | Requirement | Priority | Notes | |----|-------------|----------|-------| -| REQ-XC-CLI-010 | The CLI MUST use the generated Policy Manager client (`github.com/dcm-project/policy-manager/pkg/client`) for all policy operations | MUST | | -| REQ-XC-CLI-020 | The CLI MUST use the generated Catalog Manager client (`github.com/dcm-project/catalog-manager/pkg/client`) for all catalog operations | MUST | | -| REQ-XC-CLI-025 | The CLI MUST use the generated SP Resource Manager client (`github.com/dcm-project/service-provider-manager/pkg/client/resource_manager`) for all SP resource operations | MUST | | -| REQ-XC-CLI-026 | The CLI MUST use the generated SP Manager client (`github.com/dcm-project/service-provider-manager/pkg/client`) for all SP provider operations | MUST | | -| REQ-XC-CLI-030 | All clients MUST be instantiated with the API Gateway URL appended with `/api/v1alpha1` | MUST | | +| REQ-XC-CLI-010 | The CLI MUST use the generated Policy Manager client ([pkg/policy/client](https://github.com/dcm-project/control-plane/tree/main/pkg/policy/client)) for all policy operations | MUST | | +| REQ-XC-CLI-020 | The CLI MUST use the generated Catalog Manager client ([pkg/catalog/client](https://github.com/dcm-project/control-plane/tree/main/pkg/catalog/client)) for all catalog operations | MUST | | +| REQ-XC-CLI-025 | The CLI MUST use the generated SP Resource Manager client ([pkg/sp/client/resource_manager](https://github.com/dcm-project/control-plane/tree/main/pkg/sp/client/resource_manager)) for all SP resource operations | MUST | | +| REQ-XC-CLI-026 | The CLI MUST use the generated SP Manager client ([pkg/sp/client/provider](https://github.com/dcm-project/control-plane/tree/main/pkg/sp/client/provider)) for all SP provider operations | MUST | | +| REQ-XC-CLI-030 | All clients MUST be instantiated with the control-plane URL appended with `/api/v1alpha1` | MUST | | | REQ-XC-CLI-040 | All clients MUST respect the configured request timeout. The timeout applies to the HTTP request deadline (context timeout) only; file I/O and output formatting are not subject to the timeout. | MUST | | -| REQ-XC-CLI-050 | All clients MUST use a custom HTTP client with TLS transport when the API Gateway URL uses `https://` | MUST | | +| REQ-XC-CLI-050 | All clients MUST use a custom HTTP client with TLS transport when the control-plane URL uses `https://` | MUST | | #### Acceptance Criteria ##### AC-XC-CLI-010: Client instantiation - **Validates:** REQ-XC-CLI-030 -- **Given** the API Gateway URL is `http://localhost:9080` +- **Given** the control-plane URL is `http://localhost:8080` - **When** the generated clients are created -- **Then** the Policy Manager client MUST be created with `http://localhost:9080/api/v1alpha1` -- **And** the Catalog Manager client MUST be created with `http://localhost:9080/api/v1alpha1` -- **And** the SP Resource Manager client MUST be created with `http://localhost:9080/api/v1alpha1` -- **And** the SP Manager client MUST be created with `http://localhost:9080/api/v1alpha1` +- **Then** the Policy Manager client MUST be created with `http://localhost:8080/api/v1alpha1` +- **And** the Catalog Manager client MUST be created with `http://localhost:8080/api/v1alpha1` +- **And** the SP Resource Manager client MUST be created with `http://localhost:8080/api/v1alpha1` +- **And** the SP Manager client MUST be created with `http://localhost:8080/api/v1alpha1` ##### AC-XC-CLI-020: Request timeout @@ -1451,7 +1447,7 @@ Formatting). ##### AC-XC-CLI-030: TLS transport for HTTPS - **Validates:** REQ-XC-CLI-050 -- **Given** the API Gateway URL is `https://gateway.example.com:9443` +- **Given** the control-plane URL is `https://gateway.example.com:9443` - **When** the generated clients are created - **Then** a custom HTTP client with TLS transport MUST be passed via `WithHTTPClient` @@ -1487,8 +1483,8 @@ Formatting). | ID | Requirement | Priority | Notes | |----|-------------|----------|-------| -| REQ-XC-TLS-010 | When the API Gateway URL uses `https://`, the CLI MUST establish a TLS connection | MUST | | -| REQ-XC-TLS-020 | When the API Gateway URL uses `http://`, the CLI MUST NOT use TLS and MUST silently ignore TLS-related flags/config | MUST | | +| REQ-XC-TLS-010 | When the control-plane URL uses `https://`, the CLI MUST establish a TLS connection | MUST | | +| REQ-XC-TLS-020 | When the control-plane URL uses `http://`, the CLI MUST NOT use TLS and MUST silently ignore TLS-related flags/config | MUST | | | REQ-XC-TLS-030 | The CLI MUST support a `--tls-ca-cert` flag to specify a custom CA certificate for server verification | MUST | | | REQ-XC-TLS-040 | The CLI MUST support `--tls-client-cert` and `--tls-client-key` flags for mutual TLS (mTLS) | MUST | | | REQ-XC-TLS-050 | The CLI MUST support a `--tls-skip-verify` flag to skip server certificate verification | MUST | | @@ -1501,14 +1497,14 @@ Formatting). ##### AC-XC-TLS-010: HTTPS triggers TLS - **Validates:** REQ-XC-TLS-010 -- **Given** the API Gateway URL is `https://gateway.example.com:9443` +- **Given** the control-plane URL is `https://gateway.example.com:9443` - **When** any command makes a request - **Then** the HTTP client MUST use a TLS transport ##### AC-XC-TLS-020: HTTP skips TLS - **Validates:** REQ-XC-TLS-020 -- **Given** the API Gateway URL is `http://localhost:9080` +- **Given** the control-plane URL is `http://localhost:8080` - **And** `--tls-skip-verify` or other TLS flags are set - **When** any command makes a request - **Then** TLS MUST NOT be used and TLS flags MUST be silently ignored @@ -1516,7 +1512,7 @@ Formatting). ##### AC-XC-TLS-030: Custom CA certificate - **Validates:** REQ-XC-TLS-030, REQ-XC-TLS-080 -- **Given** the API Gateway URL is `https://gateway.example.com:9443` +- **Given** the control-plane URL is `https://gateway.example.com:9443` - **And** `--tls-ca-cert /path/to/ca.pem` is provided - **When** the TLS connection is established - **Then** the custom CA certificate MUST be used to verify the server @@ -1524,7 +1520,7 @@ Formatting). ##### AC-XC-TLS-040: Mutual TLS with client certificate - **Validates:** REQ-XC-TLS-040 -- **Given** the API Gateway URL is `https://gateway.example.com:9443` +- **Given** the control-plane URL is `https://gateway.example.com:9443` - **And** `--tls-client-cert /path/to/cert.pem` and `--tls-client-key /path/to/key.pem` are provided - **When** the TLS connection is established - **Then** the client certificate and key MUST be used for mutual TLS @@ -1532,7 +1528,7 @@ Formatting). ##### AC-XC-TLS-050: Skip TLS verification - **Validates:** REQ-XC-TLS-050 -- **Given** the API Gateway URL is `https://gateway.example.com:9443` +- **Given** the control-plane URL is `https://gateway.example.com:9443` - **And** `--tls-skip-verify` is set - **When** the TLS connection is established - **Then** server certificate verification MUST be skipped @@ -1557,7 +1553,7 @@ Formatting). | Config Key | Env Var | Flag | Default | Required | Topic | |------------|---------|------|---------|----------|-------| -| api-gateway-url | DCM_API_GATEWAY_URL | --api-gateway-url | http://localhost:9080 | No | 2 | +| control-plane-url | DCM_CONTROL_PLANE_URL | --control-plane-url | http://localhost:8080 | No | 2 | | output-format | DCM_OUTPUT_FORMAT | --output / -o | table | No | 2 | | timeout | DCM_TIMEOUT | --timeout | 30 | No | 2 | | - | DCM_CONFIG | --config | ~/.dcm/config.yaml | No | 2 | @@ -1572,8 +1568,9 @@ Formatting). ### DD-010: Generated clients over hand-written HTTP -**Decision:** Use oapi-codegen generated clients from `policy-manager/pkg/client` -and `catalog-manager/pkg/client` instead of hand-writing HTTP client code. +**Decision:** Use oapi-codegen generated clients from +[control-plane/pkg](https://github.com/dcm-project/control-plane/tree/main/pkg) +instead of hand-writing HTTP client code. **Rationale:** Generated clients guarantee API contract conformance, reduce boilerplate, and evolve with the OpenAPI specs. The CLI is a thin wrapper around @@ -1594,14 +1591,15 @@ parsing, and help generation depend on Cobra. **Related requirements:** REQ-CFG-060 -### DD-030: Single API Gateway endpoint +### DD-030: Single control-plane endpoint -**Decision:** All CLI commands communicate through a single API Gateway URL. +**Decision:** All CLI commands communicate through a single control-plane URL. The CLI does not connect to backend services directly. -**Rationale:** Simplifies configuration (single URL), enables the gateway to -handle routing, load balancing, and future cross-cutting concerns (auth, rate -limiting). Matches the system architecture. +**Rationale:** Simplifies configuration (single URL). The control-plane monolith +serves all v1alpha1 routes on one process (`:8080`, prefix `/api/v1alpha1`). +Policy evaluation and placement run in-process and are not exposed as separate +HTTP APIs to the CLI. **Related requirements:** REQ-XC-CLI-030 @@ -1642,7 +1640,7 @@ programmatic use. ### DD-080: Protocol-driven TLS -**Decision:** TLS is enabled automatically when the API Gateway URL uses `https://` +**Decision:** TLS is enabled automatically when the control-plane URL uses `https://` and disabled when it uses `http://`. TLS flags are silently ignored for `http://` URLs. **Rationale:** Follows the principle of least surprise — the URL scheme already diff --git a/.ai/test-plans/dcm-cli-unit.test-plan.md b/.ai/test-plans/dcm-cli-unit.test-plan.md index b1e4f3e..45cca30 100644 --- a/.ai/test-plans/dcm-cli-unit.test-plan.md +++ b/.ai/test-plans/dcm-cli-unit.test-plan.md @@ -9,8 +9,7 @@ - **Created:** 2026-03-09 Unit tests verify individual components in isolation. All external dependencies -(API Gateway, Policy Manager, Catalog Manager) are replaced with -`net/http/httptest` servers. Tests use direct command execution via Cobra's +(control-plane HTTP API) are replaced with `net/http/httptest` servers. Tests use direct command execution via Cobra's `Execute()` with captured stdout/stderr, and direct function calls for pure logic (config loading, output formatting, file parsing). @@ -38,27 +37,27 @@ test classes. Instead: - **Requirement:** REQ-CFG-010 - **Acceptance Criteria:** AC-CFG-010 - **Type:** Unit -- **Given:** A config file exists at a temporary path with `api-gateway-url: http://custom:9080` +- **Given:** A config file exists at a temporary path with `control-plane-url: http://custom:8080` - **When:** Config is loaded with `--config` pointing to that file -- **Then:** The loaded config has `APIGatewayURL = "http://custom:9080"` +- **Then:** The loaded config has `ControlPlaneURL = "http://custom:8080"` ### TC-U002: Environment variable overrides config file - **Requirement:** REQ-CFG-030, REQ-CFG-040 - **Acceptance Criteria:** AC-CFG-030, AC-CFG-040 - **Type:** Unit -- **Given:** A config file has `api-gateway-url: http://file:9080` AND `DCM_API_GATEWAY_URL=http://env:9080` is set -- **When:** Config is loaded without `--api-gateway-url` flag -- **Then:** The loaded config has `APIGatewayURL = "http://env:9080"` +- **Given:** A config file has `control-plane-url: http://file:8080` AND `DCM_CONTROL_PLANE_URL=http://env:8080` is set +- **When:** Config is loaded without `--control-plane-url` flag +- **Then:** The loaded config has `ControlPlaneURL = "http://env:8080"` ### TC-U003: CLI flag overrides environment variable and config file - **Requirement:** REQ-CFG-040 - **Acceptance Criteria:** AC-CFG-040 - **Type:** Unit -- **Given:** `DCM_API_GATEWAY_URL=http://env:9080` is set AND config file has `api-gateway-url: http://file:9080` -- **When:** Config is loaded with `--api-gateway-url http://flag:9080` -- **Then:** The loaded config has `APIGatewayURL = "http://flag:9080"` +- **Given:** `DCM_CONTROL_PLANE_URL=http://env:8080` is set AND config file has `control-plane-url: http://file:8080` +- **When:** Config is loaded with `--control-plane-url http://flag:8080` +- **Then:** The loaded config has `ControlPlaneURL = "http://flag:8080"` ### TC-U004: Default values applied when no config specified @@ -67,7 +66,7 @@ test classes. Instead: - **Type:** Unit - **Given:** No config file exists AND no environment variables are set AND no flags are provided - **When:** Config is loaded -- **Then:** `APIGatewayURL` defaults to `"http://localhost:9080"` AND `OutputFormat` defaults to `"table"` AND `Timeout` defaults to `30` AND `TLSCACert` defaults to `""` AND `TLSClientCert` defaults to `""` AND `TLSClientKey` defaults to `""` AND `TLSSkipVerify` defaults to `false` +- **Then:** `ControlPlaneURL` defaults to `"http://localhost:8080"` AND `OutputFormat` defaults to `"table"` AND `Timeout` defaults to `30` AND `TLSCACert` defaults to `""` AND `TLSClientCert` defaults to `""` AND `TLSClientKey` defaults to `""` AND `TLSSkipVerify` defaults to `false` ### TC-U005: Missing config file does not cause failure @@ -105,7 +104,7 @@ test classes. Instead: | Environment Variable | Value | Expected Config Field | |------------------------|------------|-----------------------| - | `DCM_API_GATEWAY_URL` | `http://e:9080` | `APIGatewayURL` | + | `DCM_CONTROL_PLANE_URL` | `http://e:8080` | `ControlPlaneURL` | | `DCM_OUTPUT_FORMAT` | `json` | `OutputFormat` | | `DCM_TIMEOUT` | `60` | `Timeout` | | `DCM_TLS_CA_CERT` | `/path/ca.pem` | `TLSCACert` | @@ -289,7 +288,7 @@ test classes. Instead: - **Type:** Unit - **Given:** The root command is created - **When:** `dcm --help` is executed -- **Then:** Flags `--api-gateway-url`, `--output`/`-o`, `--timeout`, `--config`, `--tls-ca-cert`, `--tls-client-cert`, `--tls-client-key`, and `--tls-skip-verify` are listed +- **Then:** Flags `--control-plane-url`, `--output`/`-o`, `--timeout`, `--config`, `--tls-ca-cert`, `--tls-client-cert`, `--tls-client-key`, and `--tls-skip-verify` are listed ### TC-U022: Exit code 0 on success @@ -1198,7 +1197,7 @@ test classes. Instead: - **Requirement:** REQ-XC-TLS-010 - **Acceptance Criteria:** AC-XC-TLS-010 - **Type:** Unit -- **Given:** The API Gateway URL is `https://localhost:<port>` pointing to a TLS-enabled httptest server +- **Given:** The control-plane URL is `https://localhost:<port>` pointing to a TLS-enabled httptest server - **When:** `dcm policy list --tls-skip-verify` is executed - **Then:** The request MUST succeed over TLS AND the mock server receives the request @@ -1207,7 +1206,7 @@ test classes. Instead: - **Requirement:** REQ-XC-TLS-020 - **Acceptance Criteria:** AC-XC-TLS-020 - **Type:** Unit -- **Given:** The API Gateway URL is `http://localhost:<port>` AND `--tls-skip-verify` is set AND `--tls-ca-cert /some/path` is set +- **Given:** The control-plane URL is `http://localhost:<port>` AND `--tls-skip-verify` is set AND `--tls-ca-cert /some/path` is set - **When:** `dcm policy list` is executed against a non-TLS httptest server - **Then:** The request MUST succeed without TLS AND TLS flags MUST be silently ignored @@ -1217,7 +1216,7 @@ test classes. Instead: - **Acceptance Criteria:** AC-XC-TLS-030 - **Type:** Unit - **Given:** A TLS-enabled httptest server with a self-signed certificate AND the CA cert is written to a temp file -- **When:** `dcm policy list --api-gateway-url https://... --tls-ca-cert /tmp/ca.pem` is executed +- **When:** `dcm policy list --control-plane-url https://... --tls-ca-cert /tmp/ca.pem` is executed - **Then:** The TLS handshake MUST succeed using the provided CA certificate ### TC-U091: Mutual TLS with client certificate and key @@ -1235,7 +1234,7 @@ test classes. Instead: - **Acceptance Criteria:** AC-XC-TLS-050 - **Type:** Unit - **Given:** A TLS-enabled httptest server with a self-signed certificate AND no CA cert is provided -- **When:** `dcm policy list --api-gateway-url https://... --tls-skip-verify` is executed +- **When:** `dcm policy list --control-plane-url https://... --tls-skip-verify` is executed - **Then:** The request MUST succeed despite the untrusted certificate ### TC-U093: Incomplete mTLS config — cert without key @@ -1261,7 +1260,7 @@ test classes. Instead: - **Requirement:** REQ-XC-TLS-070 - **Acceptance Criteria:** AC-XC-TLS-070 - **Type:** Unit -- **Given:** `--tls-ca-cert /nonexistent/ca.pem` is provided AND the API Gateway URL uses `https://` +- **Given:** `--tls-ca-cert /nonexistent/ca.pem` is provided AND the control-plane URL uses `https://` - **When:** The CLI attempts to load the CA certificate - **Then:** The CLI MUST exit with code 1 AND display a clear error message @@ -1270,7 +1269,7 @@ test classes. Instead: - **Requirement:** REQ-XC-TLS-070 - **Acceptance Criteria:** AC-XC-TLS-070 - **Type:** Unit -- **Given:** `--tls-client-cert /nonexistent/cert.pem` and `--tls-client-key /tmp/valid-key.pem` are provided AND the API Gateway URL uses `https://` +- **Given:** `--tls-client-cert /nonexistent/cert.pem` and `--tls-client-key /tmp/valid-key.pem` are provided AND the control-plane URL uses `https://` - **When:** The CLI attempts to load the client certificate - **Then:** The CLI MUST exit with code 1 AND display a clear error message @@ -1279,7 +1278,7 @@ test classes. Instead: - **Requirement:** REQ-XC-TLS-080 - **Acceptance Criteria:** AC-XC-TLS-030 - **Type:** Unit -- **Given:** The API Gateway URL uses `https://` AND no `--tls-ca-cert` is provided +- **Given:** The control-plane URL uses `https://` AND no `--tls-ca-cert` is provided - **When:** The TLS transport is configured - **Then:** The system default CA bundle MUST be used (RootCAs is nil in tls.Config) @@ -1348,7 +1347,7 @@ test classes. Instead: - **Requirement:** REQ-XC-ERR-040 - **Acceptance Criteria:** AC-XC-ERR-030 - **Type:** Unit -- **Given:** The API Gateway URL points to a closed/non-existent server +- **Given:** The control-plane URL points to a closed/non-existent server - **When:** `dcm policy list` is executed - **Then:** The CLI displays a connection error message AND exits with code 1 @@ -1437,9 +1436,9 @@ dedicated test class or `Describe` block. - **Requirement:** REQ-XC-CLI-010, REQ-XC-CLI-030 - **Acceptance Criteria:** AC-XC-CLI-010 - **Type:** Unit -- **Given:** The API Gateway URL is `http://localhost:9080` +- **Given:** The control-plane URL is `http://localhost:8080` - **When:** The Policy Manager client is created -- **Then:** The client base URL is `http://localhost:9080/api/v1alpha1` +- **Then:** The client base URL is `http://localhost:8080/api/v1alpha1` - **Referenced by:** TC-U026 (create policy verifies request goes to correct URL path) #### TC-U065: Catalog Manager client instantiated with correct URL @@ -1447,9 +1446,9 @@ dedicated test class or `Describe` block. - **Requirement:** REQ-XC-CLI-020, REQ-XC-CLI-030 - **Acceptance Criteria:** AC-XC-CLI-010 - **Type:** Unit -- **Given:** The API Gateway URL is `http://localhost:9080` +- **Given:** The control-plane URL is `http://localhost:8080` - **When:** The Catalog Manager client is created -- **Then:** The client base URL is `http://localhost:9080/api/v1alpha1` +- **Then:** The client base URL is `http://localhost:8080/api/v1alpha1` - **Referenced by:** TC-U042 (list service types verifies request goes to correct URL path) #### TC-U066: Policy Manager generated client used for policy operations @@ -1477,9 +1476,9 @@ dedicated test class or `Describe` block. - **Requirement:** REQ-XC-CLI-025, REQ-XC-CLI-030 - **Acceptance Criteria:** AC-XC-CLI-010 - **Type:** Unit -- **Given:** The API Gateway URL is `http://localhost:9080` +- **Given:** The control-plane URL is `http://localhost:8080` - **When:** The SP Resource Manager client is created -- **Then:** The client base URL is `http://localhost:9080/api/v1alpha1` +- **Then:** The client base URL is `http://localhost:8080/api/v1alpha1` - **Referenced by:** TC-U121 (list SP resources verifies request goes to correct URL path) #### TC-U131: SP Resource Manager generated client used for SP resource operations @@ -1497,9 +1496,9 @@ dedicated test class or `Describe` block. - **Requirement:** REQ-XC-CLI-026, REQ-XC-CLI-030 - **Acceptance Criteria:** AC-XC-CLI-010 - **Type:** Unit -- **Given:** The API Gateway URL is `http://localhost:9080` +- **Given:** The control-plane URL is `http://localhost:8080` - **When:** The SP Manager client is created -- **Then:** The client base URL is `http://localhost:9080/api/v1alpha1` +- **Then:** The client base URL is `http://localhost:8080/api/v1alpha1` - **Referenced by:** TC-U139 (list SP providers verifies request goes to correct URL path) #### TC-U149: SP Manager generated client used for SP provider operations diff --git a/CLAUDE.md b/CLAUDE.md index 3de8e21..851349e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,14 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -DCM CLI (`dcm`) is a Go-based command-line tool for interacting with the DCM (Data Center Management) control plane. It communicates through the API Gateway (KrakenD on port 9080) to reach the PolicyManager and CatalogManager backends. The CLI uses generated clients from `policy-manager/pkg/client` and `catalog-manager/pkg/client` (oapi-codegen generated) as Go module dependencies. +DCM CLI (`dcm`) is a Go-based command-line tool for interacting with the DCM (Data Center Management) control plane. It communicates directly with the control-plane monolith on port 8080. The CLI uses oapi-codegen generated clients from the [control-plane](https://github.com/dcm-project/control-plane/tree/main/pkg) repo as a Go module dependency. + +Generated client packages: + +- [pkg/policy/client](https://github.com/dcm-project/control-plane/tree/main/pkg/policy/client) — Policy Manager +- [pkg/catalog/client](https://github.com/dcm-project/control-plane/tree/main/pkg/catalog/client) — Catalog Manager +- [pkg/sp/client/resource_manager](https://github.com/dcm-project/control-plane/tree/main/pkg/sp/client/resource_manager) — SP Resource Manager +- [pkg/sp/client/provider](https://github.com/dcm-project/control-plane/tree/main/pkg/sp/client/provider) — SP Manager ## Build and Development Commands @@ -54,10 +61,14 @@ make test-e2e - **internal/commands/**: Cobra command definitions - `root.go`: Root command with global flags + - `helpers.go`: Client constructors, HTTP/TLS helpers, input file parsing - `policy.go`: Policy CRUD commands - `catalog_service_type.go`: Service type list/get commands - `catalog_item.go`: Catalog item create/list/get/delete commands - - `catalog_instance.go`: Catalog instance create/list/get/delete commands + - `catalog_instance.go`: Catalog instance create/list/get/delete/rehydrate commands + - `sp_resource.go`: SP resource list/get commands + - `sp_provider.go`: SP provider list/get commands + - `completion.go`: Shell completion - `version.go`: Version display command - **internal/version/**: Build-time version info injected via ldflags @@ -70,15 +81,15 @@ make test-e2e The project uses Ginkgo as the test framework with Gomega matchers. HTTP-level mocking uses `net/http/httptest`. -E2E tests live under `test/e2e/` and use the `e2e` build tag (`//go:build e2e`). They require a live DCM stack with `DCM_API_GATEWAY_URL` set. +E2E tests live under `test/e2e/` and use the `e2e` build tag (`//go:build e2e`). They require a live DCM stack with `DCM_CONTROL_PLANE_URL` set. ## Key Conventions -1. **Cobra commands**: Each resource group (policy, catalog service-type, catalog item, catalog instance) has its own file with subcommands. Policy supports create/list/get/update/delete. Catalog item and catalog instance do not support update. +1. **Cobra commands**: Each resource group (policy, catalog service-type, catalog item, catalog instance, sp resource, sp provider) has its own file with subcommands. Policy supports create/list/get/update/delete. Catalog item and catalog instance do not support update. SP commands are read-only (list/get). -2. **Generated clients**: Import `github.com/dcm-project/policy-manager/pkg/client` and `github.com/dcm-project/catalog-manager/pkg/client`. No hand-written HTTP client code. +2. **Generated clients**: Import from `github.com/dcm-project/control-plane/pkg/...` (see links in Project Overview). Client constructors live in `helpers.go`. No hand-written HTTP client code. -3. **Configuration precedence**: CLI flags > environment variables (`DCM_API_GATEWAY_URL`, `DCM_OUTPUT_FORMAT`, `DCM_TIMEOUT`, `DCM_CONFIG`) > config file (`~/.dcm/config.yaml`) > built-in defaults. +3. **Configuration precedence**: CLI flags > environment variables (`DCM_CONTROL_PLANE_URL`, `DCM_OUTPUT_FORMAT`, `DCM_TIMEOUT`, `DCM_CONFIG`) > config file (`~/.dcm/config.yaml`) > built-in defaults. 4. **Output formatting**: All commands support `--output/-o` flag with `table` (default), `json`, and `yaml` formats. @@ -89,3 +100,5 @@ E2E tests live under `test/e2e/` and use the `e2e` build tag (`//go:build e2e`). 7. **Version injection**: Build-time ldflags set `internal/version.Version`, `internal/version.Commit`, `internal/version.BuildTime`. 8. **Commit conventions**: All commit messages must include a `Co-Authored-By:` line. The `git commit` command must always use the `--signoff` flag (e.g., `git commit --signoff`). + +9. **Documentation sync**: When changing behavior, flags, env vars, module paths, or architecture, update `README.md`, `CLAUDE.md`, `.ai/specs/`, and `.ai/test-plans/`. Leave `.ai/checkpoints/` unchanged — they are historical dev notes. diff --git a/README.md b/README.md index 6e18487..d9ad034 100644 --- a/README.md +++ b/README.md @@ -4,18 +4,17 @@ ### 1.1 Purpose -The DCM CLI (`dcm`) is the primary user-facing command-line interface for interacting with the DCM (Data Center Management) control plane. It provides commands for managing policies, service types, catalog items, catalog item instances, and service provider resources through the API Gateway. +The DCM CLI (`dcm`) is the primary user-facing command-line interface for interacting with the DCM (Data Center Management) control plane. It provides commands for managing policies, service types, catalog items, catalog item instances, and service provider resources through the control-plane monolith API. ### 1.2 Version Scope -This specification covers the `v1alpha1` API surface, matching the API Gateway route prefix `/api/v1alpha1`. +This specification covers the `v1alpha1` API surface, matching the control-plane route prefix `/api/v1alpha1`. ### 1.3 Reference Documents | Document | Description | |----------|-------------| -| Policy Manager OpenAPI | `api/v1alpha1/openapi.yaml` in dcm-policy-manager | -| Catalog Manager OpenAPI | `api/v1alpha1/openapi.yaml` in dcm-catalog-manager | +| Control-plane OpenAPI | `api/*/v1alpha1/openapi.yaml` in dcm-project/control-plane | | AEP Standards | [aep.dev](https://aep.dev/) - API Enhancement Proposals | | RFC 7807 | Problem Details for HTTP APIs | | RFC 7396 | JSON Merge Patch | @@ -27,28 +26,23 @@ This specification covers the `v1alpha1` API surface, matching the API Gateway r ### 2.1 System Context ``` -┌─────────┐ ┌──────────────────┐ ┌──────────────────┐ -│ │ │ │ ┌──────────────────┐ -│ dcm │─────────────▶│ │──────▶│ Policy Manager │ -│ CLI │ HTTP / HTTPS │ API Gateway │ │ (port 8080) │ -│ │ │ (KrakenD 9080) │ └──────────────────┘ -└─────────┘ │ │ ┌──────────────────┐ - │ │──────▶│ Catalog Manager │ - │ │ │ (port 8080) │ - │ │ └──────────────────┘ - │ │ ┌──────────────────┐ - │ │──────▶│ SP Resource Mgr │ - └──────────────────┘ │ (port 8080) │ - └──────────────────┘ +┌─────────┐ ┌────────────────────────────────────────────────────┐ +│ │ │ control-plane monolith │ +│ dcm │─────────────▶│ (port 8080, /api/v1alpha1/*) │ +│ CLI │ HTTP / HTTPS │ │ +│ │ │ Policy Manager · Catalog Manager · SP Manager │ +│ │ │ │ +└─────────┘ └────────────────────────────────────────────────────┘ ``` -The CLI communicates exclusively through the API Gateway (KrakenD on port 9080). When the API Gateway URL uses an `https://` scheme, the CLI establishes a TLS connection. When the URL uses `http://`, TLS is skipped entirely. The gateway routes requests to the appropriate backend service based on URL path: +The CLI communicates exclusively through the control plane (port 8080). When the control plane URL uses an `https://` scheme, the CLI establishes a TLS connection. When the URL uses `http://`, TLS is skipped entirely. These managers run in-process in the monolith (formerly separate services). CLI commands call paths under `/api/v1alpha1`: - `/api/v1alpha1/policies/*` → Policy Manager - `/api/v1alpha1/service-types/*` → Catalog Manager - `/api/v1alpha1/catalog-items/*` → Catalog Manager - `/api/v1alpha1/catalog-item-instances/*` → Catalog Manager - `/api/v1alpha1/service-type-instances/*` → SP Resource Manager +- `/api/v1alpha1/providers/*` → SP Manager ### 2.2 Internal Architecture @@ -81,7 +75,7 @@ internal/ | `internal/commands` | Cobra command definitions, flag binding, client invocation | | `internal/version` | Build-time version info injected via ldflags | -The CLI uses **generated clients** from `policy-manager/pkg/client`, `catalog-manager/pkg/client`, and `service-provider-manager/pkg/client/resource_manager` (oapi-codegen generated) as Go module dependencies. No hand-written HTTP client is needed. +The CLI uses **generated clients** from the [control-plane](https://github.com/dcm-project/control-plane/tree/main/pkg) repo ([policy/client](https://github.com/dcm-project/control-plane/tree/main/pkg/policy/client), [catalog/client](https://github.com/dcm-project/control-plane/tree/main/pkg/catalog/client), [sp/client](https://github.com/dcm-project/control-plane/tree/main/pkg/sp/client)) (oapi-codegen generated) as Go module dependencies. No hand-written HTTP client is needed. --- @@ -92,7 +86,7 @@ The CLI uses **generated clients** from `policy-manager/pkg/client`, `catalog-ma Default location: `~/.dcm/config.yaml` ```yaml -api-gateway-url: http://localhost:9080 +control-plane-url: http://localhost:8080 output-format: table timeout: 30 tls-ca-cert: "" @@ -105,7 +99,7 @@ tls-skip-verify: false | Variable | Description | Default | |----------|-------------|---------| -| `DCM_API_GATEWAY_URL` | API Gateway base URL | `http://localhost:9080` | +| `DCM_CONTROL_PLANE_URL` | Control plane API base URL | `http://localhost:8080` | | `DCM_OUTPUT_FORMAT` | Output format (`table`, `json`, `yaml`) | `table` | | `DCM_TIMEOUT` | Request timeout in seconds | `30` | | `DCM_CONFIG` | Path to config file | `~/.dcm/config.yaml` | @@ -118,8 +112,8 @@ tls-skip-verify: false Configuration values are resolved in the following order (highest to lowest priority): -1. **Command-line flags** (`--api-gateway-url`, `--output`, `--timeout`) -2. **Environment variables** (`DCM_API_GATEWAY_URL`, etc.) +1. **Command-line flags** (`--control-plane-url`, `--output`, `--timeout`) +2. **Environment variables** (`DCM_CONTROL_PLANE_URL`, etc.) 3. **Configuration file** (`~/.dcm/config.yaml`) 4. **Built-in defaults** @@ -129,7 +123,7 @@ These flags are available on all commands: | Flag | Short | Description | |------|-------|-------------| -| `--api-gateway-url` | | API Gateway URL | +| `--control-plane-url` | | Control plane API URL | | `--output` | `-o` | Output format: `table`, `json`, `yaml` | | `--timeout` | | Request timeout in seconds | | `--config` | | Path to config file | @@ -588,7 +582,7 @@ Manages CLI configuration with file persistence and environment/flag overrides. package config type Config struct { - APIGatewayURL string `yaml:"api-gateway-url" mapstructure:"api-gateway-url"` + ControlPlaneURL string `yaml:"control-plane-url" mapstructure:"control-plane-url"` OutputFormat string `yaml:"output-format" mapstructure:"output-format"` Timeout int `yaml:"timeout" mapstructure:"timeout"` TLSCACert string `yaml:"tls-ca-cert" mapstructure:"tls-ca-cert"` @@ -702,25 +696,26 @@ func Get() Info ### 5.5 Generated Clients (External Dependencies) -The CLI imports generated client packages as Go module dependencies: +The CLI imports generated client packages from the control-plane monorepo: -- `github.com/dcm-project/policy-manager/pkg/client` - Policy Manager client -- `github.com/dcm-project/catalog-manager/pkg/client` - Catalog Manager client -- `github.com/dcm-project/service-provider-manager/pkg/client/resource_manager` - SP Resource Manager client +- [pkg/policy/client](https://github.com/dcm-project/control-plane/tree/main/pkg/policy/client) — Policy Manager client +- [pkg/catalog/client](https://github.com/dcm-project/control-plane/tree/main/pkg/catalog/client) — Catalog Manager client +- [pkg/sp/client/resource_manager](https://github.com/dcm-project/control-plane/tree/main/pkg/sp/client/resource_manager) — SP Resource Manager client +- [pkg/sp/client/provider](https://github.com/dcm-project/control-plane/tree/main/pkg/sp/client/provider) — SP Manager client These are oapi-codegen generated clients providing typed API access. Key interfaces: ```go -// Policy Manager client (from policy-manager/pkg/client) +// Policy client (from control-plane/pkg/policy/client) type ClientInterface interface { CreatePolicy(ctx context.Context, params *CreatePolicyParams, body CreatePolicyJSONRequestBody, ...) (*http.Response, error) ListPolicies(ctx context.Context, params *ListPoliciesParams, ...) (*http.Response, error) GetPolicy(ctx context.Context, policyId string, ...) (*http.Response, error) - UpdatePolicy(ctx context.Context, policyId string, body UpdatePolicyJSONRequestBody, ...) (*http.Response, error) + UpdatePolicyWithApplicationMergePatchPlusJSONBody(ctx context.Context, policyId string, body UpdatePolicyApplicationMergePatchPlusJSONRequestBody, ...) (*http.Response, error) DeletePolicy(ctx context.Context, policyId string, ...) (*http.Response, error) } -// Catalog Manager client (from catalog-manager/pkg/client) +// Catalog client (from control-plane/pkg/catalog/client) type ClientInterface interface { CreateServiceType(ctx context.Context, params *CreateServiceTypeParams, body CreateServiceTypeJSONRequestBody, ...) (*http.Response, error) ListServiceTypes(ctx context.Context, params *ListServiceTypesParams, ...) (*http.Response, error) @@ -729,15 +724,15 @@ type ClientInterface interface { } ``` -Both clients are instantiated with the API Gateway URL and a configured HTTP client. When the API Gateway URL uses `https://`, the HTTP client is configured with a TLS transport based on the TLS settings (CA cert, client cert/key, skip verify). When the URL uses `http://`, TLS is not configured. +Clients are instantiated with the control-plane URL and a configured HTTP client. When the control-plane URL uses `https://`, the HTTP client is configured with a TLS transport based on the TLS settings (CA cert, client cert/key, skip verify). When the URL uses `http://`, TLS is not configured. ```go httpClient := buildHTTPClient(cfg) // configures TLS transport when URL is https -policyClient, _ := policyclient.NewClient(cfg.APIGatewayURL + "/api/v1alpha1", +policyClient, _ := policyclient.NewClient(cfg.ControlPlaneURL + "/api/v1alpha1", policyclient.WithHTTPClient(httpClient)) -catalogClient, _ := catalogclient.NewClient(cfg.APIGatewayURL + "/api/v1alpha1", +catalogClient, _ := catalogclient.NewClient(cfg.ControlPlaneURL + "/api/v1alpha1", catalogclient.WithHTTPClient(httpClient)) -sprmClient, _ := sprmclient.NewClient(cfg.APIGatewayURL + "/api/v1alpha1", +sprmClient, _ := sprmclient.NewClient(cfg.ControlPlaneURL + "/api/v1alpha1", sprmclient.WithHTTPClient(httpClient)) ``` @@ -820,7 +815,7 @@ User invokes command │ ├─▶ Build HTTP client (configure TLS transport if URL is https://) │ - ├─▶ Create generated client with API Gateway URL and HTTP client + ├─▶ Create generated client with control-plane URL and HTTP client │ ├─▶ Execute API call via generated client │ @@ -913,7 +908,7 @@ For JSON/YAML output, `next_page_token` is included in the response object. ### 8.1 Protocol-Based TLS Behavior -TLS is determined automatically by the API Gateway URL scheme: +TLS is determined automatically by the control-plane URL scheme: - **`http://`** — TLS is not used. All TLS-related flags and config are ignored. - **`https://`** — TLS is enabled. The CLI constructs a `tls.Config` and uses it for the HTTP transport. @@ -930,7 +925,7 @@ TLS is determined automatically by the API Gateway URL scheme: ### 8.3 Validation Rules - If `--tls-client-cert` is set, `--tls-client-key` MUST also be set (and vice versa). The CLI MUST exit with code 2 if only one is provided. -- TLS flags are silently ignored when the API Gateway URL uses `http://`. +- TLS flags are silently ignored when the control-plane URL uses `http://`. - If the CA cert, client cert, or client key file does not exist or is not readable, the CLI MUST exit with code 1 with a clear error message. ### 8.4 Configuration Precedence @@ -951,7 +946,7 @@ TLS options follow the same precedence as other configuration: | Category | Description | Exit Code | |----------|-------------|-----------| | Configuration error | Invalid config file, missing required config | 1 | -| Connection error | Cannot reach API Gateway | 1 | +| Connection error | Cannot reach control plane | 1 | | API error | Backend returned an error response | 1 | | Input error | Invalid flags, missing arguments, bad file | 2 | | Timeout | Request exceeded timeout | 1 | @@ -1070,9 +1065,7 @@ go 1.25.5 | `github.com/spf13/cobra` | CLI framework | | `github.com/spf13/viper` | Configuration management | | `gopkg.in/yaml.v3` | YAML parsing/output | -| `github.com/dcm-project/policy-manager/pkg/client` | Generated Policy Manager client | -| `github.com/dcm-project/catalog-manager/pkg/client` | Generated Catalog Manager client | -| `github.com/dcm-project/service-provider-manager/pkg/client/resource_manager` | Generated SP Resource Manager client | +| `github.com/dcm-project/control-plane` | Generated API clients (policy, catalog, SP) | | `github.com/onsi/ginkgo/v2` | Test framework (test dependency) | | `github.com/onsi/gomega` | Test matchers (test dependency) | @@ -1142,14 +1135,14 @@ go test -run TestName ./internal/commands # Specific test - **Location**: `test/e2e/` - **Build tag**: `//go:build e2e` (excluded from `make test`) -- **Requirements**: Live DCM stack (API Gateway + backends) +- **Requirements**: Live DCM stack (control-plane on :8080) - **Framework**: Ginkgo + Gomega - **Scope**: Full command execution against real services Running E2E tests: ```bash -make test-e2e # Requires DCM_API_GATEWAY_URL pointing to live stack +make test-e2e # Requires DCM_CONTROL_PLANE_URL pointing to live stack ``` --- @@ -1173,11 +1166,11 @@ make test-e2e # Requires DCM_API_GATEWAY_URL pointing to live stack ### 12.2 Out of Scope (v1alpha1) -- Authentication and authorization (no auth in v1alpha1 API Gateway) +- Authentication and authorization (no auth in v1alpha1 control-plane API) - Interactive/wizard-style resource creation - Watch/streaming operations - Plugin/extension system - Offline mode or local caching - Bulk operations - Resource diff/dry-run -- Health check command for API Gateway connectivity +- Health check command for control-plane connectivity diff --git a/go.mod b/go.mod index 6c5216a..5677942 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,9 @@ module github.com/dcm-project/cli go 1.25.5 require ( - github.com/dcm-project/catalog-manager v0.0.0-20260408124701-1315c44e39b9 - github.com/dcm-project/policy-manager v0.0.0-20260310132113-15bd45617e87 - github.com/dcm-project/service-provider-manager v0.0.0-20260402145323-bf3185a968d6 - github.com/onsi/ginkgo/v2 v2.28.1 - github.com/onsi/gomega v1.39.1 + github.com/dcm-project/control-plane v0.0.0-20260617094433-e4374fc25292 + github.com/onsi/ginkgo/v2 v2.29.0 + github.com/onsi/gomega v1.41.0 github.com/spf13/cobra v1.10.2 github.com/spf13/viper v1.21.0 go.yaml.in/yaml/v3 v3.0.4 @@ -16,39 +14,38 @@ require ( require ( github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect - github.com/getkin/kin-openapi v0.134.0 // indirect + github.com/getkin/kin-openapi v0.139.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-openapi/jsonpointer v0.22.4 // indirect github.com/go-openapi/swag/jsonname v0.25.4 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 // indirect + github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 // indirect github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/mailru/easyjson v0.9.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect - github.com/oapi-codegen/runtime v1.3.1 // indirect - github.com/oasdiff/yaml v0.0.0-20260313112342-a3ea61cb4d4c // indirect - github.com/oasdiff/yaml3 v0.0.0-20260224194419-61cd415a242b // indirect + github.com/oapi-codegen/runtime v1.4.1 // indirect + github.com/oasdiff/yaml v0.1.0 // indirect + github.com/oasdiff/yaml3 v0.0.13 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/sagikazarmark/locafero v0.11.0 // indirect + github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/woodsbury/decimal128 v1.4.0 // indirect - golang.org/x/mod v0.33.0 // indirect - golang.org/x/net v0.50.0 // indirect - golang.org/x/sync v0.19.0 // indirect - golang.org/x/sys v0.42.0 // indirect - golang.org/x/text v0.34.0 // indirect - golang.org/x/tools v0.42.0 // indirect + golang.org/x/mod v0.35.0 // indirect + golang.org/x/net v0.53.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.44.0 // indirect + golang.org/x/text v0.37.0 // indirect + golang.org/x/tools v0.44.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) diff --git a/go.sum b/go.sum index 62abfde..847b02c 100644 --- a/go.sum +++ b/go.sum @@ -8,18 +8,16 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6N github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dcm-project/catalog-manager v0.0.0-20260408124701-1315c44e39b9 h1:+J3PhsbirFK7GB/RTXE11d9LTXQYHhrQVSERestD45Y= -github.com/dcm-project/catalog-manager v0.0.0-20260408124701-1315c44e39b9/go.mod h1:Xx+nYzmOFKpo0nlSHe3/jHVz3t2dIhN3ov3w+8P2jjU= -github.com/dcm-project/policy-manager v0.0.0-20260310132113-15bd45617e87 h1:IgIFK8eWeNHLloVuwbGZLzun8LHA6d5nqVrct7nB+S8= -github.com/dcm-project/policy-manager v0.0.0-20260310132113-15bd45617e87/go.mod h1:a9eT8Ws0Gy/6FJGp+dWmrB4s/hyfVE0PQPats/aQW0E= -github.com/dcm-project/service-provider-manager v0.0.0-20260402145323-bf3185a968d6 h1:chOdC2Aq4wYBPwewSBZvE8nLQtllhqBTGIcVd9VfUG4= -github.com/dcm-project/service-provider-manager v0.0.0-20260402145323-bf3185a968d6/go.mod h1:enkKEIkI/KahXsXWW/hrWYAIFVy1VkpYSlCMRrOE8Qo= +github.com/dcm-project/control-plane v0.0.0-20260617094433-e4374fc25292 h1:VnUyqtBqLrXTC1ClDSogbvVGDWvikhvmyxP5Znw1yX4= +github.com/dcm-project/control-plane v0.0.0-20260617094433-e4374fc25292/go.mod h1:dYV62wU2z+H1XUBdGoTMdh0V5j8AyJc4Lk55TLgPSBw= +github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= +github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/getkin/kin-openapi v0.134.0 h1:/L5+1+kfe6dXh8Ot/wqiTgUkjOIEJiC0bbYVziHB8rU= -github.com/getkin/kin-openapi v0.134.0/go.mod h1:wK6ZLG/VgoETO9pcLJ/VmAtIcl/DNlMayNTb716EUxE= +github.com/getkin/kin-openapi v0.139.0 h1:pBFXcZJFwz9J1X64jzxlOoNgFm+TF7kNrs9+HJVN6Ic= +github.com/getkin/kin-openapi v0.139.0/go.mod h1:NGxPfE4PwS/TRXEbyx2RrxDFPZvxcWw31Tw8XXjPZLs= github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= @@ -44,8 +42,8 @@ github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 h1:z2ogiKUYzX5Is6zr/vP9vJGqPwcdqsWjOt+V8J7+bTc= -github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= +github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 h1:EwtI+Al+DeppwYX2oXJCETMO23COyaKGP6fHVpkpWpg= +github.com/google/pprof v0.0.0-20260402051712-545e8a4df936/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -70,16 +68,18 @@ github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3v github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/oapi-codegen/runtime v1.3.1 h1:RgDY6J4OGQLbRXhG/Xpt3vSVqYpHQS7hN4m85+5xB9g= -github.com/oapi-codegen/runtime v1.3.1/go.mod h1:kOdeacKy7t40Rclb1je37ZLFboFxh+YLy0zaPCMibPY= -github.com/oasdiff/yaml v0.0.0-20260313112342-a3ea61cb4d4c h1:7ACFcSaQsrWtrH4WHHfUqE1C+f8r2uv8KGaW0jTNjus= -github.com/oasdiff/yaml v0.0.0-20260313112342-a3ea61cb4d4c/go.mod h1:JKox4Gszkxt57kj27u7rvi7IFoIULvCZHUsBTUmQM/s= -github.com/oasdiff/yaml3 v0.0.0-20260224194419-61cd415a242b h1:vivRhVUAa9t1q0Db4ZmezBP8pWQWnXHFokZj0AOea2g= -github.com/oasdiff/yaml3 v0.0.0-20260224194419-61cd415a242b/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= -github.com/onsi/ginkgo/v2 v2.28.1 h1:S4hj+HbZp40fNKuLUQOYLDgZLwNUVn19N3Atb98NCyI= -github.com/onsi/ginkgo/v2 v2.28.1/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE= -github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28= -github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg= +github.com/oapi-codegen/nullable v1.1.0 h1:eAh8JVc5430VtYVnq00Hrbpag9PFRGWLjxR1/3KntMs= +github.com/oapi-codegen/nullable v1.1.0/go.mod h1:KUZ3vUzkmEKY90ksAmit2+5juDIhIZhfDl+0PwOQlFY= +github.com/oapi-codegen/runtime v1.4.1 h1:9nwLoI+KrWxzbBcp0jO/R8uXqbik/HUyCvPeU68Y/qo= +github.com/oapi-codegen/runtime v1.4.1/go.mod h1:GwV7hC2hviaMzj+ITfHVRESK5J2W/GefVwIND/bMGvU= +github.com/oasdiff/yaml v0.1.0 h1:0bqZjfKc/8S9urj4JuwepX41WX9EoA6ifhU3SV06cXg= +github.com/oasdiff/yaml v0.1.0/go.mod h1:kOlRmMdL2X3vucLCEQO5u61SU22RysnfXvcttrZA1O0= +github.com/oasdiff/yaml3 v0.0.13 h1:06svmvOHOVBqF81+sY2EUScvUI/iS/vl2VIeUUxZQwg= +github.com/oasdiff/yaml3 v0.0.13/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= +github.com/onsi/ginkgo/v2 v2.29.0 h1:rfh+ZFjgJhYWRoIqVf3Uwx/W20yLrcrE2h2GmYVRaag= +github.com/onsi/ginkgo/v2 v2.29.0/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= +github.com/onsi/gomega v1.41.0 h1:OwKp4pXNgVxf6sCplzYo794OFNuoL2q2SBMU5NSWOjA= +github.com/onsi/gomega v1.41.0/go.mod h1:M/Uqpu/8qTjtzCLUA2zJHX9Iilrau25x1PdoSRbWh5A= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= @@ -92,6 +92,8 @@ github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWN github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= @@ -126,18 +128,18 @@ github.com/woodsbury/decimal128 v1.4.0 h1:xJATj7lLu4f2oObouMt2tgGiElE5gO6mSWUjQs github.com/woodsbury/decimal128 v1.4.0/go.mod h1:BP46FUrVjVhdTbKT+XuQh2xfQaGki9LMIRJSFuh6THU= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= -golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= -golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= -golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= -golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= -golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= -golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= +golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= +golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= +golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= +golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= +golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= +golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/commands/catalog_instance.go b/internal/commands/catalog_instance.go index 496707b..997fe98 100644 --- a/internal/commands/catalog_instance.go +++ b/internal/commands/catalog_instance.go @@ -5,7 +5,7 @@ import ( "fmt" "net/http" - catalogapi "github.com/dcm-project/catalog-manager/api/v1alpha1" + catalogapi "github.com/dcm-project/control-plane/api/catalog/v1alpha1" "github.com/dcm-project/cli/internal/config" "github.com/dcm-project/cli/internal/output" diff --git a/internal/commands/catalog_instance_test.go b/internal/commands/catalog_instance_test.go index c79e50d..b0059cc 100644 --- a/internal/commands/catalog_instance_test.go +++ b/internal/commands/catalog_instance_test.go @@ -64,7 +64,7 @@ var _ = Describe("Catalog Instance Commands", func() { "--config", nonexistentConfigPath(), } if server != nil { - fullArgs = append(fullArgs, "--api-gateway-url", server.URL) + fullArgs = append(fullArgs, "--control-plane-url", server.URL) } fullArgs = append(fullArgs, args...) cmd.SetArgs(fullArgs) diff --git a/internal/commands/catalog_item.go b/internal/commands/catalog_item.go index ac02a5a..e224b26 100644 --- a/internal/commands/catalog_item.go +++ b/internal/commands/catalog_item.go @@ -5,7 +5,7 @@ import ( "fmt" "net/http" - catalogapi "github.com/dcm-project/catalog-manager/api/v1alpha1" + catalogapi "github.com/dcm-project/control-plane/api/catalog/v1alpha1" "github.com/dcm-project/cli/internal/config" "github.com/dcm-project/cli/internal/output" diff --git a/internal/commands/catalog_item_test.go b/internal/commands/catalog_item_test.go index c215deb..1968c00 100644 --- a/internal/commands/catalog_item_test.go +++ b/internal/commands/catalog_item_test.go @@ -63,7 +63,7 @@ var _ = Describe("Catalog Item Commands", func() { "--config", nonexistentConfigPath(), } if server != nil { - fullArgs = append(fullArgs, "--api-gateway-url", server.URL) + fullArgs = append(fullArgs, "--control-plane-url", server.URL) } fullArgs = append(fullArgs, args...) cmd.SetArgs(fullArgs) diff --git a/internal/commands/catalog_service_type.go b/internal/commands/catalog_service_type.go index a5c2faf..67c2f18 100644 --- a/internal/commands/catalog_service_type.go +++ b/internal/commands/catalog_service_type.go @@ -5,7 +5,7 @@ import ( "fmt" "net/http" - catalogapi "github.com/dcm-project/catalog-manager/api/v1alpha1" + catalogapi "github.com/dcm-project/control-plane/api/catalog/v1alpha1" "github.com/dcm-project/cli/internal/config" "github.com/dcm-project/cli/internal/output" diff --git a/internal/commands/catalog_service_type_test.go b/internal/commands/catalog_service_type_test.go index d640491..b8ed8cb 100644 --- a/internal/commands/catalog_service_type_test.go +++ b/internal/commands/catalog_service_type_test.go @@ -62,7 +62,7 @@ var _ = Describe("Catalog Service-Type Commands", func() { "--config", nonexistentConfigPath(), } if server != nil { - fullArgs = append(fullArgs, "--api-gateway-url", server.URL) + fullArgs = append(fullArgs, "--control-plane-url", server.URL) } fullArgs = append(fullArgs, args...) cmd.SetArgs(fullArgs) diff --git a/internal/commands/helpers.go b/internal/commands/helpers.go index ca09d6a..1231c43 100644 --- a/internal/commands/helpers.go +++ b/internal/commands/helpers.go @@ -13,9 +13,9 @@ import ( "strings" "time" - catalogclient "github.com/dcm-project/catalog-manager/pkg/client" - spmclient "github.com/dcm-project/service-provider-manager/pkg/client/provider" - sprmclient "github.com/dcm-project/service-provider-manager/pkg/client/resource_manager" + catalogclient "github.com/dcm-project/control-plane/pkg/catalog/client" + spmclient "github.com/dcm-project/control-plane/pkg/sp/client/provider" + sprmclient "github.com/dcm-project/control-plane/pkg/sp/client/resource_manager" "github.com/dcm-project/cli/internal/config" "github.com/dcm-project/cli/internal/output" @@ -64,10 +64,10 @@ func newFormatter(cmd *cobra.Command, table *output.TableDef, command string) (* } // buildHTTPClient creates an HTTP client from the resolved configuration. -// When the API Gateway URL uses https://, TLS is configured using the +// When the control plane URL uses https://, TLS is configured using the // TLS-related settings. When it uses http://, TLS settings are ignored. func buildHTTPClient(cfg *config.Config) (*http.Client, error) { - if !strings.HasPrefix(cfg.APIGatewayURL, "https://") { + if !strings.HasPrefix(cfg.ControlPlaneURL, "https://") { return &http.Client{}, nil } @@ -109,7 +109,7 @@ func buildHTTPClient(cfg *config.Config) (*http.Client, error) { // apiBaseURL returns the API base URL with the /api/v1alpha1 suffix. func apiBaseURL(cfg *config.Config) string { - return strings.TrimRight(cfg.APIGatewayURL, "/") + "/api/v1alpha1" + return strings.TrimRight(cfg.ControlPlaneURL, "/") + "/api/v1alpha1" } // parseInputFile reads a YAML or JSON file and returns its content as a map. @@ -180,7 +180,7 @@ func connectionError(err error, cfg *config.Config) error { if isTimeoutError(err) { return fmt.Errorf("request timed out after %d seconds", cfg.Timeout) } - return fmt.Errorf("failed to connect to API Gateway at %s: %w", cfg.APIGatewayURL, err) + return fmt.Errorf("failed to connect to control plane at %s: %w", cfg.ControlPlaneURL, err) } // isTimeoutError checks whether an error is a timeout (context deadline or net timeout). diff --git a/internal/commands/helpers_test.go b/internal/commands/helpers_test.go index 567665b..9a88408 100644 --- a/internal/commands/helpers_test.go +++ b/internal/commands/helpers_test.go @@ -125,7 +125,7 @@ var _ = Describe("buildHTTPClient (via commands)", func() { // TLS flags are present but should be silently ignored for http:// err := executeWithArgs( - "--api-gateway-url", server.URL, + "--control-plane-url", server.URL, "--tls-skip-verify", "policy", "list", ) @@ -152,7 +152,7 @@ var _ = Describe("buildHTTPClient (via commands)", func() { caFile := writePEM(tmpDir, "ca.pem", ca.certPEM) err = executeWithArgs( - "--api-gateway-url", server.URL, + "--control-plane-url", server.URL, "--tls-ca-cert", caFile, "policy", "list", ) @@ -166,7 +166,7 @@ var _ = Describe("buildHTTPClient (via commands)", func() { defer server.Close() err := executeWithArgs( - "--api-gateway-url", server.URL, + "--control-plane-url", server.URL, "--tls-skip-verify", "policy", "list", ) @@ -180,7 +180,7 @@ var _ = Describe("buildHTTPClient (via commands)", func() { defer server.Close() err := executeWithArgs( - "--api-gateway-url", server.URL, + "--control-plane-url", server.URL, "policy", "list", ) Expect(err).To(HaveOccurred()) @@ -215,7 +215,7 @@ var _ = Describe("buildHTTPClient (via commands)", func() { keyFile := writePEM(tmpDir, "client-key.pem", clientKey) err = executeWithArgs( - "--api-gateway-url", server.URL, + "--control-plane-url", server.URL, "--tls-ca-cert", caFile, "--tls-client-cert", certFile, "--tls-client-key", keyFile, @@ -231,7 +231,7 @@ var _ = Describe("buildHTTPClient (via commands)", func() { certFile := writePEM(tmpDir, "client.pem", []byte("dummy")) err := executeWithArgs( - "--api-gateway-url", "https://localhost:9999", + "--control-plane-url", "https://localhost:9999", "--tls-client-cert", certFile, "--tls-skip-verify", "policy", "list", @@ -248,7 +248,7 @@ var _ = Describe("buildHTTPClient (via commands)", func() { keyFile := writePEM(tmpDir, "client-key.pem", []byte("dummy")) err := executeWithArgs( - "--api-gateway-url", "https://localhost:9999", + "--control-plane-url", "https://localhost:9999", "--tls-client-key", keyFile, "--tls-skip-verify", "policy", "list", @@ -262,7 +262,7 @@ var _ = Describe("buildHTTPClient (via commands)", func() { It("should return error when --tls-ca-cert file does not exist", func() { err := executeWithArgs( - "--api-gateway-url", "https://localhost:9999", + "--control-plane-url", "https://localhost:9999", "--tls-ca-cert", "/nonexistent/ca.pem", "--tls-skip-verify", "policy", "list", @@ -280,7 +280,7 @@ var _ = Describe("buildHTTPClient (via commands)", func() { caFile := writePEM(tmpDir, "bad-ca.pem", []byte("not a certificate")) err := executeWithArgs( - "--api-gateway-url", "https://localhost:9999", + "--control-plane-url", "https://localhost:9999", "--tls-ca-cert", caFile, "--tls-skip-verify", "policy", "list", @@ -294,7 +294,7 @@ var _ = Describe("buildHTTPClient (via commands)", func() { keyFile := writePEM(tmpDir, "client-key.pem", []byte("dummy")) err := executeWithArgs( - "--api-gateway-url", "https://localhost:9999", + "--control-plane-url", "https://localhost:9999", "--tls-client-cert", "/nonexistent/client.pem", "--tls-client-key", keyFile, "--tls-skip-verify", @@ -312,7 +312,7 @@ var _ = Describe("buildHTTPClient (via commands)", func() { // Even with mismatched mTLS flags, http:// should succeed err := executeWithArgs( - "--api-gateway-url", server.URL, + "--control-plane-url", server.URL, "--tls-client-cert", "/nonexistent/client.pem", "policy", "list", ) diff --git a/internal/commands/policy.go b/internal/commands/policy.go index 65ac5e7..cec5129 100644 --- a/internal/commands/policy.go +++ b/internal/commands/policy.go @@ -5,8 +5,8 @@ import ( "fmt" "net/http" - policyapi "github.com/dcm-project/policy-manager/api/v1alpha1" - policyclient "github.com/dcm-project/policy-manager/pkg/client" + policyapi "github.com/dcm-project/control-plane/api/policy/v1alpha1" + policyclient "github.com/dcm-project/control-plane/pkg/policy/client" "github.com/dcm-project/cli/internal/config" "github.com/dcm-project/cli/internal/output" diff --git a/internal/commands/policy_test.go b/internal/commands/policy_test.go index 695f1bf..ff1fd2d 100644 --- a/internal/commands/policy_test.go +++ b/internal/commands/policy_test.go @@ -20,7 +20,7 @@ import ( // clearDCMEnvVars removes all DCM_* environment variables to isolate tests. func clearDCMEnvVars() { for _, env := range []string{ - "DCM_API_GATEWAY_URL", + "DCM_CONTROL_PLANE_URL", "DCM_OUTPUT_FORMAT", "DCM_TIMEOUT", "DCM_CONFIG", @@ -108,7 +108,7 @@ var _ = Describe("Policy Commands", func() { }) // executeCommand creates a root command, sets up output capture, and executes - // with the given args prepended by --api-gateway-url and --config. + // with the given args prepended by --control-plane-url and --config. executeCommand := func(args ...string) error { cmd := commands.NewRootCommand() outBuf = new(bytes.Buffer) @@ -120,7 +120,7 @@ var _ = Describe("Policy Commands", func() { "--config", nonexistentConfigPath(), } if server != nil { - fullArgs = append(fullArgs, "--api-gateway-url", server.URL) + fullArgs = append(fullArgs, "--control-plane-url", server.URL) } fullArgs = append(fullArgs, args...) cmd.SetArgs(fullArgs) @@ -566,7 +566,7 @@ var _ = Describe("Policy Commands", func() { }) // TC-U083: Connection error displays clear message - It("TC-U083: should display a connection error when API Gateway is unreachable", func() { + It("TC-U083: should display a connection error when control plane is unreachable", func() { // Use a server that is immediately closed to simulate unreachable closedServer := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {})) closedURL := closedServer.URL @@ -579,7 +579,7 @@ var _ = Describe("Policy Commands", func() { cmd.SetErr(errBuf) cmd.SetArgs([]string{ "--config", nonexistentConfigPath(), - "--api-gateway-url", closedURL, + "--control-plane-url", closedURL, "policy", "list", }) diff --git a/internal/commands/root.go b/internal/commands/root.go index e964c2d..1098666 100644 --- a/internal/commands/root.go +++ b/internal/commands/root.go @@ -36,14 +36,12 @@ func NewRootCommand() *cobra.Command { }, } - // Wrap flag parsing errors as usage errors (exit code 2). cmd.SetFlagErrorFunc(func(_ *cobra.Command, err error) error { return &UsageError{Err: err} }) - // Global flags flags := cmd.PersistentFlags() - flags.String("api-gateway-url", "http://localhost:9080", "API Gateway URL") + flags.String("control-plane-url", "http://localhost:8080", "Control plane API URL") flags.StringP("output", "o", "table", "Output format: table, json, yaml") flags.Int("timeout", 30, "Request timeout in seconds") flags.String("config", "", "Path to config file (default: ~/.dcm/config.yaml)") @@ -52,7 +50,6 @@ func NewRootCommand() *cobra.Command { flags.String("tls-client-key", "", "Path to client private key file for mTLS") flags.Bool("tls-skip-verify", false, "Skip TLS certificate verification") - // Register subcommand groups cmd.AddCommand(newPolicyCommand()) cmd.AddCommand(newCatalogCommand()) cmd.AddCommand(newSPCommand()) diff --git a/internal/commands/root_test.go b/internal/commands/root_test.go index 045510e..ea89183 100644 --- a/internal/commands/root_test.go +++ b/internal/commands/root_test.go @@ -84,7 +84,7 @@ var _ = Describe("Root Command", func() { helpOutput := out.String() expectedFlags := []string{ - "--api-gateway-url", + "--control-plane-url", "--output", "-o", "--timeout", diff --git a/internal/commands/sp_provider.go b/internal/commands/sp_provider.go index f52629a..057bb11 100644 --- a/internal/commands/sp_provider.go +++ b/internal/commands/sp_provider.go @@ -5,7 +5,7 @@ import ( "fmt" "net/http" - spmapi "github.com/dcm-project/service-provider-manager/api/v1alpha1/provider" + spmapi "github.com/dcm-project/control-plane/api/sp/v1alpha1/provider" "github.com/dcm-project/cli/internal/config" "github.com/dcm-project/cli/internal/output" diff --git a/internal/commands/sp_provider_test.go b/internal/commands/sp_provider_test.go index 10af061..4a523c1 100644 --- a/internal/commands/sp_provider_test.go +++ b/internal/commands/sp_provider_test.go @@ -62,7 +62,7 @@ var _ = Describe("SP Provider Commands", func() { "--config", nonexistentConfigPath(), } if server != nil { - fullArgs = append(fullArgs, "--api-gateway-url", server.URL) + fullArgs = append(fullArgs, "--control-plane-url", server.URL) } fullArgs = append(fullArgs, args...) cmd.SetArgs(fullArgs) diff --git a/internal/commands/sp_resource.go b/internal/commands/sp_resource.go index ff39f2a..9e7bf9f 100644 --- a/internal/commands/sp_resource.go +++ b/internal/commands/sp_resource.go @@ -5,7 +5,7 @@ import ( "fmt" "net/http" - sprmapi "github.com/dcm-project/service-provider-manager/api/v1alpha1/resource_manager" + sprmapi "github.com/dcm-project/control-plane/api/sp/v1alpha1/resource_manager" "github.com/dcm-project/cli/internal/config" "github.com/dcm-project/cli/internal/output" diff --git a/internal/commands/sp_resource_test.go b/internal/commands/sp_resource_test.go index 096c89e..a89e83d 100644 --- a/internal/commands/sp_resource_test.go +++ b/internal/commands/sp_resource_test.go @@ -75,7 +75,7 @@ var _ = Describe("SP Resource Commands", func() { "--config", nonexistentConfigPath(), } if server != nil { - fullArgs = append(fullArgs, "--api-gateway-url", server.URL) + fullArgs = append(fullArgs, "--control-plane-url", server.URL) } fullArgs = append(fullArgs, args...) cmd.SetArgs(fullArgs) diff --git a/internal/config/config.go b/internal/config/config.go index 9f6c63b..c9fc49f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -12,6 +12,8 @@ import ( "github.com/spf13/viper" ) +const defaultControlPlaneURL = "http://localhost:8080" + type contextKey struct{} // WithConfig stores a Config in the given context. @@ -19,7 +21,7 @@ func WithConfig(ctx context.Context, cfg *Config) context.Context { return context.WithValue(ctx, contextKey{}, cfg) } -// FromContext retrieves the Config from a context. Returns nil if not present. +// FromContext retrieves a Config from a context. Returns nil if not present. func FromContext(ctx context.Context) *Config { cfg, _ := ctx.Value(contextKey{}).(*Config) return cfg @@ -32,13 +34,13 @@ func FromCommand(cmd *cobra.Command) *Config { // Config holds the resolved CLI configuration. type Config struct { - APIGatewayURL string `yaml:"api-gateway-url" mapstructure:"api-gateway-url"` - OutputFormat string `yaml:"output-format" mapstructure:"output-format"` - Timeout int `yaml:"timeout" mapstructure:"timeout"` - TLSCACert string `yaml:"tls-ca-cert" mapstructure:"tls-ca-cert"` - TLSClientCert string `yaml:"tls-client-cert" mapstructure:"tls-client-cert"` - TLSClientKey string `yaml:"tls-client-key" mapstructure:"tls-client-key"` - TLSSkipVerify bool `yaml:"tls-skip-verify" mapstructure:"tls-skip-verify"` + ControlPlaneURL string `yaml:"control-plane-url" mapstructure:"control-plane-url"` + OutputFormat string `yaml:"output-format" mapstructure:"output-format"` + Timeout int `yaml:"timeout" mapstructure:"timeout"` + TLSCACert string `yaml:"tls-ca-cert" mapstructure:"tls-ca-cert"` + TLSClientCert string `yaml:"tls-client-cert" mapstructure:"tls-client-cert"` + TLSClientKey string `yaml:"tls-client-key" mapstructure:"tls-client-key"` + TLSSkipVerify bool `yaml:"tls-skip-verify" mapstructure:"tls-skip-verify"` } // Load reads configuration from file, environment variables, and command-line @@ -47,7 +49,7 @@ func Load(cmd *cobra.Command) (*Config, error) { v := viper.New() // Built-in defaults (REQ-CFG-050) - v.SetDefault("api-gateway-url", "http://localhost:9080") + v.SetDefault("control-plane-url", defaultControlPlaneURL) v.SetDefault("output-format", "table") v.SetDefault("timeout", 30) v.SetDefault("tls-ca-cert", "") @@ -57,7 +59,7 @@ func Load(cmd *cobra.Command) (*Config, error) { // Environment variable binding (REQ-CFG-030) v.SetEnvPrefix("DCM") - v.MustBindEnv("api-gateway-url", "DCM_API_GATEWAY_URL") + v.MustBindEnv("control-plane-url", "DCM_CONTROL_PLANE_URL") v.MustBindEnv("output-format", "DCM_OUTPUT_FORMAT") v.MustBindEnv("timeout", "DCM_TIMEOUT") v.MustBindEnv("tls-ca-cert", "DCM_TLS_CA_CERT") @@ -120,13 +122,13 @@ func configFilePath(cmd *cobra.Command) string { // unset flags don't override environment variables or config file values. func bindFlags(v *viper.Viper, cmd *cobra.Command) error { flagToKey := map[string]string{ - "api-gateway-url": "api-gateway-url", - "output": "output-format", - "timeout": "timeout", - "tls-ca-cert": "tls-ca-cert", - "tls-client-cert": "tls-client-cert", - "tls-client-key": "tls-client-key", - "tls-skip-verify": "tls-skip-verify", + "control-plane-url": "control-plane-url", + "output": "output-format", + "timeout": "timeout", + "tls-ca-cert": "tls-ca-cert", + "tls-client-cert": "tls-client-cert", + "tls-client-key": "tls-client-key", + "tls-skip-verify": "tls-skip-verify", } for flagName, configKey := range flagToKey { diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 2aae857..e135f8a 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -14,7 +14,7 @@ import ( // clearDCMEnvVars removes all DCM_* environment variables to isolate tests. func clearDCMEnvVars() { envVars := []string{ - "DCM_API_GATEWAY_URL", + "DCM_CONTROL_PLANE_URL", "DCM_OUTPUT_FORMAT", "DCM_TIMEOUT", "DCM_CONFIG", @@ -42,10 +42,9 @@ var _ = Describe("Configuration", func() { clearDCMEnvVars() }) - // TC-U001: Load configuration from config file Describe("TC-U001: Config file loading", func() { - It("should load api-gateway-url from config file", func() { - cfgPath := writeConfigFile("api-gateway-url: http://custom:9080\n") + It("should load control-plane-url from config file", func() { + cfgPath := writeConfigFile("control-plane-url: http://custom:8080\n") cmd := commands.NewRootCommand() cmd.SetArgs([]string{"--config", cfgPath, "version"}) cmd.SetOut(GinkgoWriter) @@ -54,15 +53,14 @@ var _ = Describe("Configuration", func() { cfg, err := config.Load(cmd) Expect(err).NotTo(HaveOccurred()) - Expect(cfg.APIGatewayURL).To(Equal("http://custom:9080")) + Expect(cfg.ControlPlaneURL).To(Equal("http://custom:8080")) }) }) - // TC-U002: Environment variable overrides config file Describe("TC-U002: Env var overrides config file", func() { - It("should use environment variable over config file value", func() { - cfgPath := writeConfigFile("api-gateway-url: http://file:9080\n") - GinkgoT().Setenv("DCM_API_GATEWAY_URL", "http://env:9080") + It("should use DCM_CONTROL_PLANE_URL over config file value", func() { + cfgPath := writeConfigFile("control-plane-url: http://file:8080\n") + GinkgoT().Setenv("DCM_CONTROL_PLANE_URL", "http://env:8080") cmd := commands.NewRootCommand() cmd.SetArgs([]string{"--config", cfgPath, "version"}) @@ -72,20 +70,19 @@ var _ = Describe("Configuration", func() { cfg, err := config.Load(cmd) Expect(err).NotTo(HaveOccurred()) - Expect(cfg.APIGatewayURL).To(Equal("http://env:9080")) + Expect(cfg.ControlPlaneURL).To(Equal("http://env:8080")) }) }) - // TC-U003: CLI flag overrides environment variable and config file Describe("TC-U003: CLI flag overrides env var and config file", func() { - It("should use CLI flag over environment variable and config file", func() { - cfgPath := writeConfigFile("api-gateway-url: http://file:9080\n") - GinkgoT().Setenv("DCM_API_GATEWAY_URL", "http://env:9080") + It("should use --control-plane-url over environment and config file", func() { + cfgPath := writeConfigFile("control-plane-url: http://file:8080\n") + GinkgoT().Setenv("DCM_CONTROL_PLANE_URL", "http://env:8080") cmd := commands.NewRootCommand() cmd.SetArgs([]string{ "--config", cfgPath, - "--api-gateway-url", "http://flag:9080", + "--control-plane-url", "http://flag:8080", "version", }) cmd.SetOut(GinkgoWriter) @@ -94,11 +91,10 @@ var _ = Describe("Configuration", func() { cfg, err := config.Load(cmd) Expect(err).NotTo(HaveOccurred()) - Expect(cfg.APIGatewayURL).To(Equal("http://flag:9080")) + Expect(cfg.ControlPlaneURL).To(Equal("http://flag:8080")) }) }) - // TC-U004: Default values applied when no config specified Describe("TC-U004: Built-in defaults", func() { It("should apply default values when no config file, env vars, or flags are set", func() { cfgPath := filepath.Join(GinkgoT().TempDir(), "nonexistent.yaml") @@ -110,7 +106,7 @@ var _ = Describe("Configuration", func() { cfg, err := config.Load(cmd) Expect(err).NotTo(HaveOccurred()) - Expect(cfg.APIGatewayURL).To(Equal("http://localhost:9080")) + Expect(cfg.ControlPlaneURL).To(Equal("http://localhost:8080")) Expect(cfg.OutputFormat).To(Equal("table")) Expect(cfg.Timeout).To(Equal(30)) Expect(cfg.TLSCACert).To(BeEmpty()) @@ -120,7 +116,6 @@ var _ = Describe("Configuration", func() { }) }) - // TC-U005: Missing config file does not cause failure Describe("TC-U005: Missing config file", func() { It("should not fail when the config file does not exist", func() { cfgPath := filepath.Join(GinkgoT().TempDir(), "does-not-exist.yaml") @@ -132,11 +127,10 @@ var _ = Describe("Configuration", func() { cfg, err := config.Load(cmd) Expect(err).NotTo(HaveOccurred()) - Expect(cfg.APIGatewayURL).To(Equal("http://localhost:9080")) + Expect(cfg.ControlPlaneURL).To(Equal("http://localhost:8080")) }) }) - // TC-U006: Custom config file path via --config flag Describe("TC-U006: Custom config file via --config", func() { It("should load configuration from a custom file path", func() { cfgPath := writeConfigFile("timeout: 60\n") @@ -152,7 +146,6 @@ var _ = Describe("Configuration", func() { }) }) - // TC-U007: Custom config file path via DCM_CONFIG environment variable Describe("TC-U007: Custom config file via DCM_CONFIG", func() { It("should load configuration from DCM_CONFIG path", func() { cfgPath := writeConfigFile("timeout: 45\n") @@ -170,7 +163,6 @@ var _ = Describe("Configuration", func() { }) }) - // TC-U008: All environment variables are supported Describe("TC-U008: All environment variables", func() { DescribeTable("should load configuration from each environment variable", func(envVar, envValue, configField string, expected any) { @@ -187,8 +179,8 @@ var _ = Describe("Configuration", func() { Expect(err).NotTo(HaveOccurred()) switch configField { - case "APIGatewayURL": - Expect(cfg.APIGatewayURL).To(Equal(expected)) + case "ControlPlaneURL": + Expect(cfg.ControlPlaneURL).To(Equal(expected)) case "OutputFormat": Expect(cfg.OutputFormat).To(Equal(expected)) case "Timeout": @@ -203,7 +195,7 @@ var _ = Describe("Configuration", func() { Expect(cfg.TLSSkipVerify).To(Equal(expected)) } }, - Entry("DCM_API_GATEWAY_URL", "DCM_API_GATEWAY_URL", "http://e:9080", "APIGatewayURL", "http://e:9080"), + Entry("DCM_CONTROL_PLANE_URL", "DCM_CONTROL_PLANE_URL", "http://cp:8080", "ControlPlaneURL", "http://cp:8080"), Entry("DCM_OUTPUT_FORMAT", "DCM_OUTPUT_FORMAT", "json", "OutputFormat", "json"), Entry("DCM_TIMEOUT", "DCM_TIMEOUT", "60", "Timeout", 60), Entry("DCM_TLS_CA_CERT", "DCM_TLS_CA_CERT", "/path/ca.pem", "TLSCACert", "/path/ca.pem"),