diff --git a/go.mod b/go.mod index a5f755576c..a0433162fd 100644 --- a/go.mod +++ b/go.mod @@ -62,7 +62,7 @@ require ( github.com/confluentinc/mds-sdk-go-public/mdsv1 v0.0.0-20240923163156-b922b35891f9 github.com/confluentinc/mds-sdk-go-public/mdsv2alpha1 v0.0.0-20240923163156-b922b35891f9 github.com/confluentinc/properties v0.0.0-20190814194548-42c10394a787 - github.com/confluentinc/schema-registry-sdk-go v0.1.1-0.20251021214222-018e0cd35bf9 + github.com/confluentinc/schema-registry-sdk-go v0.1.3-0.20260812213011-262e9f97627b github.com/davecgh/go-spew v1.1.1 github.com/dghubble/sling v1.4.2 github.com/fatih/color v1.17.0 diff --git a/go.sum b/go.sum index fcdefcf035..60d1c937e9 100644 --- a/go.sum +++ b/go.sum @@ -265,6 +265,8 @@ github.com/confluentinc/proto-go-setter v0.3.0 h1:6BhgpDV5rWdEa+6LsnQWGV1udFboCa github.com/confluentinc/proto-go-setter v0.3.0/go.mod h1:WJkmDZf4f/nxGzjYJ+o3AsXI2hk64gLM5XlZrmlzs1s= github.com/confluentinc/schema-registry-sdk-go v0.1.1-0.20251021214222-018e0cd35bf9 h1:8rPUxlx0J0VVIOqF1pO25divMhyPZfD64bq1HDQnyUY= github.com/confluentinc/schema-registry-sdk-go v0.1.1-0.20251021214222-018e0cd35bf9/go.mod h1:yuuBwRHiqXeYbeuiUX8R7HgbquPJh8XTGpW7O08ErmU= +github.com/confluentinc/schema-registry-sdk-go v0.1.3-0.20260812213011-262e9f97627b h1:vfMHWJhsDWlzRTDFugCw1JZm8I6tWK9TABHYnxjvqqw= +github.com/confluentinc/schema-registry-sdk-go v0.1.3-0.20260812213011-262e9f97627b/go.mod h1:p5zwkMP5gyR4mgsoVQFjCI1EZMbYtxhzsJNnflrECPE= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= diff --git a/internal/schema-registry/command_exporter_configuration.go b/internal/schema-registry/command_exporter_configuration.go index c76b9038d2..1af30931be 100644 --- a/internal/schema-registry/command_exporter_configuration.go +++ b/internal/schema-registry/command_exporter_configuration.go @@ -12,6 +12,11 @@ func (c *command) newExporterConfigurationCommand(cfg *config.Config) *cobra.Com Short: "Manage the schema exporter configuration.", } + if cfg.IsCloudLogin() { + cmd.AddCommand(c.newExporterConfigurationClusterLinkCommand()) + } else { + cmd.AddCommand(c.newExporterConfigurationClusterLinkCommandOnPrem()) + } cmd.AddCommand(c.newExporterConfigurationDescribeCommand(cfg)) return cmd diff --git a/internal/schema-registry/command_exporter_configuration_cluster_link.go b/internal/schema-registry/command_exporter_configuration_cluster_link.go new file mode 100644 index 0000000000..073dcfa753 --- /dev/null +++ b/internal/schema-registry/command_exporter_configuration_cluster_link.go @@ -0,0 +1,19 @@ +package schemaregistry + +import ( + "github.com/spf13/cobra" + + pcmd "github.com/confluentinc/cli/v4/pkg/cmd" +) + +func (c *command) newExporterConfigurationClusterLinkCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "cluster-link", + Short: "Manage the schema exporter's Cluster Link config.", + Annotations: map[string]string{pcmd.RunRequirement: pcmd.RequireCloudLogin}, + } + + cmd.AddCommand(c.newExporterConfigurationClusterLinkDescribeCommand()) + + return cmd +} diff --git a/internal/schema-registry/command_exporter_configuration_cluster_link_describe.go b/internal/schema-registry/command_exporter_configuration_cluster_link_describe.go new file mode 100644 index 0000000000..dd1d53eb0d --- /dev/null +++ b/internal/schema-registry/command_exporter_configuration_cluster_link_describe.go @@ -0,0 +1,41 @@ +package schemaregistry + +import ( + "github.com/spf13/cobra" + + pcmd "github.com/confluentinc/cli/v4/pkg/cmd" + "github.com/confluentinc/cli/v4/pkg/output" +) + +func (c *command) newExporterConfigurationClusterLinkDescribeCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "describe ", + Short: "Describe the schema exporter's Cluster Link config.", + Long: "Derive the Cluster Link config(s) that replicate a schema exporter's subject/context translation, so they don't have to be hand-copied.", + Args: cobra.ExactArgs(1), + RunE: c.exporterConfigurationClusterLinkDescribe, + } + + pcmd.AddContextFlag(cmd, c.CLICommand) + pcmd.AddEnvironmentFlag(cmd, c.AuthenticatedCLICommand) + addSchemaRegistryEndpointFlag(cmd) + pcmd.AddOutputFlagWithDefaultValue(cmd, output.JSON.String()) + + return cmd +} + +func (c *command) exporterConfigurationClusterLinkDescribe(cmd *cobra.Command, args []string) error { + client, err := c.GetSchemaRegistryClient(cmd) + if err != nil { + return err + } + + configs, err := client.GetExporterClusterLinkConfig(args[0]) + if err != nil { + return err + } + + table := output.NewTable(cmd) + table.Add(configs) + return table.Print() +} diff --git a/internal/schema-registry/command_exporter_configuration_cluster_link_describe_onprem.go b/internal/schema-registry/command_exporter_configuration_cluster_link_describe_onprem.go new file mode 100644 index 0000000000..bdc88c3b1b --- /dev/null +++ b/internal/schema-registry/command_exporter_configuration_cluster_link_describe_onprem.go @@ -0,0 +1,25 @@ +package schemaregistry + +import ( + "github.com/spf13/cobra" + + pcmd "github.com/confluentinc/cli/v4/pkg/cmd" + "github.com/confluentinc/cli/v4/pkg/output" +) + +func (c *command) newExporterConfigurationClusterLinkDescribeCommandOnPrem() *cobra.Command { + cmd := &cobra.Command{ + Use: "describe ", + Short: "Describe the schema exporter's Cluster Link config.", + Long: "Derive the Cluster Link config(s) that replicate a schema exporter's subject/context translation, so they don't have to be hand-copied.", + Args: cobra.ExactArgs(1), + RunE: c.exporterConfigurationClusterLinkDescribe, + } + + pcmd.AddContextFlag(cmd, c.CLICommand) + addCaLocationAndClientPathFlags(cmd) + addSchemaRegistryEndpointFlag(cmd) + pcmd.AddOutputFlagWithDefaultValue(cmd, output.JSON.String()) + + return cmd +} diff --git a/internal/schema-registry/command_exporter_configuration_cluster_link_onprem.go b/internal/schema-registry/command_exporter_configuration_cluster_link_onprem.go new file mode 100644 index 0000000000..7f25e285d1 --- /dev/null +++ b/internal/schema-registry/command_exporter_configuration_cluster_link_onprem.go @@ -0,0 +1,19 @@ +package schemaregistry + +import ( + "github.com/spf13/cobra" +) + +// No RunRequirement annotation: like its sibling command_exporter_configuration_describe.go, +// this connects directly to a Schema Registry endpoint via --schema-registry-endpoint and +// certificate flags, with no MDS login required. +func (c *command) newExporterConfigurationClusterLinkCommandOnPrem() *cobra.Command { + cmd := &cobra.Command{ + Use: "cluster-link", + Short: "Manage the schema exporter's Cluster Link config.", + } + + cmd.AddCommand(c.newExporterConfigurationClusterLinkDescribeCommandOnPrem()) + + return cmd +} diff --git a/pkg/schemaregistry/client.go b/pkg/schemaregistry/client.go index 15cfe32e73..1a14ef6514 100644 --- a/pkg/schemaregistry/client.go +++ b/pkg/schemaregistry/client.go @@ -124,6 +124,11 @@ func (c *Client) GetExporterConfig(name string) (map[string]string, error) { return res, err } +func (c *Client) GetExporterClusterLinkConfig(name string) (map[string]string, error) { + res, _, err := c.DefaultApi.GetExporterClusterLinkConfig(c.context(), name).Execute() + return res, err +} + func (c *Client) ResumeExporter(name string) (srsdk.UpdateExporterResponse, error) { res, _, err := c.DefaultApi.ResumeExporter(c.context(), name).Execute() return res, err diff --git a/test/fixtures/output/schema-registry/exporter/configuration/cluster-link-describe-json.golden b/test/fixtures/output/schema-registry/exporter/configuration/cluster-link-describe-json.golden new file mode 100644 index 0000000000..9b3ffd325c --- /dev/null +++ b/test/fixtures/output/schema-registry/exporter/configuration/cluster-link-describe-json.golden @@ -0,0 +1,3 @@ +{ + "topic.config.sync.associations.filters": "{\"topicsToInclude\":[\"*\"],\"topicsToExclude\":[],\"contextType\":\"AUTO\",\"subjectRenameFormat\":\"my-${subject}\",\"sourceSRDeployment\":\"CONFLUENT_CLOUD\",\"sourceLSRC\":\"lsrc-abc123\"}" +} diff --git a/test/fixtures/output/schema-registry/exporter/configuration/cluster-link-describe-yaml.golden b/test/fixtures/output/schema-registry/exporter/configuration/cluster-link-describe-yaml.golden new file mode 100644 index 0000000000..195faffc71 --- /dev/null +++ b/test/fixtures/output/schema-registry/exporter/configuration/cluster-link-describe-yaml.golden @@ -0,0 +1 @@ +topic.config.sync.associations.filters: '{"topicsToInclude":["*"],"topicsToExclude":[],"contextType":"AUTO","subjectRenameFormat":"my-${subject}","sourceSRDeployment":"CONFLUENT_CLOUD","sourceLSRC":"lsrc-abc123"}' diff --git a/test/schema_registry_test.go b/test/schema_registry_test.go index d3d9413c1a..474cefeb8d 100644 --- a/test/schema_registry_test.go +++ b/test/schema_registry_test.go @@ -91,6 +91,8 @@ func (s *CLITestSuite) TestSchemaRegistryExporter() { {args: fmt.Sprintf(`schema-registry exporter delete myexporter myexporter2 --environment %s --force`, testserver.SRApiEnvId), fixture: "schema-registry/exporter/delete-multiple-success.golden"}, {args: fmt.Sprintf("schema-registry exporter delete myexporter --environment %s", testserver.SRApiEnvId), input: "y\n", fixture: "schema-registry/exporter/delete-prompt.golden"}, {args: fmt.Sprintf("schema-registry exporter status describe myexporter --environment %s", testserver.SRApiEnvId), fixture: "schema-registry/exporter/status/describe.golden"}, + {args: fmt.Sprintf("schema-registry exporter configuration cluster-link describe myexporter --output json --environment %s", testserver.SRApiEnvId), fixture: "schema-registry/exporter/configuration/cluster-link-describe-json.golden"}, + {args: fmt.Sprintf("schema-registry exporter configuration cluster-link describe myexporter --output yaml --environment %s", testserver.SRApiEnvId), fixture: "schema-registry/exporter/configuration/cluster-link-describe-yaml.golden"}, {args: fmt.Sprintf("schema-registry exporter configuration describe myexporter --output json --environment %s", testserver.SRApiEnvId), fixture: "schema-registry/exporter/configuration/describe-json.golden"}, {args: fmt.Sprintf("schema-registry exporter configuration describe myexporter --output yaml --environment %s", testserver.SRApiEnvId), fixture: "schema-registry/exporter/configuration/describe-yaml.golden"}, {args: fmt.Sprintf("schema-registry exporter pause myexporter --environment %s", testserver.SRApiEnvId), fixture: "schema-registry/exporter/pause.golden"}, diff --git a/test/test-server/schema_registry_handlers.go b/test/test-server/schema_registry_handlers.go index aec3c26165..c117e5d44a 100644 --- a/test/test-server/schema_registry_handlers.go +++ b/test/test-server/schema_registry_handlers.go @@ -389,6 +389,20 @@ func handleSRExporterConfig(t *testing.T) http.HandlerFunc { } } +// Handler for: "/exporters/{name}/config/clusterlink" +func handleSRExporterClusterLinkConfig(t *testing.T) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + filterConfig := `{"topicsToInclude":["*"],"topicsToExclude":[],"contextType":"AUTO",` + + `"subjectRenameFormat":"my-${subject}","sourceSRDeployment":"CONFLUENT_CLOUD",` + + `"sourceLSRC":"lsrc-abc123"}` + config := map[string]string{ + "topic.config.sync.associations.filters": filterConfig, + } + err := json.NewEncoder(w).Encode(config) + require.NoError(t, err) + } +} + // Handler for: "/exporters/{name}/pause" func handleSRExporterPause(t *testing.T) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { diff --git a/test/test-server/schema_registry_router.go b/test/test-server/schema_registry_router.go index 911331df63..337b2b61cb 100644 --- a/test/test-server/schema_registry_router.go +++ b/test/test-server/schema_registry_router.go @@ -29,6 +29,7 @@ var schemaRegistryRoutes = []route{ {"/exporters/{name}", handleSRExporter}, {"/exporters/{name}/status", handleSRExporterStatus}, {"/exporters/{name}/config", handleSRExporterConfig}, + {"/exporters/{name}/config/clusterlink", handleSRExporterClusterLinkConfig}, {"/exporters/{name}/pause", handleSRExporterPause}, {"/exporters/{name}/resume", handleSRExporterResume}, {"/exporters/{name}/reset", handleSRExporterReset},