From 613f4431201cde43f8c2749af0b3bf1caa253449 Mon Sep 17 00:00:00 2001 From: Alberto Sartori Date: Thu, 26 Jun 2025 18:28:36 +0200 Subject: [PATCH 1/2] feat: add konnect-control-plane-id flag Introduce the --konnect-control-plane-id flag, which is mutually exclusive with the --konnect-control-plane-name flag. While names are more user-friendly, IDs are better suited for automation scripts due to their simplicity (no spaces, special characters, or sensitive names). This flag is intended for internal use and is therefore hidden. --- cmd/common.go | 19 +++++++++ cmd/common_konnect.go | 38 ++++++++++++----- cmd/root.go | 13 +++++- go.mod | 2 + go.sum | 4 +- tests/integration/dump_test.go | 41 +++++++++++++++++++ tests/integration/sync_test.go | 7 ++++ .../047-konnect-cp-id/konnect_test_cp.yaml | 14 +++++++ 8 files changed, 123 insertions(+), 15 deletions(-) create mode 100644 tests/integration/testdata/sync/047-konnect-cp-id/konnect_test_cp.yaml diff --git a/cmd/common.go b/cmd/common.go index 0f2a126a4..751f6f628 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -127,6 +127,21 @@ func evaluateTargetRuntimeGroupOrControlPlaneName(targetContent *file.Content) e return nil } +func evaluateControlPlaneID(targetContent *file.Content) error { + idFromFile := targetContent.Konnect.ControlPlaneID + if idFromFile != "" && konnectControlPlaneID != "" && idFromFile != konnectControlPlaneID { + return fmt.Errorf("control plane ID '%v' specified via "+ + "--konnect-control-plane-id flag is "+ + "different from '%v' found in state file(s)", + konnectControlPlaneID, idFromFile) + } + if konnectControlPlaneID == "" && idFromFile != "" { + // if no ID specified via flag, use the one from the file + konnectControlPlaneID = idFromFile + } + return nil +} + func RemoveConsumerPlugins(targetContentPlugins []file.FPlugin) []file.FPlugin { filteredPlugins := []file.FPlugin{} @@ -198,6 +213,9 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism, if err := evaluateTargetRuntimeGroupOrControlPlaneName(targetContent); err != nil { return err } + if err := evaluateControlPlaneID(targetContent); err != nil { + return err + } } if konnectRuntimeGroup != "" { konnectControlPlane = konnectRuntimeGroup @@ -208,6 +226,7 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism, return err } dumpConfig.KonnectControlPlane = konnectControlPlane + dumpConfig.KonnectControlPlaneID = konnectControlPlaneID } rootClient, err := reconcilerUtils.GetKongClient(rootConfig) diff --git a/cmd/common_konnect.go b/cmd/common_konnect.go index 0f79e217c..84ca76375 100644 --- a/cmd/common_konnect.go +++ b/cmd/common_konnect.go @@ -84,9 +84,14 @@ func GetKongClientForKonnectMode( if err != nil { return nil, fmt.Errorf("authenticating with Konnect: %w", err) } - cpID, err := fetchKonnectControlPlaneID(ctx, konnectClient, konnectConfig.ControlPlaneName) - if err != nil { - return nil, err + var cpID string + if konnectControlPlaneID != "" { + cpID = konnectControlPlaneID + } else { + cpID, err = fetchKonnectControlPlaneID(ctx, konnectClient, konnectConfig.ControlPlaneName) + if err != nil { + return nil, err + } } // set the kong control plane ID in the client @@ -140,6 +145,10 @@ func dumpKonnectV2(ctx context.Context) error { konnectControlPlane = defaultControlPlaneName } dumpConfig.KonnectControlPlane = konnectControlPlane + + if konnectControlPlaneID != "" { + dumpConfig.KonnectControlPlaneID = konnectControlPlaneID + } konnectConfig.TLSConfig = rootConfig.TLSConfig client, err := GetKongClientForKonnectMode(ctx, &konnectConfig) if err != nil { @@ -153,14 +162,21 @@ func dumpKonnectV2(ctx context.Context) error { if err != nil { return fmt.Errorf("building state: %w", err) } - return file.KongStateToFile(ks, file.WriteConfig{ - SelectTags: dumpConfig.SelectorTags, - Filename: dumpCmdKongStateFile, - FileFormat: file.Format(strings.ToUpper(dumpCmdStateFormat)), - WithID: dumpWithID, - ControlPlaneName: konnectControlPlane, - KongVersion: fetchKonnectKongVersion(), - }) + writeConfig := file.WriteConfig{ + SelectTags: dumpConfig.SelectorTags, + Filename: dumpCmdKongStateFile, + FileFormat: file.Format(strings.ToUpper(dumpCmdStateFormat)), + WithID: dumpWithID, + KongVersion: fetchKonnectKongVersion(), + } + + if konnectControlPlaneID != "" { + writeConfig.ControlPlaneID = konnectControlPlaneID + } else { + writeConfig.ControlPlaneName = konnectControlPlane + } + + return file.KongStateToFile(ks, writeConfig) } func syncKonnect(ctx context.Context, diff --git a/cmd/root.go b/cmd/root.go index 9b33fe483..b5e1eb714 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -35,8 +35,9 @@ var ( disableAnalytics bool konnectConnectionDesired bool - konnectRuntimeGroup string - konnectControlPlane string + konnectRuntimeGroup string + konnectControlPlane string + konnectControlPlaneID string ) // NewRootCmd represents the base command when called without any subcommands @@ -222,7 +223,14 @@ It can be used to export, import, or sync entities to Kong.`, viper.BindPFlag("konnect-control-plane-name", rootCmd.PersistentFlags().Lookup("konnect-control-plane-name")) + rootCmd.PersistentFlags().String("konnect-control-plane-id", "", + "Konnect Control Plane ID.") + viper.BindPFlag("konnect-control-plane-id", + rootCmd.PersistentFlags().Lookup("konnect-control-plane-id")) + rootCmd.PersistentFlags().MarkHidden("konnect-control-plane-id") + rootCmd.MarkFlagsMutuallyExclusive("konnect-runtime-group-name", "konnect-control-plane-name") + rootCmd.MarkFlagsMutuallyExclusive("konnect-control-plane-id", "konnect-control-plane-name") rootCmd.AddCommand(newVersionCmd()) rootCmd.AddCommand(newCompletionCmd()) @@ -441,6 +449,7 @@ func initKonnectConfig() error { konnectConfig.Headers = extendHeaders(viper.GetStringSlice("headers")) konnectControlPlane = viper.GetString("konnect-control-plane-name") konnectRuntimeGroup = viper.GetString("konnect-runtime-group-name") + konnectControlPlaneID = viper.GetString("konnect-control-plane-id") return nil } diff --git a/go.mod b/go.mod index 443ccb789..ed460d620 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,8 @@ replace github.com/yudai/gojsondiff v1.0.0 => github.com/Kong/gojsondiff v1.3.0 replace gopkg.in/yaml.v3 v3.0.1 => github.com/Kong/yaml v1.0.0 +replace github.com/kong/go-database-reconciler v1.24.2 => github.com/Kong/go-database-reconciler v1.24.3-0.20250709135146-2240d712929f + require ( github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/blang/semver/v4 v4.0.0 diff --git a/go.sum b/go.sum index 4e90d8ebb..b6135e160 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Kong/go-database-reconciler v1.24.3-0.20250709135146-2240d712929f h1:gMpgex99TZ5ozYPkUp3YmDWm1vXIWxcgOTn8/n0fJ4k= +github.com/Kong/go-database-reconciler v1.24.3-0.20250709135146-2240d712929f/go.mod h1:fWjwkA2MDQu1QKLV8qeUCagGmOU4wNhPAQkl5LBfYd0= github.com/Kong/go-diff v1.2.2 h1:KKKaqHc8IxuguFVIZMNt3bi6YuC/t9r7BGD8bOOpSgM= github.com/Kong/go-diff v1.2.2/go.mod h1:nlvdwVZQk3Rm+tbI0cDmKFrOjghtcZTrZBp+UruvvA8= github.com/Kong/gojsondiff v1.3.2 h1:qIOVq2mUXt+NXy8Be5gRUee9TP3Ve0MbQSafg9bXKZE= @@ -242,8 +244,6 @@ github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/q github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kong/go-apiops v0.1.46 h1:5uOC47vzf11B65kH/mWVphiymGrVrwrq5bRLefqJ1xw= github.com/kong/go-apiops v0.1.46/go.mod h1:hKnHJ3UyeuG932SkI/yMpuT/PqSqGXNTS1zhno1lDqg= -github.com/kong/go-database-reconciler v1.24.2 h1:TNY4dy+uJcmSlWAElye65eVl1Wd1xgOYsJ73mW+DQ4M= -github.com/kong/go-database-reconciler v1.24.2/go.mod h1:fWjwkA2MDQu1QKLV8qeUCagGmOU4wNhPAQkl5LBfYd0= github.com/kong/go-kong v0.66.1 h1:UVdemzcCpfXEl6O/VHdf0rT2bXdIO5ykuJbf2z1JTko= github.com/kong/go-kong v0.66.1/go.mod h1:wRMPAXGOB3kn53TF6zN4l2JhIWPUfXDFKNHkMHBB3iQ= github.com/kong/go-slugify v1.0.0 h1:vCFAyf2sdoSlBtLcrmDWUFn0ohlpKiKvQfXZkO5vSKY= diff --git a/tests/integration/dump_test.go b/tests/integration/dump_test.go index 49c3fc010..d3b02813b 100644 --- a/tests/integration/dump_test.go +++ b/tests/integration/dump_test.go @@ -289,6 +289,47 @@ func Test_Dump_KonnectRename(t *testing.T) { } } +func Test_Dump_KonnectControlPlaneID(t *testing.T) { + tests := []struct { + name string + stateFile string + expectedFile string + flags []string + }{ + { + name: "dump with konnect-control-plane-id", + stateFile: "testdata/sync/047-konnect-cp-id/konnect_test_cp.yaml", + expectedFile: "testdata/sync/047-konnect-cp-id/konnect_test_cp.yaml", + flags: []string{"--konnect-control-plane-id", "a998e247-8889-4d49-818b-883cab519675"}, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Cleanup(func() { + reset(t, tc.flags...) + }) + runWhenKonnect(t) + setup(t) + + require.NoError(t, sync(context.Background(), tc.stateFile)) + + var ( + output string + err error + ) + flags := []string{"-o", "-", "--with-id"} + flags = append(flags, tc.flags...) + output, err = dump(flags...) + + require.NoError(t, err) + + expected, err := readFile(tc.expectedFile) + require.NoError(t, err) + assert.Equal(t, expected, output) + }) + } +} + func Test_Dump_ConsumerGroupConsumersWithCustomID(t *testing.T) { runWhen(t, "enterprise", ">=3.0.0") setup(t) diff --git a/tests/integration/sync_test.go b/tests/integration/sync_test.go index 880e8a72c..cb9737afc 100644 --- a/tests/integration/sync_test.go +++ b/tests/integration/sync_test.go @@ -6565,6 +6565,13 @@ func Test_Sync_KonnectRenameErrors(t *testing.T) { expectedError: errors.New(`warning: control plane 'cp1' specified via ` + `--konnect-[control-plane|runtime-group]-name flag is different from 'default' found in state file(s)`), }, + { + name: "different control plane ids fail", + kongFile: "testdata/sync/047-konnect-cp-id/konnect_test_cp.yaml", + flags: []string{"--konnect-control-plane-id", "f313df74-5479-487e-966c-6d999e60ff21"}, + expectedError: errors.New(`control plane ID 'f313df74-5479-487e-966c-6d999e60ff21' specified via ` + + `--konnect-control-plane-id flag is different from 'a998e247-8889-4d49-818b-883cab519675' found in state file(s)`), + }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { diff --git a/tests/integration/testdata/sync/047-konnect-cp-id/konnect_test_cp.yaml b/tests/integration/testdata/sync/047-konnect-cp-id/konnect_test_cp.yaml new file mode 100644 index 000000000..0a501088a --- /dev/null +++ b/tests/integration/testdata/sync/047-konnect-cp-id/konnect_test_cp.yaml @@ -0,0 +1,14 @@ +_format_version: "3.0" +_konnect: + control_plane_id: a998e247-8889-4d49-818b-883cab519675 +services: +- connect_timeout: 60000 + enabled: true + host: mockbin-test.org + id: 58076db2-28b6-423b-ba39-a797193017f7 + name: test + port: 80 + protocol: http + read_timeout: 60000 + retries: 5 + write_timeout: 60000 From becd8f184fcaf345a923b901e7e2216c3da5f51a Mon Sep 17 00:00:00 2001 From: Alberto Sartori Date: Fri, 11 Jul 2025 09:59:07 +0200 Subject: [PATCH 2/2] chore: amend error message Remove "warning:" as it is preceded by "Error:" --- cmd/common.go | 2 +- tests/integration/sync_test.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/common.go b/cmd/common.go index 751f6f628..c70786ec7 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -113,7 +113,7 @@ func evaluateTargetRuntimeGroupOrControlPlaneName(targetContent *file.Content) e targetFromCLI = konnectRuntimeGroup } if targetFromCLI != "" && targetFromFile != targetFromCLI { - return fmt.Errorf("warning: control plane '%v' specified via "+ + return fmt.Errorf("control plane '%v' specified via "+ "--konnect-[control-plane|runtime-group]-name flag is "+ "different from '%v' found in state file(s)", targetFromCLI, targetFromFile) diff --git a/tests/integration/sync_test.go b/tests/integration/sync_test.go index cb9737afc..67705026c 100644 --- a/tests/integration/sync_test.go +++ b/tests/integration/sync_test.go @@ -6541,28 +6541,28 @@ func Test_Sync_KonnectRenameErrors(t *testing.T) { name: "different runtime group names fail", kongFile: "testdata/sync/026-konnect-rename/konnect_default_cp.yaml", flags: []string{"--konnect-runtime-group-name", "rg1"}, - expectedError: errors.New(`warning: control plane 'rg1' specified via ` + + expectedError: errors.New(`control plane 'rg1' specified via ` + `--konnect-[control-plane|runtime-group]-name flag is different from 'default' found in state file(s)`), }, { name: "different runtime group names fail", kongFile: "testdata/sync/026-konnect-rename/konnect_default_rg.yaml", flags: []string{"--konnect-runtime-group-name", "rg1"}, - expectedError: errors.New(`warning: control plane 'rg1' specified via ` + + expectedError: errors.New(`control plane 'rg1' specified via ` + `--konnect-[control-plane|runtime-group]-name flag is different from 'default' found in state file(s)`), }, { name: "different control plane names fail", kongFile: "testdata/sync/026-konnect-rename/konnect_default_cp.yaml", flags: []string{"--konnect-control-plane-name", "cp1"}, - expectedError: errors.New(`warning: control plane 'cp1' specified via ` + + expectedError: errors.New(`control plane 'cp1' specified via ` + `--konnect-[control-plane|runtime-group]-name flag is different from 'default' found in state file(s)`), }, { name: "different control plane names fail", kongFile: "testdata/sync/026-konnect-rename/konnect_default_rg.yaml", flags: []string{"--konnect-control-plane-name", "cp1"}, - expectedError: errors.New(`warning: control plane 'cp1' specified via ` + + expectedError: errors.New(`control plane 'cp1' specified via ` + `--konnect-[control-plane|runtime-group]-name flag is different from 'default' found in state file(s)`), }, {