diff --git a/go.mod b/go.mod index 87db2f47a..f41d412d0 100644 --- a/go.mod +++ b/go.mod @@ -14,8 +14,8 @@ require ( github.com/fatih/color v1.18.0 github.com/google/go-cmp v0.7.0 github.com/kong/go-apiops v0.2.1 - github.com/kong/go-database-reconciler v1.31.0 - github.com/kong/go-kong v0.71.0 + github.com/kong/go-database-reconciler v1.31.2-0.20260122131746-f644916c22cf + github.com/kong/go-kong v0.72.0 github.com/mitchellh/go-homedir v1.1.0 github.com/spf13/cobra v1.9.1 github.com/spf13/pflag v1.0.6 diff --git a/go.sum b/go.sum index 8f44a6c2c..1d67b5a2e 100644 --- a/go.sum +++ b/go.sum @@ -244,10 +244,10 @@ 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.2.1 h1:6HtyQyOj+CLA86iRtXA6rpTqemp7VqJJ6gpHyNHdB7o= github.com/kong/go-apiops v0.2.1/go.mod h1:yPwbl3P2eQinVGAEA0d3legaYmzPJ+WtJf9fSeGF4b8= -github.com/kong/go-database-reconciler v1.31.0 h1:LITt0L/EhajVaSNDMj/TOxh4Cst7CRhiTykYMMlOoLk= -github.com/kong/go-database-reconciler v1.31.0/go.mod h1:kw4+JGF6iv70LcLBmuY2UuMr8KSdqop77IR86B1mYvU= -github.com/kong/go-kong v0.71.0 h1:unPik6osV1DD3DF+jLs9oMedxWQsnepPYTm1dRQSIa4= -github.com/kong/go-kong v0.71.0/go.mod h1:J0vGB3wsZ2i99zly1zTRe3v7rOKpkhQZRwbcTFP76qM= +github.com/kong/go-database-reconciler v1.31.2-0.20260122131746-f644916c22cf h1:gcwPpfu6klBNYsKyRLglziiXanrbyGqlqFgV3ZDdmSc= +github.com/kong/go-database-reconciler v1.31.2-0.20260122131746-f644916c22cf/go.mod h1:X/OC45eZQkif+zyz2h5LLKklBaUFpVlTGFvKKdabE4Q= +github.com/kong/go-kong v0.72.0 h1:8Pb9454WK0BvPrXpLvG80qODaWslEB7qIqScL2nhrc0= +github.com/kong/go-kong v0.72.0/go.mod h1:J0vGB3wsZ2i99zly1zTRe3v7rOKpkhQZRwbcTFP76qM= github.com/kong/go-slugify v1.0.0 h1:vCFAyf2sdoSlBtLcrmDWUFn0ohlpKiKvQfXZkO5vSKY= github.com/kong/go-slugify v1.0.0/go.mod h1:dbR2h3J2QKXQ1k0aww6cN7o4cIcwlWflr6RKRdcoaiw= github.com/kong/kubernetes-configuration v1.4.2 h1:/OafLbl2NucvgQV7Xf/uneIgjxmPPUeE92BrssfVAQY= diff --git a/kong2tf/generate_resource.go b/kong2tf/generate_resource.go index 98c0cdcaa..0df946e82 100644 --- a/kong2tf/generate_resource.go +++ b/kong2tf/generate_resource.go @@ -141,7 +141,7 @@ func generateRelationship( s := fmt.Sprintf(`resource "konnect_%s" "%s" {`, entityType, name) // Extract keys to iterate in a deterministic order - keys := make([]string, 0) + keys := make([]string, 0, len(relations)) for k := range relations { keys = append(keys, k) } diff --git a/tests/integration/dump_test.go b/tests/integration/dump_test.go index c22445a79..4e893cb50 100644 --- a/tests/integration/dump_test.go +++ b/tests/integration/dump_test.go @@ -301,7 +301,7 @@ func Test_Dump_KonnectRename(t *testing.T) { output string err error ) - flags := []string{"-o", "-", "--with-id"} + flags := []string{"-o", "-", "--with-id"} //nolint:prealloc flags = append(flags, tc.flags...) output, err = dump(flags...) @@ -1132,3 +1132,18 @@ func Test_Dump_Services_TLS_Sans(t *testing.T) { }) } } + +func Test_UpstreamsAndTargetsKonnect(t *testing.T) { + runWhenKonnect(t) + setup(t) + + stateFile := "testdata/dump/011-upstreams-and-targets/kong.yaml" + require.NoError(t, sync(context.Background(), stateFile)) + + output, err := dump("-o", "-") + require.NoError(t, err) + + expected, err := readFile(stateFile) + require.NoError(t, err) + assert.Equal(t, expected, output) +} diff --git a/tests/integration/render_test.go b/tests/integration/render_test.go index cb776613a..e5db55397 100644 --- a/tests/integration/render_test.go +++ b/tests/integration/render_test.go @@ -51,9 +51,7 @@ func Test_RenderPlain(t *testing.T) { } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - renderOpts := []string{ - tc.stateFile, - } + renderOpts := []string{tc.stateFile} //nolint:prealloc renderOpts = append(renderOpts, tc.additionalArgs...) for k, v := range tc.envVars { diff --git a/tests/integration/sync_test.go b/tests/integration/sync_test.go index 388250189..ba5cca29e 100644 --- a/tests/integration/sync_test.go +++ b/tests/integration/sync_test.go @@ -8539,8 +8539,145 @@ func Test_Sync_SkipConsumersWithConsumerGroups_Konnect(t *testing.T) { }) } -func Test_Sync_Partials_Plugins(t *testing.T) { - runWhenEnterpriseOrKonnect(t, ">=3.10.0") +func Test_Sync_Partials_PluginsTill3_12(t *testing.T) { + runWhen(t, "enterprise", ">=3.10.0 <3.13.0") + + client, err := getTestClient() + require.NoError(t, err) + + ctx := context.Background() + + dumpConfig := deckDump.Config{} + + partialConfig := kong.Configuration{ + "cluster_max_redirections": float64(5), + "cluster_nodes": nil, + "connect_timeout": float64(2000), + "connection_is_proxied": bool(false), + "database": float64(0), + "host": string("127.0.0.1"), + "keepalive_backlog": nil, + "keepalive_pool_size": float64(256), + "password": nil, + "port": float64(6379), + "read_timeout": float64(3001), + "send_timeout": float64(2004), + "sentinel_master": nil, + "sentinel_nodes": nil, + "sentinel_password": nil, + "sentinel_role": nil, + "sentinel_username": nil, + "server_name": nil, + "ssl": bool(false), + "ssl_verify": bool(false), + "username": nil, + } + + t.Run("create a partial and link to a plugin via name", func(t *testing.T) { + require.NoError(t, sync(ctx, "testdata/sync/039-partials/kong.yaml")) + t.Cleanup(func() { + reset(t) + }) + + newState, err := fetchCurrentState(ctx, client, dumpConfig, t) + require.NoError(t, err) + + // check for partial + partials, err := newState.Partials.GetAll() + require.NoError(t, err) + require.NotNil(t, partials) + + require.Len(t, partials, 1) + assert.Equal(t, "my-ee-partial", *partials[0].Name) + assert.Equal(t, "redis-ee", *partials[0].Type) + assert.IsType(t, kong.Configuration{}, partials[0].Config) + assert.Equal(t, partialConfig, partials[0].Config) + + // check for plugin + plugins, err := newState.Plugins.GetAll() + require.NoError(t, err) + require.NotNil(t, plugins) + require.Len(t, plugins, 1) + assert.Equal(t, "rate-limiting-advanced", *plugins[0].Name) + assert.IsType(t, []*kong.PartialLink{}, plugins[0].Partials) + require.Len(t, plugins[0].Partials, 1) + assert.Equal(t, *partials[0].ID, *plugins[0].Partials[0].ID) + assert.Equal(t, "config.redis", *plugins[0].Partials[0].Path) + }) + + t.Run("partial id is preserved if passed and linking can be done via id", func(t *testing.T) { + require.NoError(t, sync(ctx, "testdata/sync/039-partials/kong-ids.yaml")) + t.Cleanup(func() { + reset(t) + }) + + newState, err := fetchCurrentState(ctx, client, dumpConfig, t) + require.NoError(t, err) + + // check for partial + partials, err := newState.Partials.GetAll() + require.NoError(t, err) + require.NotNil(t, partials) + + require.Len(t, partials, 1) + assert.Equal(t, "13dc230d-d65e-439a-9f05-9fd71abfee4d", *partials[0].ID) + assert.Equal(t, "my-ee-partial", *partials[0].Name) + assert.Equal(t, "redis-ee", *partials[0].Type) + assert.IsType(t, kong.Configuration{}, partials[0].Config) + assert.Equal(t, partialConfig, partials[0].Config) + + // check for plugin + plugins, err := newState.Plugins.GetAll() + require.NoError(t, err) + require.NotNil(t, plugins) + require.Len(t, plugins, 1) + assert.Equal(t, "rate-limiting-advanced", *plugins[0].Name) + assert.IsType(t, []*kong.PartialLink{}, plugins[0].Partials) + require.Len(t, plugins[0].Partials, 1) + assert.Equal(t, "13dc230d-d65e-439a-9f05-9fd71abfee4d", *plugins[0].Partials[0].ID) + assert.Equal(t, "config.redis", *plugins[0].Partials[0].Path) + }) + + t.Run("linking to a plugin fails in case of non-existent partial", func(t *testing.T) { + err := sync(ctx, "testdata/sync/039-partials/kong-wrong.yaml") + require.Error(t, err) + assert.ErrorContains(t, err, "partial non-existent-partial for plugin rate-limiting-advanced: entity not found") + }) + + t.Run("partial linking with a consumer-group scoped plugin works fine", func(t *testing.T) { + require.NoError(t, sync(ctx, "testdata/sync/039-partials/cg-plugin-partial.yaml")) + t.Cleanup(func() { + reset(t) + }) + + newState, err := fetchCurrentState(ctx, client, dumpConfig, t) + require.NoError(t, err) + + // check for partial + partials, err := newState.Partials.GetAll() + require.NoError(t, err) + require.NotNil(t, partials) + + require.Len(t, partials, 1) + assert.Equal(t, "my-redis-ee", *partials[0].Name) + assert.Equal(t, "redis-ee", *partials[0].Type) + + // check for plugin + plugins, err := newState.Plugins.GetAll() + require.NoError(t, err) + require.NotNil(t, plugins) + require.Len(t, plugins, 1) + assert.Equal(t, "rate-limiting-advanced", *plugins[0].Name) + assert.IsType(t, []*kong.PartialLink{}, plugins[0].Partials) + require.Len(t, plugins[0].Partials, 1) + assert.Equal(t, *partials[0].ID, *plugins[0].Partials[0].ID) + assert.Equal(t, "config.redis", *plugins[0].Partials[0].Path) + assert.Equal(t, "foo", *plugins[0].ConsumerGroup.Name) + }) +} + +func Test_Sync_Partials_PluginsAfter3_13(t *testing.T) { + runWhenEnterpriseOrKonnect(t, ">=3.13.0") client, err := getTestClient() require.NoError(t, err) @@ -8550,6 +8687,7 @@ func Test_Sync_Partials_Plugins(t *testing.T) { dumpConfig := deckDump.Config{} partialConfig := kong.Configuration{ + "cloud_authentication": nil, "cluster_max_redirections": float64(5), "cluster_nodes": nil, "connect_timeout": float64(2000), @@ -10063,6 +10201,7 @@ func Test_Sync_Partials_Tagging_Konnect(t *testing.T) { Name: kong.String("redis-ee-common"), Type: kong.String("redis-ee"), Config: kong.Configuration{ + "cloud_authentication": nil, "cluster_max_redirections": float64(5), "cluster_nodes": nil, "connect_timeout": float64(2000), @@ -10092,6 +10231,7 @@ func Test_Sync_Partials_Tagging_Konnect(t *testing.T) { Name: kong.String("redis-ee-sentinel"), Type: kong.String("redis-ee"), Config: kong.Configuration{ + "cloud_authentication": nil, "cluster_max_redirections": float64(5), "cluster_nodes": nil, "connect_timeout": float64(2000), diff --git a/tests/integration/test_utils.go b/tests/integration/test_utils.go index 123f1e890..e6d8b9184 100644 --- a/tests/integration/test_utils.go +++ b/tests/integration/test_utils.go @@ -242,7 +242,7 @@ func testKongState(t *testing.T, client *kong.Client, isKonnect bool, kongState, err := deckDump.Get(ctx, client, dumpConfig) require.NoError(t, err) - opt := []cmp.Option{ + opt := []cmp.Option{ //nolint:prealloc cmpopts.IgnoreFields(kong.Service{}, "CreatedAt", "UpdatedAt"), cmpopts.IgnoreFields(kong.Route{}, "CreatedAt", "UpdatedAt"), cmpopts.IgnoreFields(kong.Plugin{}, "ID", "CreatedAt"), diff --git a/tests/integration/testdata/dump/011-upstreams-and-targets/kong.yaml b/tests/integration/testdata/dump/011-upstreams-and-targets/kong.yaml new file mode 100644 index 000000000..e494ca18d --- /dev/null +++ b/tests/integration/testdata/dump/011-upstreams-and-targets/kong.yaml @@ -0,0 +1,148 @@ +_format_version: "3.0" +_konnect: + control_plane_name: default +upstreams: +- algorithm: round-robin + hash_fallback: none + hash_on: none + hash_on_cookie_path: / + healthchecks: + active: + concurrency: 10 + healthy: + http_statuses: + - 200 + - 302 + interval: 0 + successes: 0 + http_path: / + https_verify_certificate: true + timeout: 1 + type: http + unhealthy: + http_failures: 0 + http_statuses: + - 429 + - 404 + - 500 + - 501 + - 502 + - 503 + - 504 + - 505 + interval: 0 + tcp_failures: 0 + timeouts: 0 + passive: + healthy: + http_statuses: + - 200 + - 201 + - 202 + - 203 + - 204 + - 205 + - 206 + - 207 + - 208 + - 226 + - 300 + - 301 + - 302 + - 303 + - 304 + - 305 + - 306 + - 307 + - 308 + successes: 0 + type: http + unhealthy: + http_failures: 0 + http_statuses: + - 429 + - 500 + - 503 + tcp_failures: 0 + timeouts: 0 + threshold: 0 + name: bar + slots: 10000 + sticky_sessions_cookie_path: / + targets: + - failover: false + target: 1.2.3.4:8000 + weight: 100 + use_srv_name: false +- algorithm: round-robin + hash_fallback: none + hash_on: none + hash_on_cookie_path: / + healthchecks: + active: + concurrency: 10 + healthy: + http_statuses: + - 200 + - 302 + interval: 0 + successes: 0 + http_path: / + https_verify_certificate: true + timeout: 1 + type: http + unhealthy: + http_failures: 0 + http_statuses: + - 429 + - 404 + - 500 + - 501 + - 502 + - 503 + - 504 + - 505 + interval: 0 + tcp_failures: 0 + timeouts: 0 + passive: + healthy: + http_statuses: + - 200 + - 201 + - 202 + - 203 + - 204 + - 205 + - 206 + - 207 + - 208 + - 226 + - 300 + - 301 + - 302 + - 303 + - 304 + - 305 + - 306 + - 307 + - 308 + successes: 0 + type: http + unhealthy: + http_failures: 0 + http_statuses: + - 429 + - 500 + - 503 + tcp_failures: 0 + timeouts: 0 + threshold: 0 + name: foo + slots: 10000 + sticky_sessions_cookie_path: / + targets: + - failover: false + target: 1.2.3.4:8000 + weight: 100 + use_srv_name: false diff --git a/tests/integration/validate_test.go b/tests/integration/validate_test.go index 2e2e974b0..311dd93cb 100644 --- a/tests/integration/validate_test.go +++ b/tests/integration/validate_test.go @@ -155,9 +155,7 @@ func Test_Validate_File(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - validateOpts := []string{ - tc.stateFile, - } + validateOpts := []string{tc.stateFile} //nolint:prealloc validateOpts = append(validateOpts, tc.additionalArgs...) err := validate(OFFLINE, validateOpts...) @@ -205,9 +203,7 @@ func Test_Validate_Gateway(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - validateOpts := []string{ - tc.stateFile, - } + validateOpts := []string{tc.stateFile} //nolint:prealloc validateOpts = append(validateOpts, tc.additionalArgs...) err := validate(ONLINE, validateOpts...) @@ -284,9 +280,7 @@ func Test_Validate_Gateway_EE(t *testing.T) { require.NoError(t, sync(ctx, tc.priorStateFile)) } - validateOpts := []string{ - tc.stateFile, - } + validateOpts := []string{tc.stateFile} //nolint:prealloc validateOpts = append(validateOpts, tc.additionalArgs...) err := validate(ONLINE, validateOpts...) @@ -353,9 +347,7 @@ func Test_Validate_PartialLookupTags(t *testing.T) { t.Run(tc.name, func(t *testing.T) { require.NoError(t, sync(ctx, "testdata/validate/001-partials/partials.yaml")) - validateOpts := []string{ - tc.stateFile, - } + validateOpts := []string{tc.stateFile} //nolint:prealloc validateOpts = append(validateOpts, tc.additionalArgs...) err := validate(tc.mode, validateOpts...)