From 25406a184295ec40a6b5a747be7e753d99871513 Mon Sep 17 00:00:00 2001 From: Gloria Ciavarrini Date: Fri, 12 Jun 2026 16:59:26 +0200 Subject: [PATCH 01/10] Rename API gateway URL settings to control-plane. Add control-plane-url flag, DCM_CONTROL_PLANE_URL, and :8080 default. Keep legacy api-gateway names as aliases. Assisted-By: Claude (Anthropic) Signed-off-by: Gloria Ciavarrini --- CLAUDE.md | 4 +- README.md | 22 +-- internal/commands/catalog_instance_test.go | 2 +- internal/commands/catalog_item_test.go | 2 +- .../commands/catalog_service_type_test.go | 2 +- internal/commands/helpers.go | 8 +- internal/commands/helpers_test.go | 22 +-- internal/commands/policy_test.go | 7 +- internal/commands/root.go | 6 +- internal/commands/root_test.go | 2 +- internal/commands/sp_provider_test.go | 2 +- internal/commands/sp_resource_test.go | 2 +- internal/config/config.go | 52 ++++-- internal/config/config_test.go | 158 +++++++----------- 14 files changed, 141 insertions(+), 150 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 3de8e21..a645209 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -70,7 +70,7 @@ 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` (or legacy `DCM_API_GATEWAY_URL`) set. ## Key Conventions @@ -78,7 +78,7 @@ E2E tests live under `test/e2e/` and use the `e2e` build tag (`//go:build e2e`). 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. -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`, legacy `DCM_API_GATEWAY_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. diff --git a/README.md b/README.md index 6e18487..342609d 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,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 +105,8 @@ 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_API_GATEWAY_URL` | Deprecated alias for `DCM_CONTROL_PLANE_URL` | — | | `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 +119,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 +130,8 @@ These flags are available on all commands: | Flag | Short | Description | |------|-------|-------------| -| `--api-gateway-url` | | API Gateway URL | +| `--control-plane-url` | | Control plane API URL | +| `--api-gateway-url` | | Deprecated; use `--control-plane-url` | | `--output` | `-o` | Output format: `table`, `json`, `yaml` | | `--timeout` | | Request timeout in seconds | | `--config` | | Path to config file | @@ -588,7 +590,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"` @@ -733,11 +735,11 @@ Both clients are instantiated with the API Gateway URL and a configured HTTP cli ```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)) ``` @@ -1149,7 +1151,7 @@ go test -run TestName ./internal/commands # Specific test 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 ``` --- 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_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_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..a97a0cb 100644 --- a/internal/commands/helpers.go +++ b/internal/commands/helpers.go @@ -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_test.go b/internal/commands/policy_test.go index 695f1bf..48734a2 100644 --- a/internal/commands/policy_test.go +++ b/internal/commands/policy_test.go @@ -20,6 +20,7 @@ import ( // clearDCMEnvVars removes all DCM_* environment variables to isolate tests. func clearDCMEnvVars() { for _, env := range []string{ + "DCM_CONTROL_PLANE_URL", "DCM_API_GATEWAY_URL", "DCM_OUTPUT_FORMAT", "DCM_TIMEOUT", @@ -108,7 +109,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 +121,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) @@ -579,7 +580,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..b2b4619 100644 --- a/internal/commands/root.go +++ b/internal/commands/root.go @@ -36,14 +36,13 @@ 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.String("api-gateway-url", "http://localhost:8080", "Deprecated: use --control-plane-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 +51,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_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_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..5dd0694 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. @@ -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 @@ -46,8 +48,6 @@ type Config struct { 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("output-format", "table") v.SetDefault("timeout", 30) v.SetDefault("tls-ca-cert", "") @@ -55,9 +55,7 @@ func Load(cmd *cobra.Command) (*Config, error) { v.SetDefault("tls-client-key", "") v.SetDefault("tls-skip-verify", false) - // Environment variable binding (REQ-CFG-030) v.SetEnvPrefix("DCM") - v.MustBindEnv("api-gateway-url", "DCM_API_GATEWAY_URL") v.MustBindEnv("output-format", "DCM_OUTPUT_FORMAT") v.MustBindEnv("timeout", "DCM_TIMEOUT") v.MustBindEnv("tls-ca-cert", "DCM_TLS_CA_CERT") @@ -65,7 +63,6 @@ func Load(cmd *cobra.Command) (*Config, error) { v.MustBindEnv("tls-client-key", "DCM_TLS_CLIENT_KEY") v.MustBindEnv("tls-skip-verify", "DCM_TLS_SKIP_VERIFY") - // Config file path (REQ-CFG-010, REQ-CFG-020) configPath := configFilePath(cmd) if configPath != "" { v.SetConfigFile(configPath) @@ -77,7 +74,6 @@ func Load(cmd *cobra.Command) (*Config, error) { v.SetConfigFile(filepath.Join(home, ".dcm", "config.yaml")) } - // Read config file — ignore "not found" errors (REQ-CFG-070) if err := v.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); !ok { if !os.IsNotExist(err) { @@ -86,7 +82,6 @@ func Load(cmd *cobra.Command) (*Config, error) { } } - // Bind CLI flags so they override env vars and config file (REQ-CFG-040) if cmd != nil { if err := bindFlags(v, cmd); err != nil { return nil, err @@ -98,9 +93,37 @@ func Load(cmd *cobra.Command) (*Config, error) { return nil, fmt.Errorf("unmarshalling config: %w", err) } + cfg.ControlPlaneURL = resolveControlPlaneURL(v, cmd) return &cfg, nil } +func resolveControlPlaneURL(v *viper.Viper, cmd *cobra.Command) string { + if cmd != nil { + if f := cmd.Root().PersistentFlags().Lookup("control-plane-url"); f != nil && f.Changed { + return f.Value.String() + } + if f := cmd.Root().PersistentFlags().Lookup("api-gateway-url"); f != nil && f.Changed { + return f.Value.String() + } + } + + if u := os.Getenv("DCM_CONTROL_PLANE_URL"); u != "" { + return u + } + if u := os.Getenv("DCM_API_GATEWAY_URL"); u != "" { + return u + } + + if v.InConfig("control-plane-url") { + return v.GetString("control-plane-url") + } + if v.InConfig("api-gateway-url") { + return v.GetString("api-gateway-url") + } + + return defaultControlPlaneURL +} + // configFilePath resolves the config file path from the --config flag // or the DCM_CONFIG environment variable. func configFilePath(cmd *cobra.Command) string { @@ -120,7 +143,6 @@ 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", diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 2aae857..f912680 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -11,9 +11,9 @@ import ( "github.com/dcm-project/cli/internal/config" ) -// clearDCMEnvVars removes all DCM_* environment variables to isolate tests. func clearDCMEnvVars() { envVars := []string{ + "DCM_CONTROL_PLANE_URL", "DCM_API_GATEWAY_URL", "DCM_OUTPUT_FORMAT", "DCM_TIMEOUT", @@ -28,7 +28,6 @@ func clearDCMEnvVars() { } } -// writeConfigFile creates a temporary config file with the given YAML content. func writeConfigFile(content string) string { dir := GinkgoT().TempDir() path := filepath.Join(dir, "config.yaml") @@ -37,80 +36,82 @@ func writeConfigFile(content string) string { return path } +func loadConfig(args ...string) *config.Config { + cmd := commands.NewRootCommand() + cmd.SetArgs(args) + cmd.SetOut(GinkgoWriter) + cmd.SetErr(GinkgoWriter) + _ = cmd.Execute() + + cfg, err := config.Load(cmd) + Expect(err).NotTo(HaveOccurred()) + return cfg +} + var _ = Describe("Configuration", func() { BeforeEach(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") - cmd := commands.NewRootCommand() - cmd.SetArgs([]string{"--config", cfgPath, "version"}) - cmd.SetOut(GinkgoWriter) - cmd.SetErr(GinkgoWriter) - _ = cmd.Execute() - - cfg, err := config.Load(cmd) - Expect(err).NotTo(HaveOccurred()) - Expect(cfg.APIGatewayURL).To(Equal("http://custom:9080")) + It("should load control-plane-url from config file", func() { + cfgPath := writeConfigFile("control-plane-url: http://custom:8080\n") + cfg := loadConfig("--config", cfgPath, "version") + Expect(cfg.ControlPlaneURL).To(Equal("http://custom:8080")) + }) + + It("should still load legacy api-gateway-url from config file", func() { + cfgPath := writeConfigFile("api-gateway-url: http://legacy:9080\n") + cfg := loadConfig("--config", cfgPath, "version") + Expect(cfg.ControlPlaneURL).To(Equal("http://legacy:9080")) }) }) - // 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") - - cmd := commands.NewRootCommand() - cmd.SetArgs([]string{"--config", cfgPath, "version"}) - cmd.SetOut(GinkgoWriter) - cmd.SetErr(GinkgoWriter) - _ = cmd.Execute() - - cfg, err := config.Load(cmd) - Expect(err).NotTo(HaveOccurred()) - Expect(cfg.APIGatewayURL).To(Equal("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") + cfg := loadConfig("--config", cfgPath, "version") + Expect(cfg.ControlPlaneURL).To(Equal("http://env:8080")) + }) + + It("should still use legacy DCM_API_GATEWAY_URL over config file value", func() { + cfgPath := writeConfigFile("control-plane-url: http://file:8080\n") + GinkgoT().Setenv("DCM_API_GATEWAY_URL", "http://legacy-env:9080") + cfg := loadConfig("--config", cfgPath, "version") + Expect(cfg.ControlPlaneURL).To(Equal("http://legacy-env:9080")) }) }) - // 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") + cfg := loadConfig( + "--config", cfgPath, + "--control-plane-url", "http://flag:8080", + "version", + ) + Expect(cfg.ControlPlaneURL).To(Equal("http://flag:8080")) + }) - cmd := commands.NewRootCommand() - cmd.SetArgs([]string{ + It("should still accept deprecated --api-gateway-url flag", func() { + cfgPath := writeConfigFile("control-plane-url: http://file:8080\n") + GinkgoT().Setenv("DCM_CONTROL_PLANE_URL", "http://env:8080") + cfg := loadConfig( "--config", cfgPath, - "--api-gateway-url", "http://flag:9080", + "--api-gateway-url", "http://legacy-flag:9080", "version", - }) - cmd.SetOut(GinkgoWriter) - cmd.SetErr(GinkgoWriter) - _ = cmd.Execute() - - cfg, err := config.Load(cmd) - Expect(err).NotTo(HaveOccurred()) - Expect(cfg.APIGatewayURL).To(Equal("http://flag:9080")) + ) + Expect(cfg.ControlPlaneURL).To(Equal("http://legacy-flag:9080")) }) }) - // 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") - cmd := commands.NewRootCommand() - cmd.SetArgs([]string{"--config", cfgPath, "version"}) - cmd.SetOut(GinkgoWriter) - cmd.SetErr(GinkgoWriter) - _ = cmd.Execute() - - cfg, err := config.Load(cmd) - Expect(err).NotTo(HaveOccurred()) - Expect(cfg.APIGatewayURL).To(Equal("http://localhost:9080")) + cfg := loadConfig("--config", cfgPath, "version") + 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,75 +121,41 @@ 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") - cmd := commands.NewRootCommand() - cmd.SetArgs([]string{"--config", cfgPath, "version"}) - cmd.SetOut(GinkgoWriter) - cmd.SetErr(GinkgoWriter) - _ = cmd.Execute() - - cfg, err := config.Load(cmd) - Expect(err).NotTo(HaveOccurred()) - Expect(cfg.APIGatewayURL).To(Equal("http://localhost:9080")) + cfg := loadConfig("--config", cfgPath, "version") + 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") - cmd := commands.NewRootCommand() - cmd.SetArgs([]string{"--config", cfgPath, "version"}) - cmd.SetOut(GinkgoWriter) - cmd.SetErr(GinkgoWriter) - _ = cmd.Execute() - - cfg, err := config.Load(cmd) - Expect(err).NotTo(HaveOccurred()) + cfg := loadConfig("--config", cfgPath, "version") Expect(cfg.Timeout).To(Equal(60)) }) }) - // 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") GinkgoT().Setenv("DCM_CONFIG", cfgPath) - - cmd := commands.NewRootCommand() - cmd.SetArgs([]string{"version"}) - cmd.SetOut(GinkgoWriter) - cmd.SetErr(GinkgoWriter) - _ = cmd.Execute() - - cfg, err := config.Load(cmd) - Expect(err).NotTo(HaveOccurred()) + cfg := loadConfig("version") Expect(cfg.Timeout).To(Equal(45)) }) }) - // 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) { cfgPath := filepath.Join(GinkgoT().TempDir(), "nonexistent.yaml") GinkgoT().Setenv(envVar, envValue) - - cmd := commands.NewRootCommand() - cmd.SetArgs([]string{"--config", cfgPath, "version"}) - cmd.SetOut(GinkgoWriter) - cmd.SetErr(GinkgoWriter) - _ = cmd.Execute() - - cfg, err := config.Load(cmd) - Expect(err).NotTo(HaveOccurred()) + cfg := loadConfig("--config", cfgPath, "version") 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 +170,8 @@ 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_API_GATEWAY_URL", "DCM_API_GATEWAY_URL", "http://legacy:9080", "ControlPlaneURL", "http://legacy:9080"), 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"), From 131cee8fe3cb6ad88d6dfc0c77e8e99b613e2c60 Mon Sep 17 00:00:00 2001 From: Gloria Ciavarrini Date: Wed, 17 Jun 2026 15:39:30 +0200 Subject: [PATCH 02/10] Import API clients from control-plane monorepo. Drop catalog-manager, policy-manager, and service-provider-manager. Pin control-plane at e4374fc25292. Assisted-By: Claude (Anthropic) Signed-off-by: Gloria Ciavarrini --- go.mod | 33 ++++++------ go.sum | 66 ++++++++++++----------- internal/commands/catalog_instance.go | 2 +- internal/commands/catalog_item.go | 2 +- internal/commands/catalog_service_type.go | 2 +- internal/commands/helpers.go | 6 +-- internal/commands/policy.go | 4 +- internal/commands/policy_test.go | 2 +- internal/commands/sp_provider.go | 2 +- internal/commands/sp_resource.go | 2 +- 10 files changed, 60 insertions(+), 61 deletions(-) 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_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_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/helpers.go b/internal/commands/helpers.go index a97a0cb..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" 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 48734a2..3cda57b 100644 --- a/internal/commands/policy_test.go +++ b/internal/commands/policy_test.go @@ -567,7 +567,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 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_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" From 4a3cb305879392bcf364f5bfc3f78dab3cbed6e6 Mon Sep 17 00:00:00 2001 From: Gloria Ciavarrini Date: Wed, 17 Jun 2026 15:39:38 +0200 Subject: [PATCH 03/10] Update docs for control-plane monolith and import paths. Assisted-By: Claude (Anthropic) Signed-off-by: Gloria Ciavarrini --- .ai/specs/dcm-cli.spec.md | 145 +++++++++++------------ .ai/test-plans/dcm-cli-unit.test-plan.md | 58 ++++----- CLAUDE.md | 4 +- README.md | 67 +++++------ 4 files changed, 132 insertions(+), 142 deletions(-) diff --git a/.ai/specs/dcm-cli.spec.md b/.ai/specs/dcm-cli.spec.md index 4077992..e6d9d14 100644 --- a/.ai/specs/dcm-cli.spec.md +++ b/.ai/specs/dcm-cli.spec.md @@ -3,12 +3,10 @@ ## 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 +`github.com/dcm-project/control-plane/pkg/{policy,catalog,sp}/client` +(oapi-codegen generated) as a Go module dependency. **Version scope (v1alpha1):** @@ -28,20 +26,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 +48,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 +120,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 +143,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`, deprecated `--api-gateway-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 +154,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`, deprecated `--api-gateway-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 +215,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`, legacy `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-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 +225,8 @@ 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 | +| api-gateway-url (legacy) | DCM_API_GATEWAY_URL | --api-gateway-url | — | Deprecated alias | | 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 +240,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 +254,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_API_GATEWAY_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_API_GATEWAY_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 +392,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 +1222,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 provider client (`control-plane/pkg/sp/client/provider`). Out of scope: SP provider create/update/delete (managed via other flows), SP provider health check. @@ -1241,7 +1235,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 provider client (`github.com/dcm-project/control-plane/pkg/sp/client/provider`) | MUST | | #### Table Output Columns @@ -1330,10 +1324,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 +1353,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 +1361,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 +1414,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 client (`github.com/dcm-project/control-plane/pkg/policy/client`) for all policy operations | MUST | | +| REQ-XC-CLI-020 | The CLI MUST use the generated catalog client (`github.com/dcm-project/control-plane/pkg/catalog/client`) for all catalog operations | MUST | | +| REQ-XC-CLI-025 | The CLI MUST use the generated SP resource client (`github.com/dcm-project/control-plane/pkg/sp/client/resource_manager`) for all SP resource operations | MUST | | +| REQ-XC-CLI-026 | The CLI MUST use the generated SP provider client (`github.com/dcm-project/control-plane/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 client MUST be created with `http://localhost:8080/api/v1alpha1` +- **And** the catalog client MUST be created with `http://localhost:8080/api/v1alpha1` +- **And** the SP resource client MUST be created with `http://localhost:8080/api/v1alpha1` +- **And** the SP provider client MUST be created with `http://localhost:8080/api/v1alpha1` ##### AC-XC-CLI-020: Request timeout @@ -1451,7 +1445,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 +1481,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 +1495,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 +1510,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 +1518,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 +1526,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 +1551,8 @@ 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 | +| (legacy) | DCM_API_GATEWAY_URL | --api-gateway-url | — | 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 +1567,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 +`github.com/dcm-project/control-plane/pkg/{policy,catalog,sp}/client` 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 +1590,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 +1639,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..f6bfabf 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_API_GATEWAY_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_API_GATEWAY_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,8 @@ 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_API_GATEWAY_URL` | `http://legacy:8080` | `ControlPlaneURL` (legacy alias) | | `DCM_OUTPUT_FORMAT` | `json` | `OutputFormat` | | `DCM_TIMEOUT` | `60` | `Timeout` | | `DCM_TLS_CA_CERT` | `/path/ca.pem` | `TLSCACert` | @@ -289,7 +289,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`, deprecated `--api-gateway-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 +1198,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 +1207,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 +1217,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 +1235,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 +1261,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 +1270,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 +1279,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 +1348,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 +1437,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 +1447,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 +1477,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 +1497,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 a645209..caaebd5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ 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 generated clients from `github.com/dcm-project/control-plane/pkg/{policy,catalog,sp}/client` (oapi-codegen generated) as a Go module dependency. ## Build and Development Commands @@ -76,7 +76,7 @@ E2E tests live under `test/e2e/` and use the `e2e` build tag (`//go:build e2e`). 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. -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 `github.com/dcm-project/control-plane/pkg/{policy,catalog,sp}/client`. No hand-written HTTP client code. 3. **Configuration precedence**: CLI flags > environment variables (`DCM_CONTROL_PLANE_URL`, legacy `DCM_API_GATEWAY_URL`, `DCM_OUTPUT_FORMAT`, `DCM_TIMEOUT`, `DCM_CONFIG`) > config file (`~/.dcm/config.yaml`) > built-in defaults. diff --git a/README.md b/README.md index 342609d..afb1eec 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 `github.com/dcm-project/control-plane/pkg/{policy,catalog,sp}/client` (oapi-codegen generated) as Go module dependencies. No hand-written HTTP client is needed. --- @@ -704,25 +698,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 +- `github.com/dcm-project/control-plane/pkg/policy/client` - Policy client +- `github.com/dcm-project/control-plane/pkg/catalog/client` - Catalog client +- `github.com/dcm-project/control-plane/pkg/sp/client/resource_manager` - SP Resource Manager client +- `github.com/dcm-project/control-plane/pkg/sp/client/provider` - SP Provider 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) @@ -731,7 +726,7 @@ 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 @@ -822,7 +817,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 │ @@ -915,7 +910,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. @@ -932,7 +927,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 @@ -953,7 +948,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 | @@ -1072,9 +1067,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) | @@ -1144,7 +1137,7 @@ 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 @@ -1175,11 +1168,11 @@ make test-e2e # Requires DCM_CONTROL_PLANE_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 From 9cfa19dd902d718ffcb7e20bf373d0c1d25a85b2 Mon Sep 17 00:00:00 2001 From: Gloria Ciavarrini <gciavarrini@redhat.com> Date: Wed, 17 Jun 2026 15:49:45 +0200 Subject: [PATCH 04/10] update ai checkpoints Assisted-By: Claude (Anthropic) Signed-off-by: Gloria Ciavarrini <gciavarrini@redhat.com> --- .ai/checkpoints/catalog-instance-rehydrate.md | 13 +++++++------ .ai/checkpoints/sp-resource-show-deleted.md | 10 +++++----- .ai/checkpoints/topic-1-cli-framework.md | 6 +++--- .ai/checkpoints/topic-11-sp-provider-commands.md | 10 +++++----- .ai/checkpoints/topic-2-configuration.md | 2 +- .ai/checkpoints/topic-4-policy-commands.md | 6 +++--- .../topic-5-catalog-service-type-commands.md | 16 ++++++++-------- .ai/checkpoints/topic-9-sp-resource-commands.md | 12 ++++++------ 8 files changed, 38 insertions(+), 37 deletions(-) diff --git a/.ai/checkpoints/catalog-instance-rehydrate.md b/.ai/checkpoints/catalog-instance-rehydrate.md index e47fcdf..26d1077 100644 --- a/.ai/checkpoints/catalog-instance-rehydrate.md +++ b/.ai/checkpoints/catalog-instance-rehydrate.md @@ -14,9 +14,10 @@ an existing catalog item instance. The command sends a POST request to `/api/v1alpha1/catalog-item-instances/{id}:rehydrate` and displays the rehydrated instance in the configured output format. -This feature depends on the rehydration-flow branch of the Catalog Manager fork -(`github.com/ygalblum/dcm-catalog-manager`), integrated via a `replace` -directive in `go.mod`. +This feature uses the rehydrate endpoint on the control-plane catalog API +(`RehydrateCatalogItemInstance` on `github.com/dcm-project/control-plane/pkg/catalog/client`). +Placement runs in-process inside control-plane; the CLI does not call a separate +placement HTTP API. ### Requirements Addressed @@ -41,8 +42,8 @@ directive in `go.mod`. | File | Change | Purpose | |------|--------|---------| -| `go.mod` | Modified | Added `replace` directive to use `github.com/ygalblum/dcm-catalog-manager` fork with rehydration API | -| `go.sum` | Modified | Updated checksums for fork dependency | +| `go.mod` | Modified | Depends on `github.com/dcm-project/control-plane` (catalog client includes rehydrate API) | +| `go.sum` | Modified | Updated checksums for control-plane dependency | | `internal/commands/catalog_instance.go` | Modified | Added `newCatalogInstanceRehydrateCommand()` and registered it in `newCatalogInstanceCommand()` | | `internal/commands/catalog_instance_test.go` | Modified | Added 4 Ginkgo test specs for rehydrate (success, missing arg, not found, server error) | | `.ai/specs/dcm-cli.spec.md` | Modified | Added REQ-CIN-120, REQ-CIN-130, AC-CIN-130/140/150 for rehydrate | @@ -53,7 +54,7 @@ directive in `go.mod`. ## Key Design Decisions -1. **Fork via `replace` directive** — The upstream `catalog-manager` module does not yet have the rehydrate API. A Go module `replace` directive points to the `ygalblum/dcm-catalog-manager` fork's `rehydration-flow` branch, keeping the import paths unchanged throughout the codebase. +1. **Control-plane catalog client** — Rehydrate is part of the catalog OpenAPI in the control-plane monorepo. The CLI calls `RehydrateCatalogItemInstance` on the generated catalog client; no fork or local `replace` directive is needed. 2. **Same patterns as existing instance commands** — The rehydrate command follows the identical structure used by `get`: accepts a positional `INSTANCE_ID`, creates the catalog client, sends the request, and formats the response or error. diff --git a/.ai/checkpoints/sp-resource-show-deleted.md b/.ai/checkpoints/sp-resource-show-deleted.md index 45e7072..f131eee 100644 --- a/.ai/checkpoints/sp-resource-show-deleted.md +++ b/.ai/checkpoints/sp-resource-show-deleted.md @@ -39,9 +39,9 @@ from the response. | File | Change | Purpose | |------|--------|---------| -| `go.mod` / `go.sum` | Modified | Updated `service-provider-manager` dependency from `20260324` to `20260402` for `ShowDeleted` params and `GetInstanceParams` type | -| `internal/commands/helpers.go` | Modified | Updated `spmclient` import path from `pkg/client` to `pkg/client/provider` (upstream package restructuring) | -| `internal/commands/sp_provider.go` | Modified | Updated `spmapi` import path from `api/v1alpha1` to `api/v1alpha1/provider` (upstream package restructuring) | +| `go.mod` / `go.sum` | Modified | Bumped control-plane dependency for `ShowDeleted` on `ListInstancesParams` and `GetInstanceParams` | +| `internal/commands/helpers.go` | Modified | SP provider client import: `control-plane/pkg/sp/client/provider` | +| `internal/commands/sp_provider.go` | Modified | SP provider API types: `control-plane/api/sp/v1alpha1/provider` | | `internal/commands/sp_resource.go` | Modified | Added `spResourceWithDeletedTableDef`, `--show-deleted` flag on list and get, conditional table def selection, `ShowDeleted` param passing, updated `GetInstance` call to include `GetInstanceParams` | | `internal/commands/sp_resource_test.go` | Modified | Added `sampleDeletedSPResourceResponse()` helper and 4 new test specs | | `.ai/specs/dcm-cli.spec.md` | Modified | Added REQ-SPR-035/060/070, AC-SPR-035/036/045/046, updated table output section | @@ -52,9 +52,9 @@ from the response. ## Key Design Decisions -1. **Dependency update required** — The `service-provider-manager` module was updated from `20260324` to `20260402` because the newer version introduces `ShowDeleted` on `ListInstancesParams`, the new `GetInstanceParams` type (with `ShowDeleted`), and changes the `GetInstance` client method signature to accept `*GetInstanceParams`. +1. **Control-plane API update** — `ShowDeleted` on `ListInstancesParams`, the `GetInstanceParams` type (with `ShowDeleted`), and the updated `GetInstance` signature come from the control-plane SP resource manager OpenAPI. The CLI pins `github.com/dcm-project/control-plane` accordingly. -2. **Upstream package restructuring** — The `20260402` version moved provider types and client from `api/v1alpha1` / `pkg/client` to `api/v1alpha1/provider` / `pkg/client/provider` subpackages. Import paths in `helpers.go` and `sp_provider.go` were updated accordingly. +2. **SP client package layout** — Provider and resource manager clients live in separate control-plane packages: `pkg/sp/client/provider` and `pkg/sp/client/resource_manager`, with matching API type paths under `api/sp/v1alpha1/`. 3. **Conditional table definition** — Rather than always showing the `DELETION STATUS` column (which would be empty for most use cases), two table definitions are used: `spResourceTableDef` (default 4 columns) and `spResourceWithDeletedTableDef` (5 columns including `DELETION STATUS`). The flag value selects which definition is passed to the formatter. diff --git a/.ai/checkpoints/topic-1-cli-framework.md b/.ai/checkpoints/topic-1-cli-framework.md index 672355d..3f9746c 100644 --- a/.ai/checkpoints/topic-1-cli-framework.md +++ b/.ai/checkpoints/topic-1-cli-framework.md @@ -18,7 +18,7 @@ Topic 1 implements the foundational CLI structure per spec section 4.1. It provi | REQ-CLI-020 | Root command `dcm` with global flags | Done | | REQ-CLI-030 | Subcommand groups: `policy`, `catalog`, `version` | Done | | REQ-CLI-040 | `catalog` subgroups: `service-type`, `item`, `instance` | Done | -| REQ-CLI-050 | Global flags: `--api-gateway-url`, `--output`/`-o`, `--timeout`, `--config`, `--tls-ca-cert`, `--tls-client-cert`, `--tls-client-key`, `--tls-skip-verify` | Done | +| REQ-CLI-050 | Global flags: `--control-plane-url`, `--output`/`-o`, `--timeout`, `--config`, `--tls-ca-cert`, `--tls-client-cert`, `--tls-client-key`, `--tls-skip-verify` | Done | | REQ-CLI-060 | Exit codes: 0 success, 1 runtime, 2 usage | Done | | REQ-CLI-070 | Entry point in `cmd/dcm/main.go` | Done | @@ -54,7 +54,7 @@ Topic 1 implements the foundational CLI structure per spec section 4.1. It provi | `internal/commands/root_test.go` | Tests for TC-U019 through TC-U023 | | `.gitignore` | Ignores build artifacts, IDE files, OS files, Go test/tool outputs | | `.gitattributes` | Collapses generated files (`go.sum`, `go.mod`) in GitHub diffs | -| `.golangci.yml` | golangci-lint configuration aligned with dcm-catalog-manager | +| `.golangci.yml` | golangci-lint configuration aligned with other DCM Go repos | | `.github/workflows/ci.yaml` | CI workflow for running tests (uses shared workflows) | | `.github/workflows/lint.yaml` | Lint workflow using golangci-lint (uses shared workflows) | | `.github/workflows/check-clean-commits.yaml` | Clean commit check workflow (uses shared workflows) | @@ -73,7 +73,7 @@ Topic 1 implements the foundational CLI structure per spec section 4.1. It provi 5. **Stubs only** — All subcommand `RunE` functions return `nil`. Configuration (Topic 2), output formatting (Topic 3), and actual command logic (Topics 4–8) are not implemented. -6. **golangci-lint** — Configuration aligned with dcm-catalog-manager. Lint issues fixed: unchecked fmt return values, unused parameters, missing package comments, gofumpt formatting. +6. **golangci-lint** — Configuration aligned with other DCM Go repositories. Lint issues fixed: unchecked fmt return values, unused parameters, missing package comments, gofumpt formatting. 7. **GitHub CI** — Three workflows (ci, lint, check-clean-commits) using shared workflows from `dcm-project/shared-workflows`, matching the pattern used by other DCM repositories. diff --git a/.ai/checkpoints/topic-11-sp-provider-commands.md b/.ai/checkpoints/topic-11-sp-provider-commands.md index c25f3e1..f3f0640 100644 --- a/.ai/checkpoints/topic-11-sp-provider-commands.md +++ b/.ai/checkpoints/topic-11-sp-provider-commands.md @@ -9,7 +9,7 @@ ## Scope -Topic 11 implements the `dcm sp provider` command group with read-only subcommands (`list` and `get`) per spec section 4.11. 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`). +Topic 11 implements the `dcm sp provider` command group with read-only subcommands (`list` and `get`) per spec section 4.11. The command lists provider registrations in control-plane (not external Service Provider runtimes). All operations use the generated SP Manager client from `github.com/dcm-project/control-plane/pkg/sp/client/provider`. ### Requirements Addressed @@ -19,7 +19,7 @@ Topic 11 implements the `dcm sp provider` command group with read-only subcomman | REQ-SPP-020 | Display SP providers in configured output format | Done | | REQ-SPP-030 | `dcm sp provider get PROVIDER_ID` | Done | | REQ-SPP-040 | Missing `PROVIDER_ID` → usage error (exit code 2) | Done | -| REQ-SPP-050 | All commands use generated SP Manager client | Done | +| REQ-SPP-050 | All commands use generated SP Manager client from control-plane | Done | ### Tests Implemented (12 specs) @@ -53,13 +53,13 @@ Topic 11 implements the `dcm sp provider` command group with read-only subcomman ## Key Design Decisions -1. **Top-level SP Manager client** — Per REQ-SPP-050, all SP provider operations use the oapi-codegen generated client from `github.com/dcm-project/service-provider-manager/pkg/client` (not the `resource_manager` sub-package). The `newSPProviderClient` function follows the same pattern as `newSPResourceClient`. +1. **SP Manager client from control-plane** — Per REQ-SPP-050, all SP provider operations use the oapi-codegen generated client from `github.com/dcm-project/control-plane/pkg/sp/client/provider` (not the `resource_manager` sub-package). The `newSPProviderClient` function follows the same pattern as `newSPResourceClient`. -2. **Separate API type import** — The SP Manager has its API types in `api/v1alpha1`, imported as `spmapi` for `ListProvidersParams`. +2. **Separate API type import** — SP provider API types live in `github.com/dcm-project/control-plane/api/sp/v1alpha1/provider`, imported as `spmapi` for `ListProvidersParams`. 3. **Table columns** — ID, NAME, SERVICE TYPE, HEALTH, CREATED per spec section 4.11. Fields map to `id`, `name`, `service_type`, `health_status`, `create_time` from the `Provider` type. (Updated 2026-04-22: removed STATUS column after upstream API dropped the `status` field.) -4. **List response uses `providers` field** — The SP Manager's `ProviderList` type uses `providers` for the array and `next_page_token` for pagination. +4. **List response uses `providers` field** — The SP Manager API's `ProviderList` type uses `providers` for the array and `next_page_token` for pagination. 5. **`--type` filter** — The `ListProvidersParams` includes a `Type` field passed as the `type` query parameter, matching REQ-SPP-010. diff --git a/.ai/checkpoints/topic-2-configuration.md b/.ai/checkpoints/topic-2-configuration.md index d941913..117a2af 100644 --- a/.ai/checkpoints/topic-2-configuration.md +++ b/.ai/checkpoints/topic-2-configuration.md @@ -27,7 +27,7 @@ Topic 2 implements configuration management per spec section 4.2. It provides Vi | TC ID | Description | Status | |-------|-------------|--------| -| TC-U001 | Config file loading (`api-gateway-url` from YAML) | Pass | +| TC-U001 | Config file loading (`control-plane-url` from YAML) | Pass | | TC-U002 | Env var overrides config file value | Pass | | TC-U003 | CLI flag overrides env var and config file | Pass | | TC-U004 | Built-in defaults for all 7 config fields | Pass | diff --git a/.ai/checkpoints/topic-4-policy-commands.md b/.ai/checkpoints/topic-4-policy-commands.md index 4038ee5..126f5ff 100644 --- a/.ai/checkpoints/topic-4-policy-commands.md +++ b/.ai/checkpoints/topic-4-policy-commands.md @@ -50,7 +50,7 @@ Topic 4 implements the five policy CRUD commands (create, list, get, update, del | TC-U041 | `policy update` without `--from-file` exits code 2 | Pass | | TC-U062 | `policy delete POLICY_ID` sends DELETE, returns success message | Pass | | TC-U063 | `policy delete` without ID exits code 2 | Pass | -| TC-U080 | Connection refused returns user-friendly error with gateway URL | Pass | +| TC-U080 | Connection refused returns user-friendly error with control plane URL | Pass | | TC-U081 | Request timeout returns timeout-specific error message | Pass | | TC-U082 | 404 RFC 7807 error formatted to stderr, exit code 1 | Pass | | TC-U083 | Non-RFC-7807 error body returns `HTTP <status>: <body>` | Pass | @@ -72,7 +72,7 @@ Topic 4 implements the five policy CRUD commands (create, list, get, update, del | File | Change | Purpose | |------|--------|---------| | `internal/commands/helpers.go` | Created | Shared utilities: `FormattedError`, `newFormatter`, `buildHTTPClient`, `apiBaseURL`, `parseInputFile`, `parseInputFileAs` (generic typed variant), `handleErrorResponse`, `requestContext`, `connectionError`, `isTimeoutError`, `stringifyValue` | -| `internal/commands/policy.go` | Modified | Full CRUD implementation using generated policy-manager client | +| `internal/commands/policy.go` | Modified | Full CRUD implementation using generated control-plane policy client | | `internal/commands/root.go` | Modified | Added `FormattedError` handling in `Execute()`, `requiredFlagsPreRun` hook | | `internal/commands/policy_test.go` | Created | 32 Ginkgo test specs with httptest-based mocking | @@ -80,7 +80,7 @@ Topic 4 implements the five policy CRUD commands (create, list, get, update, del ## Key Design Decisions -1. **Generated client from policy-manager** — Per REQ-XC-CLI-010, all policy operations use the oapi-codegen generated client from `github.com/dcm-project/policy-manager/pkg/client`. Client is instantiated via `policyclient.NewClient(apiBaseURL(cfg), policyclient.WithHTTPClient(buildHTTPClient(cfg)))`. Create and update commands use typed client methods (`CreatePolicy`, `UpdatePolicyWithApplicationMergePatchPlusJSONBody`) with typed request bodies (`CreatePolicyJSONRequestBody`, `UpdatePolicyApplicationMergePatchPlusJSONRequestBody`) for client-side payload validation against the generated schema. +1. **Generated client from control-plane** — Per REQ-XC-CLI-010, all policy operations use the oapi-codegen generated client from `github.com/dcm-project/control-plane/pkg/policy/client`. Client is instantiated via `policyclient.NewClient(apiBaseURL(cfg), policyclient.WithHTTPClient(buildHTTPClient(cfg)))`. Create and update commands use typed client methods (`CreatePolicy`, `UpdatePolicyWithApplicationMergePatchPlusJSONBody`) with typed request bodies (`CreatePolicyJSONRequestBody`, `UpdatePolicyApplicationMergePatchPlusJSONRequestBody`) for client-side payload validation against the generated schema. 2. **Shared helpers in `helpers.go`** — HTTP client construction, request context with timeout, error handling, input file parsing, and table cell extraction are shared across all command groups. Extracted into `helpers.go` so Topics 5-7 reuse them without duplication. diff --git a/.ai/checkpoints/topic-5-catalog-service-type-commands.md b/.ai/checkpoints/topic-5-catalog-service-type-commands.md index b7ce74e..e766acd 100644 --- a/.ai/checkpoints/topic-5-catalog-service-type-commands.md +++ b/.ai/checkpoints/topic-5-catalog-service-type-commands.md @@ -10,7 +10,7 @@ ## Scope -Topic 5 implements the `dcm catalog service-type` command group with read-only subcommands (`list` and `get`) per spec section 4.5. Service types are managed by the Catalog Manager and are not user-creatable via the CLI. All commands use the generated Catalog Manager client. +Topic 5 implements the `dcm catalog service-type` command group with read-only subcommands (`list` and `get`) per spec section 4.5. Service types are part of the catalog API in control-plane and are not user-creatable via the CLI. All commands use the generated catalog client from the control-plane monorepo. ### Requirements Addressed @@ -20,7 +20,7 @@ Topic 5 implements the `dcm catalog service-type` command group with read-only s | REQ-CST-020 | Display service types in configured output format | Done | | REQ-CST-030 | `dcm catalog service-type get SERVICE_TYPE_ID` | Done | | REQ-CST-040 | Missing `SERVICE_TYPE_ID` → usage error (exit code 2) | Done | -| REQ-CST-050 | All commands use generated Catalog Manager client | Done | +| REQ-CST-050 | All commands use generated catalog client from control-plane | Done | ### Tests Implemented (9 specs) @@ -42,9 +42,9 @@ Topic 5 implements the `dcm catalog service-type` command group with read-only s | File | Change | Purpose | |------|--------|---------| -| `go.mod` / `go.sum` | Modified | Added `github.com/dcm-project/catalog-manager` dependency | +| `go.mod` / `go.sum` | Modified | Added `github.com/dcm-project/control-plane` dependency (catalog client) | | `internal/commands/helpers.go` | Modified | Added `newCatalogClient` for reuse by Topics 5-7 | -| `internal/commands/catalog_service_type.go` | Modified | Full implementation of `list` and `get` commands with generated Catalog Manager client | +| `internal/commands/catalog_service_type.go` | Modified | Full implementation of `list` and `get` commands with generated catalog client | | `internal/commands/catalog_service_type_test.go` | Created | 9 Ginkgo test specs with httptest-based mocking | | `internal/commands/helpers_test.go` | Modified | Fixed pre-existing lint issues (gofumpt `0600`→`0o600`, prealloc capacity hint) | @@ -52,15 +52,15 @@ Topic 5 implements the `dcm catalog service-type` command group with read-only s ## Key Design Decisions -1. **Generated client from catalog-manager** — Per REQ-CST-050, all service-type operations use the oapi-codegen generated client from `github.com/dcm-project/catalog-manager/pkg/client`. The `newCatalogClient` function follows the same pattern as `newPolicyClient`, using `catalogclient.NewClient(apiBaseURL(cfg), catalogclient.WithHTTPClient(httpClient))`. +1. **Generated client from control-plane** — Per REQ-CST-050, all service-type operations use the oapi-codegen generated client from `github.com/dcm-project/control-plane/pkg/catalog/client`. The `newCatalogClient` function follows the same pattern as `newPolicyClient`, using `catalogclient.NewClient(apiBaseURL(cfg), catalogclient.WithHTTPClient(httpClient))`. -2. **Separate API type import** — The catalog-manager client uses dot-import for its API types internally, but these are not re-exported. The command file imports both `catalogapi` (for `ListServiceTypesParams`) and `catalogclient` (for client construction). +2. **Separate API type import** — The catalog client uses dot-import for its API types internally, but these are not re-exported. The command file imports both `catalogapi` (from `github.com/dcm-project/control-plane/api/catalog/v1alpha1`, for `ListServiceTypesParams`) and `catalogclient` (for client construction). 3. **Table columns** — The spec does not define specific table columns for service types. Columns were chosen based on the ServiceType model fields: UID, SERVICE TYPE, API VERSION, CREATED. The `path` field was not included as a separate ID column since UID already serves as the unique identifier. -4. **List response uses `results` field** — Unlike the policy list which uses a `policies` field, the Catalog Manager's `ServiceTypeList` type uses `results` for the array of service types and `next_page_token` for pagination. +4. **List response uses `results` field** — Unlike the policy list which uses a `policies` field, the catalog API's `ServiceTypeList` type uses `results` for the array of service types and `next_page_token` for pagination. -5. **Reusable `newCatalogClient` in `helpers.go`** — The catalog client constructor lives in `helpers.go` alongside `newPolicyClient` for reuse by Topics 6 (catalog item) and 7 (catalog instance) since all catalog operations go through the same Catalog Manager. +5. **Reusable `newCatalogClient` in `helpers.go`** — The catalog client constructor lives in `helpers.go` alongside `newPolicyClient` for reuse by Topics 6 (catalog item) and 7 (catalog instance) since all catalog operations use the same control-plane catalog client. --- diff --git a/.ai/checkpoints/topic-9-sp-resource-commands.md b/.ai/checkpoints/topic-9-sp-resource-commands.md index a1fe8f5..ba47b20 100644 --- a/.ai/checkpoints/topic-9-sp-resource-commands.md +++ b/.ai/checkpoints/topic-9-sp-resource-commands.md @@ -9,7 +9,7 @@ ## Scope -Topic 9 implements the `dcm sp resource` command group with read-only subcommands (`list` and `get`) per spec section 4.9. SP resources are service type instances managed by the Service Provider Resource Manager (SPRM). The CLI provides read-only access to these resources. All commands use the generated SP Resource Manager client. +Topic 9 implements the `dcm sp resource` command group with read-only subcommands (`list` and `get`) per spec section 4.9. SP resources are service type instances exposed by the SP Resource Manager API in control-plane. The CLI provides read-only access to these resources via the generated SP resource manager client. ### Requirements Addressed @@ -42,7 +42,7 @@ Topic 9 implements the `dcm sp resource` command group with read-only subcommand | File | Change | Purpose | |------|--------|---------| -| `go.mod` / `go.sum` | Modified | Added `github.com/dcm-project/service-provider-manager` dependency | +| `go.mod` / `go.sum` | Modified | Added `github.com/dcm-project/control-plane` dependency (SP resource manager client) | | `internal/commands/helpers.go` | Modified | Added `newSPResourceClient` using the resource_manager client package | | `internal/commands/sp.go` | Created | `dcm sp` parent command group | | `internal/commands/sp_resource.go` | Created | `list` and `get` commands with generated SP Resource Manager client | @@ -55,15 +55,15 @@ Topic 9 implements the `dcm sp resource` command group with read-only subcommand ## Key Design Decisions -1. **Generated client from service-provider-manager** — Per REQ-SPR-050, all SP resource operations use the oapi-codegen generated client from `github.com/dcm-project/service-provider-manager/pkg/client/resource_manager`. The `newSPResourceClient` function follows the same pattern as `newPolicyClient` and `newCatalogClient`, using `sprmclient.NewClient(apiBaseURL(cfg), sprmclient.WithHTTPClient(httpClient))`. +1. **Generated client from control-plane** — Per REQ-SPR-050, all SP resource operations use the oapi-codegen generated client from `github.com/dcm-project/control-plane/pkg/sp/client/resource_manager`. The `newSPResourceClient` function follows the same pattern as `newPolicyClient` and `newCatalogClient`, using `sprmclient.NewClient(apiBaseURL(cfg), sprmclient.WithHTTPClient(httpClient))`. -2. **Separate API type import** — The SP resource manager has its API types in a separate package (`api/v1alpha1/resource_manager`), imported as `sprmapi` for `ListInstancesParams`. +2. **Separate API type import** — SP resource API types live in `github.com/dcm-project/control-plane/api/sp/v1alpha1/resource_manager`, imported as `sprmapi` for `ListInstancesParams`. 3. **Table columns** — ID, PROVIDER, STATUS, CREATED per spec section 4.9. Fields map to `id`, `provider_name`, `status`, `create_time` from the `ServiceTypeInstance` type. -4. **List response uses `instances` field** — Unlike the catalog manager which uses `results`, the SP Resource Manager's `ServiceTypeInstanceList` type uses `instances` for the array and `next_page_token` for pagination. The formatter re-wraps this as `results` for consistent JSON/YAML output. +4. **List response uses `instances` field** — Unlike the catalog API which uses `results`, the SP resource manager's `ServiceTypeInstanceList` type uses `instances` for the array and `next_page_token` for pagination. The formatter re-wraps this as `results` for consistent JSON/YAML output. -5. **`MaxPageSize` type difference** — The SP Resource Manager uses `*int` for `MaxPageSize` (not `*int32` like the Catalog Manager), so the `--page-size` flag value is converted from `int32` to `int`. +5. **`MaxPageSize` type difference** — The SP resource manager client uses `*int` for `MaxPageSize` (not `*int32` like the catalog client), so the `--page-size` flag value is converted from `int32` to `int`. 6. **`--provider` filter** — The `ListInstancesParams` includes a `Provider` field passed as the `provider` query parameter, matching the spec's REQ-SPR-010. From ca26527ab87d55ce74e122e097cc9683d8f8d393 Mon Sep 17 00:00:00 2001 From: Gloria Ciavarrini <gciavarrini@redhat.com> Date: Wed, 17 Jun 2026 15:55:55 +0200 Subject: [PATCH 05/10] Remove legacy api-gateway aliases, wire URL via viper. Assisted-By: Claude (Anthropic) Signed-off-by: Gloria Ciavarrini <gciavarrini@redhat.com> --- internal/commands/policy_test.go | 1 - internal/commands/root.go | 1 - internal/config/config.go | 50 ++++++++++---------------------- internal/config/config_test.go | 26 ----------------- 4 files changed, 15 insertions(+), 63 deletions(-) diff --git a/internal/commands/policy_test.go b/internal/commands/policy_test.go index 3cda57b..ff1fd2d 100644 --- a/internal/commands/policy_test.go +++ b/internal/commands/policy_test.go @@ -21,7 +21,6 @@ import ( func clearDCMEnvVars() { for _, env := range []string{ "DCM_CONTROL_PLANE_URL", - "DCM_API_GATEWAY_URL", "DCM_OUTPUT_FORMAT", "DCM_TIMEOUT", "DCM_CONFIG", diff --git a/internal/commands/root.go b/internal/commands/root.go index b2b4619..1098666 100644 --- a/internal/commands/root.go +++ b/internal/commands/root.go @@ -42,7 +42,6 @@ func NewRootCommand() *cobra.Command { flags := cmd.PersistentFlags() flags.String("control-plane-url", "http://localhost:8080", "Control plane API URL") - flags.String("api-gateway-url", "http://localhost:8080", "Deprecated: use --control-plane-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)") diff --git a/internal/config/config.go b/internal/config/config.go index 5dd0694..c9fc49f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -21,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 @@ -48,6 +48,8 @@ type Config struct { func Load(cmd *cobra.Command) (*Config, error) { v := viper.New() + // Built-in defaults (REQ-CFG-050) + v.SetDefault("control-plane-url", defaultControlPlaneURL) v.SetDefault("output-format", "table") v.SetDefault("timeout", 30) v.SetDefault("tls-ca-cert", "") @@ -55,7 +57,9 @@ func Load(cmd *cobra.Command) (*Config, error) { v.SetDefault("tls-client-key", "") v.SetDefault("tls-skip-verify", false) + // Environment variable binding (REQ-CFG-030) v.SetEnvPrefix("DCM") + 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") @@ -63,6 +67,7 @@ func Load(cmd *cobra.Command) (*Config, error) { v.MustBindEnv("tls-client-key", "DCM_TLS_CLIENT_KEY") v.MustBindEnv("tls-skip-verify", "DCM_TLS_SKIP_VERIFY") + // Config file path (REQ-CFG-010, REQ-CFG-020) configPath := configFilePath(cmd) if configPath != "" { v.SetConfigFile(configPath) @@ -74,6 +79,7 @@ func Load(cmd *cobra.Command) (*Config, error) { v.SetConfigFile(filepath.Join(home, ".dcm", "config.yaml")) } + // Read config file — ignore "not found" errors (REQ-CFG-070) if err := v.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); !ok { if !os.IsNotExist(err) { @@ -82,6 +88,7 @@ func Load(cmd *cobra.Command) (*Config, error) { } } + // Bind CLI flags so they override env vars and config file (REQ-CFG-040) if cmd != nil { if err := bindFlags(v, cmd); err != nil { return nil, err @@ -93,37 +100,9 @@ func Load(cmd *cobra.Command) (*Config, error) { return nil, fmt.Errorf("unmarshalling config: %w", err) } - cfg.ControlPlaneURL = resolveControlPlaneURL(v, cmd) return &cfg, nil } -func resolveControlPlaneURL(v *viper.Viper, cmd *cobra.Command) string { - if cmd != nil { - if f := cmd.Root().PersistentFlags().Lookup("control-plane-url"); f != nil && f.Changed { - return f.Value.String() - } - if f := cmd.Root().PersistentFlags().Lookup("api-gateway-url"); f != nil && f.Changed { - return f.Value.String() - } - } - - if u := os.Getenv("DCM_CONTROL_PLANE_URL"); u != "" { - return u - } - if u := os.Getenv("DCM_API_GATEWAY_URL"); u != "" { - return u - } - - if v.InConfig("control-plane-url") { - return v.GetString("control-plane-url") - } - if v.InConfig("api-gateway-url") { - return v.GetString("api-gateway-url") - } - - return defaultControlPlaneURL -} - // configFilePath resolves the config file path from the --config flag // or the DCM_CONFIG environment variable. func configFilePath(cmd *cobra.Command) string { @@ -143,12 +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{ - "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 f912680..032b2f0 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -14,7 +14,6 @@ import ( func clearDCMEnvVars() { envVars := []string{ "DCM_CONTROL_PLANE_URL", - "DCM_API_GATEWAY_URL", "DCM_OUTPUT_FORMAT", "DCM_TIMEOUT", "DCM_CONFIG", @@ -59,12 +58,6 @@ var _ = Describe("Configuration", func() { cfg := loadConfig("--config", cfgPath, "version") Expect(cfg.ControlPlaneURL).To(Equal("http://custom:8080")) }) - - It("should still load legacy api-gateway-url from config file", func() { - cfgPath := writeConfigFile("api-gateway-url: http://legacy:9080\n") - cfg := loadConfig("--config", cfgPath, "version") - Expect(cfg.ControlPlaneURL).To(Equal("http://legacy:9080")) - }) }) Describe("TC-U002: Env var overrides config file", func() { @@ -74,13 +67,6 @@ var _ = Describe("Configuration", func() { cfg := loadConfig("--config", cfgPath, "version") Expect(cfg.ControlPlaneURL).To(Equal("http://env:8080")) }) - - It("should still use legacy DCM_API_GATEWAY_URL over config file value", func() { - cfgPath := writeConfigFile("control-plane-url: http://file:8080\n") - GinkgoT().Setenv("DCM_API_GATEWAY_URL", "http://legacy-env:9080") - cfg := loadConfig("--config", cfgPath, "version") - Expect(cfg.ControlPlaneURL).To(Equal("http://legacy-env:9080")) - }) }) Describe("TC-U003: CLI flag overrides env var and config file", func() { @@ -94,17 +80,6 @@ var _ = Describe("Configuration", func() { ) Expect(cfg.ControlPlaneURL).To(Equal("http://flag:8080")) }) - - It("should still accept deprecated --api-gateway-url flag", func() { - cfgPath := writeConfigFile("control-plane-url: http://file:8080\n") - GinkgoT().Setenv("DCM_CONTROL_PLANE_URL", "http://env:8080") - cfg := loadConfig( - "--config", cfgPath, - "--api-gateway-url", "http://legacy-flag:9080", - "version", - ) - Expect(cfg.ControlPlaneURL).To(Equal("http://legacy-flag:9080")) - }) }) Describe("TC-U004: Built-in defaults", func() { @@ -171,7 +146,6 @@ var _ = Describe("Configuration", func() { } }, Entry("DCM_CONTROL_PLANE_URL", "DCM_CONTROL_PLANE_URL", "http://cp:8080", "ControlPlaneURL", "http://cp:8080"), - Entry("DCM_API_GATEWAY_URL", "DCM_API_GATEWAY_URL", "http://legacy:9080", "ControlPlaneURL", "http://legacy:9080"), 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"), From f98549bd26bffdfb2bbf0a729edd8a2239be21b7 Mon Sep 17 00:00:00 2001 From: Gloria Ciavarrini <gciavarrini@redhat.com> Date: Wed, 17 Jun 2026 15:55:59 +0200 Subject: [PATCH 06/10] Sync docs and specs after api-gateway alias removal. Assisted-By: Claude (Anthropic) Signed-off-by: Gloria Ciavarrini <gciavarrini@redhat.com> --- .ai/specs/dcm-cli.spec.md | 12 +++++------- .ai/test-plans/dcm-cli-unit.test-plan.md | 7 +++---- CLAUDE.md | 6 ++++-- README.md | 2 -- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/.ai/specs/dcm-cli.spec.md b/.ai/specs/dcm-cli.spec.md index e6d9d14..c23d1aa 100644 --- a/.ai/specs/dcm-cli.spec.md +++ b/.ai/specs/dcm-cli.spec.md @@ -143,7 +143,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 `--control-plane-url`, deprecated `--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 | | @@ -154,7 +154,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 `--control-plane-url`, deprecated `--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 @@ -215,7 +215,7 @@ 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_CONTROL_PLANE_URL`, legacy `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: `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 | | @@ -226,7 +226,6 @@ profile/context support. | Config Key | Env Var | Flag | Default | Description | |------------|---------|------|---------|-------------| | control-plane-url | DCM_CONTROL_PLANE_URL | --control-plane-url | http://localhost:8080 | Control plane base URL | -| api-gateway-url (legacy) | DCM_API_GATEWAY_URL | --api-gateway-url | — | Deprecated alias | | 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 | @@ -254,7 +253,7 @@ profile/context support. ##### AC-CFG-030: Environment variable override - **Validates:** REQ-CFG-030 -- **Given** `DCM_API_GATEWAY_URL=http://env:8080` is set +- **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` @@ -262,7 +261,7 @@ profile/context support. ##### AC-CFG-040: CLI flag override - **Validates:** REQ-CFG-040 -- **Given** `DCM_API_GATEWAY_URL=http://env:8080` is set +- **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` @@ -1552,7 +1551,6 @@ Formatting). | Config Key | Env Var | Flag | Default | Required | Topic | |------------|---------|------|---------|----------|-------| | control-plane-url | DCM_CONTROL_PLANE_URL | --control-plane-url | http://localhost:8080 | No | 2 | -| (legacy) | DCM_API_GATEWAY_URL | --api-gateway-url | — | 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 | diff --git a/.ai/test-plans/dcm-cli-unit.test-plan.md b/.ai/test-plans/dcm-cli-unit.test-plan.md index f6bfabf..45cca30 100644 --- a/.ai/test-plans/dcm-cli-unit.test-plan.md +++ b/.ai/test-plans/dcm-cli-unit.test-plan.md @@ -46,7 +46,7 @@ test classes. Instead: - **Requirement:** REQ-CFG-030, REQ-CFG-040 - **Acceptance Criteria:** AC-CFG-030, AC-CFG-040 - **Type:** Unit -- **Given:** A config file has `control-plane-url: http://file:8080` AND `DCM_API_GATEWAY_URL=http://env:8080` is set +- **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"` @@ -55,7 +55,7 @@ test classes. Instead: - **Requirement:** REQ-CFG-040 - **Acceptance Criteria:** AC-CFG-040 - **Type:** Unit -- **Given:** `DCM_API_GATEWAY_URL=http://env:8080` is set AND config file has `control-plane-url: http://file:8080` +- **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"` @@ -105,7 +105,6 @@ test classes. Instead: | Environment Variable | Value | Expected Config Field | |------------------------|------------|-----------------------| | `DCM_CONTROL_PLANE_URL` | `http://e:8080` | `ControlPlaneURL` | - | `DCM_API_GATEWAY_URL` | `http://legacy:8080` | `ControlPlaneURL` (legacy alias) | | `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 `--control-plane-url`, deprecated `--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 diff --git a/CLAUDE.md b/CLAUDE.md index caaebd5..3989df3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -70,7 +70,7 @@ 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_CONTROL_PLANE_URL` (or legacy `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 @@ -78,7 +78,7 @@ E2E tests live under `test/e2e/` and use the `e2e` build tag (`//go:build e2e`). 2. **Generated clients**: Import `github.com/dcm-project/control-plane/pkg/{policy,catalog,sp}/client`. No hand-written HTTP client code. -3. **Configuration precedence**: CLI flags > environment variables (`DCM_CONTROL_PLANE_URL`, legacy `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 +89,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 all of: `README.md`, `CLAUDE.md`, `.ai/specs/`, `.ai/test-plans/`, and relevant `.ai/checkpoints/`. diff --git a/README.md b/README.md index afb1eec..735b1f3 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,6 @@ tls-skip-verify: false | Variable | Description | Default | |----------|-------------|---------| | `DCM_CONTROL_PLANE_URL` | Control plane API base URL | `http://localhost:8080` | -| `DCM_API_GATEWAY_URL` | Deprecated alias for `DCM_CONTROL_PLANE_URL` | — | | `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` | @@ -125,7 +124,6 @@ These flags are available on all commands: | Flag | Short | Description | |------|-------|-------------| | `--control-plane-url` | | Control plane API URL | -| `--api-gateway-url` | | Deprecated; use `--control-plane-url` | | `--output` | `-o` | Output format: `table`, `json`, `yaml` | | `--timeout` | | Request timeout in seconds | | `--config` | | Path to config file | From d39de16f0161428f51a84a8baa8cddb8474f0c1a Mon Sep 17 00:00:00 2001 From: Gloria Ciavarrini <gciavarrini@redhat.com> Date: Wed, 17 Jun 2026 16:02:43 +0200 Subject: [PATCH 07/10] Revert loadConfig test helper to inline setup. Assisted-By: Claude (Anthropic) Signed-off-by: Gloria Ciavarrini <gciavarrini@redhat.com> --- internal/config/config_test.go | 92 ++++++++++++++++++++++++++-------- 1 file changed, 71 insertions(+), 21 deletions(-) diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 032b2f0..e135f8a 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -11,6 +11,7 @@ import ( "github.com/dcm-project/cli/internal/config" ) +// clearDCMEnvVars removes all DCM_* environment variables to isolate tests. func clearDCMEnvVars() { envVars := []string{ "DCM_CONTROL_PLANE_URL", @@ -27,6 +28,7 @@ func clearDCMEnvVars() { } } +// writeConfigFile creates a temporary config file with the given YAML content. func writeConfigFile(content string) string { dir := GinkgoT().TempDir() path := filepath.Join(dir, "config.yaml") @@ -35,18 +37,6 @@ func writeConfigFile(content string) string { return path } -func loadConfig(args ...string) *config.Config { - cmd := commands.NewRootCommand() - cmd.SetArgs(args) - cmd.SetOut(GinkgoWriter) - cmd.SetErr(GinkgoWriter) - _ = cmd.Execute() - - cfg, err := config.Load(cmd) - Expect(err).NotTo(HaveOccurred()) - return cfg -} - var _ = Describe("Configuration", func() { BeforeEach(func() { clearDCMEnvVars() @@ -55,7 +45,14 @@ var _ = Describe("Configuration", func() { Describe("TC-U001: Config file loading", func() { It("should load control-plane-url from config file", func() { cfgPath := writeConfigFile("control-plane-url: http://custom:8080\n") - cfg := loadConfig("--config", cfgPath, "version") + cmd := commands.NewRootCommand() + cmd.SetArgs([]string{"--config", cfgPath, "version"}) + cmd.SetOut(GinkgoWriter) + cmd.SetErr(GinkgoWriter) + _ = cmd.Execute() + + cfg, err := config.Load(cmd) + Expect(err).NotTo(HaveOccurred()) Expect(cfg.ControlPlaneURL).To(Equal("http://custom:8080")) }) }) @@ -64,7 +61,15 @@ var _ = Describe("Configuration", func() { 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") - cfg := loadConfig("--config", cfgPath, "version") + + cmd := commands.NewRootCommand() + cmd.SetArgs([]string{"--config", cfgPath, "version"}) + cmd.SetOut(GinkgoWriter) + cmd.SetErr(GinkgoWriter) + _ = cmd.Execute() + + cfg, err := config.Load(cmd) + Expect(err).NotTo(HaveOccurred()) Expect(cfg.ControlPlaneURL).To(Equal("http://env:8080")) }) }) @@ -73,11 +78,19 @@ var _ = Describe("Configuration", func() { 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") - cfg := loadConfig( + + cmd := commands.NewRootCommand() + cmd.SetArgs([]string{ "--config", cfgPath, "--control-plane-url", "http://flag:8080", "version", - ) + }) + cmd.SetOut(GinkgoWriter) + cmd.SetErr(GinkgoWriter) + _ = cmd.Execute() + + cfg, err := config.Load(cmd) + Expect(err).NotTo(HaveOccurred()) Expect(cfg.ControlPlaneURL).To(Equal("http://flag:8080")) }) }) @@ -85,7 +98,14 @@ var _ = Describe("Configuration", func() { 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") - cfg := loadConfig("--config", cfgPath, "version") + cmd := commands.NewRootCommand() + cmd.SetArgs([]string{"--config", cfgPath, "version"}) + cmd.SetOut(GinkgoWriter) + cmd.SetErr(GinkgoWriter) + _ = cmd.Execute() + + cfg, err := config.Load(cmd) + Expect(err).NotTo(HaveOccurred()) Expect(cfg.ControlPlaneURL).To(Equal("http://localhost:8080")) Expect(cfg.OutputFormat).To(Equal("table")) Expect(cfg.Timeout).To(Equal(30)) @@ -99,7 +119,14 @@ var _ = Describe("Configuration", func() { 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") - cfg := loadConfig("--config", cfgPath, "version") + cmd := commands.NewRootCommand() + cmd.SetArgs([]string{"--config", cfgPath, "version"}) + cmd.SetOut(GinkgoWriter) + cmd.SetErr(GinkgoWriter) + _ = cmd.Execute() + + cfg, err := config.Load(cmd) + Expect(err).NotTo(HaveOccurred()) Expect(cfg.ControlPlaneURL).To(Equal("http://localhost:8080")) }) }) @@ -107,7 +134,14 @@ var _ = Describe("Configuration", func() { Describe("TC-U006: Custom config file via --config", func() { It("should load configuration from a custom file path", func() { cfgPath := writeConfigFile("timeout: 60\n") - cfg := loadConfig("--config", cfgPath, "version") + cmd := commands.NewRootCommand() + cmd.SetArgs([]string{"--config", cfgPath, "version"}) + cmd.SetOut(GinkgoWriter) + cmd.SetErr(GinkgoWriter) + _ = cmd.Execute() + + cfg, err := config.Load(cmd) + Expect(err).NotTo(HaveOccurred()) Expect(cfg.Timeout).To(Equal(60)) }) }) @@ -116,7 +150,15 @@ var _ = Describe("Configuration", func() { It("should load configuration from DCM_CONFIG path", func() { cfgPath := writeConfigFile("timeout: 45\n") GinkgoT().Setenv("DCM_CONFIG", cfgPath) - cfg := loadConfig("version") + + cmd := commands.NewRootCommand() + cmd.SetArgs([]string{"version"}) + cmd.SetOut(GinkgoWriter) + cmd.SetErr(GinkgoWriter) + _ = cmd.Execute() + + cfg, err := config.Load(cmd) + Expect(err).NotTo(HaveOccurred()) Expect(cfg.Timeout).To(Equal(45)) }) }) @@ -126,7 +168,15 @@ var _ = Describe("Configuration", func() { func(envVar, envValue, configField string, expected any) { cfgPath := filepath.Join(GinkgoT().TempDir(), "nonexistent.yaml") GinkgoT().Setenv(envVar, envValue) - cfg := loadConfig("--config", cfgPath, "version") + + cmd := commands.NewRootCommand() + cmd.SetArgs([]string{"--config", cfgPath, "version"}) + cmd.SetOut(GinkgoWriter) + cmd.SetErr(GinkgoWriter) + _ = cmd.Execute() + + cfg, err := config.Load(cmd) + Expect(err).NotTo(HaveOccurred()) switch configField { case "ControlPlaneURL": From 64e4ca30dae5faefdd4f754a5a44da22d82c30ec Mon Sep 17 00:00:00 2001 From: Gloria Ciavarrini <gciavarrini@redhat.com> Date: Thu, 18 Jun 2026 11:14:20 +0200 Subject: [PATCH 08/10] Revert "update ai checkpoints" This reverts commit 9cfa19dd902d718ffcb7e20bf373d0c1d25a85b2. Signed-off-by: Gloria Ciavarrini <gciavarrini@redhat.com> --- .ai/checkpoints/catalog-instance-rehydrate.md | 13 ++++++------- .ai/checkpoints/sp-resource-show-deleted.md | 10 +++++----- .ai/checkpoints/topic-1-cli-framework.md | 6 +++--- .ai/checkpoints/topic-11-sp-provider-commands.md | 10 +++++----- .ai/checkpoints/topic-2-configuration.md | 2 +- .ai/checkpoints/topic-4-policy-commands.md | 6 +++--- .../topic-5-catalog-service-type-commands.md | 16 ++++++++-------- .ai/checkpoints/topic-9-sp-resource-commands.md | 12 ++++++------ 8 files changed, 37 insertions(+), 38 deletions(-) diff --git a/.ai/checkpoints/catalog-instance-rehydrate.md b/.ai/checkpoints/catalog-instance-rehydrate.md index 26d1077..e47fcdf 100644 --- a/.ai/checkpoints/catalog-instance-rehydrate.md +++ b/.ai/checkpoints/catalog-instance-rehydrate.md @@ -14,10 +14,9 @@ an existing catalog item instance. The command sends a POST request to `/api/v1alpha1/catalog-item-instances/{id}:rehydrate` and displays the rehydrated instance in the configured output format. -This feature uses the rehydrate endpoint on the control-plane catalog API -(`RehydrateCatalogItemInstance` on `github.com/dcm-project/control-plane/pkg/catalog/client`). -Placement runs in-process inside control-plane; the CLI does not call a separate -placement HTTP API. +This feature depends on the rehydration-flow branch of the Catalog Manager fork +(`github.com/ygalblum/dcm-catalog-manager`), integrated via a `replace` +directive in `go.mod`. ### Requirements Addressed @@ -42,8 +41,8 @@ placement HTTP API. | File | Change | Purpose | |------|--------|---------| -| `go.mod` | Modified | Depends on `github.com/dcm-project/control-plane` (catalog client includes rehydrate API) | -| `go.sum` | Modified | Updated checksums for control-plane dependency | +| `go.mod` | Modified | Added `replace` directive to use `github.com/ygalblum/dcm-catalog-manager` fork with rehydration API | +| `go.sum` | Modified | Updated checksums for fork dependency | | `internal/commands/catalog_instance.go` | Modified | Added `newCatalogInstanceRehydrateCommand()` and registered it in `newCatalogInstanceCommand()` | | `internal/commands/catalog_instance_test.go` | Modified | Added 4 Ginkgo test specs for rehydrate (success, missing arg, not found, server error) | | `.ai/specs/dcm-cli.spec.md` | Modified | Added REQ-CIN-120, REQ-CIN-130, AC-CIN-130/140/150 for rehydrate | @@ -54,7 +53,7 @@ placement HTTP API. ## Key Design Decisions -1. **Control-plane catalog client** — Rehydrate is part of the catalog OpenAPI in the control-plane monorepo. The CLI calls `RehydrateCatalogItemInstance` on the generated catalog client; no fork or local `replace` directive is needed. +1. **Fork via `replace` directive** — The upstream `catalog-manager` module does not yet have the rehydrate API. A Go module `replace` directive points to the `ygalblum/dcm-catalog-manager` fork's `rehydration-flow` branch, keeping the import paths unchanged throughout the codebase. 2. **Same patterns as existing instance commands** — The rehydrate command follows the identical structure used by `get`: accepts a positional `INSTANCE_ID`, creates the catalog client, sends the request, and formats the response or error. diff --git a/.ai/checkpoints/sp-resource-show-deleted.md b/.ai/checkpoints/sp-resource-show-deleted.md index f131eee..45e7072 100644 --- a/.ai/checkpoints/sp-resource-show-deleted.md +++ b/.ai/checkpoints/sp-resource-show-deleted.md @@ -39,9 +39,9 @@ from the response. | File | Change | Purpose | |------|--------|---------| -| `go.mod` / `go.sum` | Modified | Bumped control-plane dependency for `ShowDeleted` on `ListInstancesParams` and `GetInstanceParams` | -| `internal/commands/helpers.go` | Modified | SP provider client import: `control-plane/pkg/sp/client/provider` | -| `internal/commands/sp_provider.go` | Modified | SP provider API types: `control-plane/api/sp/v1alpha1/provider` | +| `go.mod` / `go.sum` | Modified | Updated `service-provider-manager` dependency from `20260324` to `20260402` for `ShowDeleted` params and `GetInstanceParams` type | +| `internal/commands/helpers.go` | Modified | Updated `spmclient` import path from `pkg/client` to `pkg/client/provider` (upstream package restructuring) | +| `internal/commands/sp_provider.go` | Modified | Updated `spmapi` import path from `api/v1alpha1` to `api/v1alpha1/provider` (upstream package restructuring) | | `internal/commands/sp_resource.go` | Modified | Added `spResourceWithDeletedTableDef`, `--show-deleted` flag on list and get, conditional table def selection, `ShowDeleted` param passing, updated `GetInstance` call to include `GetInstanceParams` | | `internal/commands/sp_resource_test.go` | Modified | Added `sampleDeletedSPResourceResponse()` helper and 4 new test specs | | `.ai/specs/dcm-cli.spec.md` | Modified | Added REQ-SPR-035/060/070, AC-SPR-035/036/045/046, updated table output section | @@ -52,9 +52,9 @@ from the response. ## Key Design Decisions -1. **Control-plane API update** — `ShowDeleted` on `ListInstancesParams`, the `GetInstanceParams` type (with `ShowDeleted`), and the updated `GetInstance` signature come from the control-plane SP resource manager OpenAPI. The CLI pins `github.com/dcm-project/control-plane` accordingly. +1. **Dependency update required** — The `service-provider-manager` module was updated from `20260324` to `20260402` because the newer version introduces `ShowDeleted` on `ListInstancesParams`, the new `GetInstanceParams` type (with `ShowDeleted`), and changes the `GetInstance` client method signature to accept `*GetInstanceParams`. -2. **SP client package layout** — Provider and resource manager clients live in separate control-plane packages: `pkg/sp/client/provider` and `pkg/sp/client/resource_manager`, with matching API type paths under `api/sp/v1alpha1/`. +2. **Upstream package restructuring** — The `20260402` version moved provider types and client from `api/v1alpha1` / `pkg/client` to `api/v1alpha1/provider` / `pkg/client/provider` subpackages. Import paths in `helpers.go` and `sp_provider.go` were updated accordingly. 3. **Conditional table definition** — Rather than always showing the `DELETION STATUS` column (which would be empty for most use cases), two table definitions are used: `spResourceTableDef` (default 4 columns) and `spResourceWithDeletedTableDef` (5 columns including `DELETION STATUS`). The flag value selects which definition is passed to the formatter. diff --git a/.ai/checkpoints/topic-1-cli-framework.md b/.ai/checkpoints/topic-1-cli-framework.md index 3f9746c..672355d 100644 --- a/.ai/checkpoints/topic-1-cli-framework.md +++ b/.ai/checkpoints/topic-1-cli-framework.md @@ -18,7 +18,7 @@ Topic 1 implements the foundational CLI structure per spec section 4.1. It provi | REQ-CLI-020 | Root command `dcm` with global flags | Done | | REQ-CLI-030 | Subcommand groups: `policy`, `catalog`, `version` | Done | | REQ-CLI-040 | `catalog` subgroups: `service-type`, `item`, `instance` | Done | -| REQ-CLI-050 | Global flags: `--control-plane-url`, `--output`/`-o`, `--timeout`, `--config`, `--tls-ca-cert`, `--tls-client-cert`, `--tls-client-key`, `--tls-skip-verify` | Done | +| REQ-CLI-050 | Global flags: `--api-gateway-url`, `--output`/`-o`, `--timeout`, `--config`, `--tls-ca-cert`, `--tls-client-cert`, `--tls-client-key`, `--tls-skip-verify` | Done | | REQ-CLI-060 | Exit codes: 0 success, 1 runtime, 2 usage | Done | | REQ-CLI-070 | Entry point in `cmd/dcm/main.go` | Done | @@ -54,7 +54,7 @@ Topic 1 implements the foundational CLI structure per spec section 4.1. It provi | `internal/commands/root_test.go` | Tests for TC-U019 through TC-U023 | | `.gitignore` | Ignores build artifacts, IDE files, OS files, Go test/tool outputs | | `.gitattributes` | Collapses generated files (`go.sum`, `go.mod`) in GitHub diffs | -| `.golangci.yml` | golangci-lint configuration aligned with other DCM Go repos | +| `.golangci.yml` | golangci-lint configuration aligned with dcm-catalog-manager | | `.github/workflows/ci.yaml` | CI workflow for running tests (uses shared workflows) | | `.github/workflows/lint.yaml` | Lint workflow using golangci-lint (uses shared workflows) | | `.github/workflows/check-clean-commits.yaml` | Clean commit check workflow (uses shared workflows) | @@ -73,7 +73,7 @@ Topic 1 implements the foundational CLI structure per spec section 4.1. It provi 5. **Stubs only** — All subcommand `RunE` functions return `nil`. Configuration (Topic 2), output formatting (Topic 3), and actual command logic (Topics 4–8) are not implemented. -6. **golangci-lint** — Configuration aligned with other DCM Go repositories. Lint issues fixed: unchecked fmt return values, unused parameters, missing package comments, gofumpt formatting. +6. **golangci-lint** — Configuration aligned with dcm-catalog-manager. Lint issues fixed: unchecked fmt return values, unused parameters, missing package comments, gofumpt formatting. 7. **GitHub CI** — Three workflows (ci, lint, check-clean-commits) using shared workflows from `dcm-project/shared-workflows`, matching the pattern used by other DCM repositories. diff --git a/.ai/checkpoints/topic-11-sp-provider-commands.md b/.ai/checkpoints/topic-11-sp-provider-commands.md index f3f0640..c25f3e1 100644 --- a/.ai/checkpoints/topic-11-sp-provider-commands.md +++ b/.ai/checkpoints/topic-11-sp-provider-commands.md @@ -9,7 +9,7 @@ ## Scope -Topic 11 implements the `dcm sp provider` command group with read-only subcommands (`list` and `get`) per spec section 4.11. The command lists provider registrations in control-plane (not external Service Provider runtimes). All operations use the generated SP Manager client from `github.com/dcm-project/control-plane/pkg/sp/client/provider`. +Topic 11 implements the `dcm sp provider` command group with read-only subcommands (`list` and `get`) per spec section 4.11. 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`). ### Requirements Addressed @@ -19,7 +19,7 @@ Topic 11 implements the `dcm sp provider` command group with read-only subcomman | REQ-SPP-020 | Display SP providers in configured output format | Done | | REQ-SPP-030 | `dcm sp provider get PROVIDER_ID` | Done | | REQ-SPP-040 | Missing `PROVIDER_ID` → usage error (exit code 2) | Done | -| REQ-SPP-050 | All commands use generated SP Manager client from control-plane | Done | +| REQ-SPP-050 | All commands use generated SP Manager client | Done | ### Tests Implemented (12 specs) @@ -53,13 +53,13 @@ Topic 11 implements the `dcm sp provider` command group with read-only subcomman ## Key Design Decisions -1. **SP Manager client from control-plane** — Per REQ-SPP-050, all SP provider operations use the oapi-codegen generated client from `github.com/dcm-project/control-plane/pkg/sp/client/provider` (not the `resource_manager` sub-package). The `newSPProviderClient` function follows the same pattern as `newSPResourceClient`. +1. **Top-level SP Manager client** — Per REQ-SPP-050, all SP provider operations use the oapi-codegen generated client from `github.com/dcm-project/service-provider-manager/pkg/client` (not the `resource_manager` sub-package). The `newSPProviderClient` function follows the same pattern as `newSPResourceClient`. -2. **Separate API type import** — SP provider API types live in `github.com/dcm-project/control-plane/api/sp/v1alpha1/provider`, imported as `spmapi` for `ListProvidersParams`. +2. **Separate API type import** — The SP Manager has its API types in `api/v1alpha1`, imported as `spmapi` for `ListProvidersParams`. 3. **Table columns** — ID, NAME, SERVICE TYPE, HEALTH, CREATED per spec section 4.11. Fields map to `id`, `name`, `service_type`, `health_status`, `create_time` from the `Provider` type. (Updated 2026-04-22: removed STATUS column after upstream API dropped the `status` field.) -4. **List response uses `providers` field** — The SP Manager API's `ProviderList` type uses `providers` for the array and `next_page_token` for pagination. +4. **List response uses `providers` field** — The SP Manager's `ProviderList` type uses `providers` for the array and `next_page_token` for pagination. 5. **`--type` filter** — The `ListProvidersParams` includes a `Type` field passed as the `type` query parameter, matching REQ-SPP-010. diff --git a/.ai/checkpoints/topic-2-configuration.md b/.ai/checkpoints/topic-2-configuration.md index 117a2af..d941913 100644 --- a/.ai/checkpoints/topic-2-configuration.md +++ b/.ai/checkpoints/topic-2-configuration.md @@ -27,7 +27,7 @@ Topic 2 implements configuration management per spec section 4.2. It provides Vi | TC ID | Description | Status | |-------|-------------|--------| -| TC-U001 | Config file loading (`control-plane-url` from YAML) | Pass | +| TC-U001 | Config file loading (`api-gateway-url` from YAML) | Pass | | TC-U002 | Env var overrides config file value | Pass | | TC-U003 | CLI flag overrides env var and config file | Pass | | TC-U004 | Built-in defaults for all 7 config fields | Pass | diff --git a/.ai/checkpoints/topic-4-policy-commands.md b/.ai/checkpoints/topic-4-policy-commands.md index 126f5ff..4038ee5 100644 --- a/.ai/checkpoints/topic-4-policy-commands.md +++ b/.ai/checkpoints/topic-4-policy-commands.md @@ -50,7 +50,7 @@ Topic 4 implements the five policy CRUD commands (create, list, get, update, del | TC-U041 | `policy update` without `--from-file` exits code 2 | Pass | | TC-U062 | `policy delete POLICY_ID` sends DELETE, returns success message | Pass | | TC-U063 | `policy delete` without ID exits code 2 | Pass | -| TC-U080 | Connection refused returns user-friendly error with control plane URL | Pass | +| TC-U080 | Connection refused returns user-friendly error with gateway URL | Pass | | TC-U081 | Request timeout returns timeout-specific error message | Pass | | TC-U082 | 404 RFC 7807 error formatted to stderr, exit code 1 | Pass | | TC-U083 | Non-RFC-7807 error body returns `HTTP <status>: <body>` | Pass | @@ -72,7 +72,7 @@ Topic 4 implements the five policy CRUD commands (create, list, get, update, del | File | Change | Purpose | |------|--------|---------| | `internal/commands/helpers.go` | Created | Shared utilities: `FormattedError`, `newFormatter`, `buildHTTPClient`, `apiBaseURL`, `parseInputFile`, `parseInputFileAs` (generic typed variant), `handleErrorResponse`, `requestContext`, `connectionError`, `isTimeoutError`, `stringifyValue` | -| `internal/commands/policy.go` | Modified | Full CRUD implementation using generated control-plane policy client | +| `internal/commands/policy.go` | Modified | Full CRUD implementation using generated policy-manager client | | `internal/commands/root.go` | Modified | Added `FormattedError` handling in `Execute()`, `requiredFlagsPreRun` hook | | `internal/commands/policy_test.go` | Created | 32 Ginkgo test specs with httptest-based mocking | @@ -80,7 +80,7 @@ Topic 4 implements the five policy CRUD commands (create, list, get, update, del ## Key Design Decisions -1. **Generated client from control-plane** — Per REQ-XC-CLI-010, all policy operations use the oapi-codegen generated client from `github.com/dcm-project/control-plane/pkg/policy/client`. Client is instantiated via `policyclient.NewClient(apiBaseURL(cfg), policyclient.WithHTTPClient(buildHTTPClient(cfg)))`. Create and update commands use typed client methods (`CreatePolicy`, `UpdatePolicyWithApplicationMergePatchPlusJSONBody`) with typed request bodies (`CreatePolicyJSONRequestBody`, `UpdatePolicyApplicationMergePatchPlusJSONRequestBody`) for client-side payload validation against the generated schema. +1. **Generated client from policy-manager** — Per REQ-XC-CLI-010, all policy operations use the oapi-codegen generated client from `github.com/dcm-project/policy-manager/pkg/client`. Client is instantiated via `policyclient.NewClient(apiBaseURL(cfg), policyclient.WithHTTPClient(buildHTTPClient(cfg)))`. Create and update commands use typed client methods (`CreatePolicy`, `UpdatePolicyWithApplicationMergePatchPlusJSONBody`) with typed request bodies (`CreatePolicyJSONRequestBody`, `UpdatePolicyApplicationMergePatchPlusJSONRequestBody`) for client-side payload validation against the generated schema. 2. **Shared helpers in `helpers.go`** — HTTP client construction, request context with timeout, error handling, input file parsing, and table cell extraction are shared across all command groups. Extracted into `helpers.go` so Topics 5-7 reuse them without duplication. diff --git a/.ai/checkpoints/topic-5-catalog-service-type-commands.md b/.ai/checkpoints/topic-5-catalog-service-type-commands.md index e766acd..b7ce74e 100644 --- a/.ai/checkpoints/topic-5-catalog-service-type-commands.md +++ b/.ai/checkpoints/topic-5-catalog-service-type-commands.md @@ -10,7 +10,7 @@ ## Scope -Topic 5 implements the `dcm catalog service-type` command group with read-only subcommands (`list` and `get`) per spec section 4.5. Service types are part of the catalog API in control-plane and are not user-creatable via the CLI. All commands use the generated catalog client from the control-plane monorepo. +Topic 5 implements the `dcm catalog service-type` command group with read-only subcommands (`list` and `get`) per spec section 4.5. Service types are managed by the Catalog Manager and are not user-creatable via the CLI. All commands use the generated Catalog Manager client. ### Requirements Addressed @@ -20,7 +20,7 @@ Topic 5 implements the `dcm catalog service-type` command group with read-only s | REQ-CST-020 | Display service types in configured output format | Done | | REQ-CST-030 | `dcm catalog service-type get SERVICE_TYPE_ID` | Done | | REQ-CST-040 | Missing `SERVICE_TYPE_ID` → usage error (exit code 2) | Done | -| REQ-CST-050 | All commands use generated catalog client from control-plane | Done | +| REQ-CST-050 | All commands use generated Catalog Manager client | Done | ### Tests Implemented (9 specs) @@ -42,9 +42,9 @@ Topic 5 implements the `dcm catalog service-type` command group with read-only s | File | Change | Purpose | |------|--------|---------| -| `go.mod` / `go.sum` | Modified | Added `github.com/dcm-project/control-plane` dependency (catalog client) | +| `go.mod` / `go.sum` | Modified | Added `github.com/dcm-project/catalog-manager` dependency | | `internal/commands/helpers.go` | Modified | Added `newCatalogClient` for reuse by Topics 5-7 | -| `internal/commands/catalog_service_type.go` | Modified | Full implementation of `list` and `get` commands with generated catalog client | +| `internal/commands/catalog_service_type.go` | Modified | Full implementation of `list` and `get` commands with generated Catalog Manager client | | `internal/commands/catalog_service_type_test.go` | Created | 9 Ginkgo test specs with httptest-based mocking | | `internal/commands/helpers_test.go` | Modified | Fixed pre-existing lint issues (gofumpt `0600`→`0o600`, prealloc capacity hint) | @@ -52,15 +52,15 @@ Topic 5 implements the `dcm catalog service-type` command group with read-only s ## Key Design Decisions -1. **Generated client from control-plane** — Per REQ-CST-050, all service-type operations use the oapi-codegen generated client from `github.com/dcm-project/control-plane/pkg/catalog/client`. The `newCatalogClient` function follows the same pattern as `newPolicyClient`, using `catalogclient.NewClient(apiBaseURL(cfg), catalogclient.WithHTTPClient(httpClient))`. +1. **Generated client from catalog-manager** — Per REQ-CST-050, all service-type operations use the oapi-codegen generated client from `github.com/dcm-project/catalog-manager/pkg/client`. The `newCatalogClient` function follows the same pattern as `newPolicyClient`, using `catalogclient.NewClient(apiBaseURL(cfg), catalogclient.WithHTTPClient(httpClient))`. -2. **Separate API type import** — The catalog client uses dot-import for its API types internally, but these are not re-exported. The command file imports both `catalogapi` (from `github.com/dcm-project/control-plane/api/catalog/v1alpha1`, for `ListServiceTypesParams`) and `catalogclient` (for client construction). +2. **Separate API type import** — The catalog-manager client uses dot-import for its API types internally, but these are not re-exported. The command file imports both `catalogapi` (for `ListServiceTypesParams`) and `catalogclient` (for client construction). 3. **Table columns** — The spec does not define specific table columns for service types. Columns were chosen based on the ServiceType model fields: UID, SERVICE TYPE, API VERSION, CREATED. The `path` field was not included as a separate ID column since UID already serves as the unique identifier. -4. **List response uses `results` field** — Unlike the policy list which uses a `policies` field, the catalog API's `ServiceTypeList` type uses `results` for the array of service types and `next_page_token` for pagination. +4. **List response uses `results` field** — Unlike the policy list which uses a `policies` field, the Catalog Manager's `ServiceTypeList` type uses `results` for the array of service types and `next_page_token` for pagination. -5. **Reusable `newCatalogClient` in `helpers.go`** — The catalog client constructor lives in `helpers.go` alongside `newPolicyClient` for reuse by Topics 6 (catalog item) and 7 (catalog instance) since all catalog operations use the same control-plane catalog client. +5. **Reusable `newCatalogClient` in `helpers.go`** — The catalog client constructor lives in `helpers.go` alongside `newPolicyClient` for reuse by Topics 6 (catalog item) and 7 (catalog instance) since all catalog operations go through the same Catalog Manager. --- diff --git a/.ai/checkpoints/topic-9-sp-resource-commands.md b/.ai/checkpoints/topic-9-sp-resource-commands.md index ba47b20..a1fe8f5 100644 --- a/.ai/checkpoints/topic-9-sp-resource-commands.md +++ b/.ai/checkpoints/topic-9-sp-resource-commands.md @@ -9,7 +9,7 @@ ## Scope -Topic 9 implements the `dcm sp resource` command group with read-only subcommands (`list` and `get`) per spec section 4.9. SP resources are service type instances exposed by the SP Resource Manager API in control-plane. The CLI provides read-only access to these resources via the generated SP resource manager client. +Topic 9 implements the `dcm sp resource` command group with read-only subcommands (`list` and `get`) per spec section 4.9. SP resources are service type instances managed by the Service Provider Resource Manager (SPRM). The CLI provides read-only access to these resources. All commands use the generated SP Resource Manager client. ### Requirements Addressed @@ -42,7 +42,7 @@ Topic 9 implements the `dcm sp resource` command group with read-only subcommand | File | Change | Purpose | |------|--------|---------| -| `go.mod` / `go.sum` | Modified | Added `github.com/dcm-project/control-plane` dependency (SP resource manager client) | +| `go.mod` / `go.sum` | Modified | Added `github.com/dcm-project/service-provider-manager` dependency | | `internal/commands/helpers.go` | Modified | Added `newSPResourceClient` using the resource_manager client package | | `internal/commands/sp.go` | Created | `dcm sp` parent command group | | `internal/commands/sp_resource.go` | Created | `list` and `get` commands with generated SP Resource Manager client | @@ -55,15 +55,15 @@ Topic 9 implements the `dcm sp resource` command group with read-only subcommand ## Key Design Decisions -1. **Generated client from control-plane** — Per REQ-SPR-050, all SP resource operations use the oapi-codegen generated client from `github.com/dcm-project/control-plane/pkg/sp/client/resource_manager`. The `newSPResourceClient` function follows the same pattern as `newPolicyClient` and `newCatalogClient`, using `sprmclient.NewClient(apiBaseURL(cfg), sprmclient.WithHTTPClient(httpClient))`. +1. **Generated client from service-provider-manager** — Per REQ-SPR-050, all SP resource operations use the oapi-codegen generated client from `github.com/dcm-project/service-provider-manager/pkg/client/resource_manager`. The `newSPResourceClient` function follows the same pattern as `newPolicyClient` and `newCatalogClient`, using `sprmclient.NewClient(apiBaseURL(cfg), sprmclient.WithHTTPClient(httpClient))`. -2. **Separate API type import** — SP resource API types live in `github.com/dcm-project/control-plane/api/sp/v1alpha1/resource_manager`, imported as `sprmapi` for `ListInstancesParams`. +2. **Separate API type import** — The SP resource manager has its API types in a separate package (`api/v1alpha1/resource_manager`), imported as `sprmapi` for `ListInstancesParams`. 3. **Table columns** — ID, PROVIDER, STATUS, CREATED per spec section 4.9. Fields map to `id`, `provider_name`, `status`, `create_time` from the `ServiceTypeInstance` type. -4. **List response uses `instances` field** — Unlike the catalog API which uses `results`, the SP resource manager's `ServiceTypeInstanceList` type uses `instances` for the array and `next_page_token` for pagination. The formatter re-wraps this as `results` for consistent JSON/YAML output. +4. **List response uses `instances` field** — Unlike the catalog manager which uses `results`, the SP Resource Manager's `ServiceTypeInstanceList` type uses `instances` for the array and `next_page_token` for pagination. The formatter re-wraps this as `results` for consistent JSON/YAML output. -5. **`MaxPageSize` type difference** — The SP resource manager client uses `*int` for `MaxPageSize` (not `*int32` like the catalog client), so the `--page-size` flag value is converted from `int32` to `int`. +5. **`MaxPageSize` type difference** — The SP Resource Manager uses `*int` for `MaxPageSize` (not `*int32` like the Catalog Manager), so the `--page-size` flag value is converted from `int32` to `int`. 6. **`--provider` filter** — The `ListInstancesParams` includes a `Provider` field passed as the `provider` query parameter, matching the spec's REQ-SPR-010. From 1bf2b04d49204f4c78414444379c6f8f2dc6231d Mon Sep 17 00:00:00 2001 From: Gloria Ciavarrini <gciavarrini@redhat.com> Date: Thu, 18 Jun 2026 16:55:33 +0200 Subject: [PATCH 09/10] Restore Manager client naming in ai spec Assisted-By: Claude (Anthropic) Signed-off-by: Gloria Ciavarrini <gciavarrini@redhat.com> --- .ai/specs/dcm-cli.spec.md | 20 ++++++++++---------- CLAUDE.md | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.ai/specs/dcm-cli.spec.md b/.ai/specs/dcm-cli.spec.md index c23d1aa..856ce8d 100644 --- a/.ai/specs/dcm-cli.spec.md +++ b/.ai/specs/dcm-cli.spec.md @@ -1221,7 +1221,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 provider client (`control-plane/pkg/sp/client/provider`). +generated SP Manager client (`github.com/dcm-project/control-plane/pkg/sp/client/provider`). Out of scope: SP provider create/update/delete (managed via other flows), SP provider health check. @@ -1234,7 +1234,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 provider client (`github.com/dcm-project/control-plane/pkg/sp/client/provider`) | MUST | | +| REQ-SPP-050 | All SP provider commands MUST use the generated SP Manager client (`github.com/dcm-project/control-plane/pkg/sp/client/provider`) | MUST | | #### Table Output Columns @@ -1413,10 +1413,10 @@ Formatting). | ID | Requirement | Priority | Notes | |----|-------------|----------|-------| -| REQ-XC-CLI-010 | The CLI MUST use the generated policy client (`github.com/dcm-project/control-plane/pkg/policy/client`) for all policy operations | MUST | | -| REQ-XC-CLI-020 | The CLI MUST use the generated catalog client (`github.com/dcm-project/control-plane/pkg/catalog/client`) for all catalog operations | MUST | | -| REQ-XC-CLI-025 | The CLI MUST use the generated SP resource client (`github.com/dcm-project/control-plane/pkg/sp/client/resource_manager`) for all SP resource operations | MUST | | -| REQ-XC-CLI-026 | The CLI MUST use the generated SP provider client (`github.com/dcm-project/control-plane/pkg/sp/client/provider`) for all SP provider operations | MUST | | +| REQ-XC-CLI-010 | The CLI MUST use the generated Policy Manager client (`github.com/dcm-project/control-plane/pkg/policy/client`) for all policy operations | MUST | | +| REQ-XC-CLI-020 | The CLI MUST use the generated Catalog Manager client (`github.com/dcm-project/control-plane/pkg/catalog/client`) for all catalog operations | MUST | | +| REQ-XC-CLI-025 | The CLI MUST use the generated SP Resource Manager client (`github.com/dcm-project/control-plane/pkg/sp/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/control-plane/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 control-plane URL uses `https://` | MUST | | @@ -1428,10 +1428,10 @@ Formatting). - **Validates:** REQ-XC-CLI-030 - **Given** the control-plane URL is `http://localhost:8080` - **When** the generated clients are created -- **Then** the policy client MUST be created with `http://localhost:8080/api/v1alpha1` -- **And** the catalog client MUST be created with `http://localhost:8080/api/v1alpha1` -- **And** the SP resource client MUST be created with `http://localhost:8080/api/v1alpha1` -- **And** the SP provider client MUST be created with `http://localhost:8080/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 diff --git a/CLAUDE.md b/CLAUDE.md index 3989df3..54213ec 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -90,4 +90,4 @@ E2E tests live under `test/e2e/` and use the `e2e` build tag (`//go:build e2e`). 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 all of: `README.md`, `CLAUDE.md`, `.ai/specs/`, `.ai/test-plans/`, and relevant `.ai/checkpoints/`. +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. From 6a55a195eeea2ccfa50f76b1fe9bdd6a0d9b6b74 Mon Sep 17 00:00:00 2001 From: Gloria Ciavarrini <gciavarrini@redhat.com> Date: Thu, 18 Jun 2026 17:14:40 +0200 Subject: [PATCH 10/10] Fix control-plane pkg links in docs and spec. Signed-off-by: Gloria Ciavarrini <gciavarrini@redhat.com> --- .ai/specs/dcm-cli.spec.md | 21 ++++++++++++--------- CLAUDE.md | 19 +++++++++++++++---- README.md | 10 +++++----- 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/.ai/specs/dcm-cli.spec.md b/.ai/specs/dcm-cli.spec.md index 856ce8d..02d6fd4 100644 --- a/.ai/specs/dcm-cli.spec.md +++ b/.ai/specs/dcm-cli.spec.md @@ -5,7 +5,10 @@ The 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 generated clients from -`github.com/dcm-project/control-plane/pkg/{policy,catalog,sp}/client` +[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):** @@ -1221,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 (`github.com/dcm-project/control-plane/pkg/sp/client/provider`). +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. @@ -1234,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 (`github.com/dcm-project/control-plane/pkg/sp/client/provider`) | 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 @@ -1413,10 +1416,10 @@ Formatting). | ID | Requirement | Priority | Notes | |----|-------------|----------|-------| -| REQ-XC-CLI-010 | The CLI MUST use the generated Policy Manager client (`github.com/dcm-project/control-plane/pkg/policy/client`) for all policy operations | MUST | | -| REQ-XC-CLI-020 | The CLI MUST use the generated Catalog Manager client (`github.com/dcm-project/control-plane/pkg/catalog/client`) for all catalog operations | MUST | | -| REQ-XC-CLI-025 | The CLI MUST use the generated SP Resource Manager client (`github.com/dcm-project/control-plane/pkg/sp/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/control-plane/pkg/sp/client/provider`) for all SP provider operations | 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 control-plane URL uses `https://` | MUST | | @@ -1566,8 +1569,8 @@ Formatting). ### DD-010: Generated clients over hand-written HTTP **Decision:** Use oapi-codegen generated clients from -`github.com/dcm-project/control-plane/pkg/{policy,catalog,sp}/client` instead of -hand-writing HTTP client code. +[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 diff --git a/CLAUDE.md b/CLAUDE.md index 54213ec..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 directly with the control-plane monolith on port 8080. The CLI uses generated clients from `github.com/dcm-project/control-plane/pkg/{policy,catalog,sp}/client` (oapi-codegen generated) as a Go module dependency. +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 @@ -74,9 +85,9 @@ E2E tests live under `test/e2e/` and use the `e2e` build tag (`//go:build e2e`). ## 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/control-plane/pkg/{policy,catalog,sp}/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_CONTROL_PLANE_URL`, `DCM_OUTPUT_FORMAT`, `DCM_TIMEOUT`, `DCM_CONFIG`) > config file (`~/.dcm/config.yaml`) > built-in defaults. diff --git a/README.md b/README.md index 735b1f3..d9ad034 100644 --- a/README.md +++ b/README.md @@ -75,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 `github.com/dcm-project/control-plane/pkg/{policy,catalog,sp}/client` (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. --- @@ -698,10 +698,10 @@ func Get() Info The CLI imports generated client packages from the control-plane monorepo: -- `github.com/dcm-project/control-plane/pkg/policy/client` - Policy client -- `github.com/dcm-project/control-plane/pkg/catalog/client` - Catalog client -- `github.com/dcm-project/control-plane/pkg/sp/client/resource_manager` - SP Resource Manager client -- `github.com/dcm-project/control-plane/pkg/sp/client/provider` - SP Provider 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: