diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 259be2c14..de19de1fa 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -49,7 +49,7 @@ jobs: go-version-file: go.mod - uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0 with: - version: v2.11.4 + version: v2.14.0 install-mode: goinstall build: diff --git a/.golangci.yml b/.golangci.yml index bd42e761f..7648aec8d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -104,6 +104,10 @@ linters: - gosec path: kong2kic/route\.go text: G115 + # Repeated string literals are idiomatic in tests (table-driven cases, fixtures) + - linters: + - goconst + path: _test\.go - linters: - unparam - unused diff --git a/Dockerfile b/Dockerfile index f73b8415c..7ec2adb00 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -ARG GO_VERSION=1.26.10@sha256:fb456410ab4c979617acaf28c46b2b9fe60b11bcc3dc8ceea0eaeb3e7cd0f8a8 +ARG GO_VERSION=1.27.3@sha256:sha256:f20d83a83e18dbd2a4714b8ffd4b30b287b6243962dd75608e86abcc63ba4efc FROM golang:${GO_VERSION} AS build WORKDIR /deck COPY go.mod ./ diff --git a/cmd/ai.go b/cmd/ai.go index 34a86475b..cd5392eb6 100644 --- a/cmd/ai.go +++ b/cmd/ai.go @@ -1,11 +1,13 @@ package cmd import ( + "errors" "fmt" "io" "os" "slices" + ai2kong "github.com/Kong/ai-deck-converter/convert" "github.com/kong/go-database-reconciler/pkg/file" "github.com/spf13/cobra" ) @@ -14,6 +16,10 @@ import ( // configuration on Konnect. const aiGatewayDocsURL = "https://developer.konghq.com/ai-gateway/kongctl" +// aiGatewayMinRuntimeVersion is the newest AI Gateway release the converter +// knows, so conversion keeps emitting the newest output shape. +const aiGatewayMinRuntimeVersion = "2.2" + // flagIncludeCustomPolicyDefinitions is the `ai sync`/`ai dump` flag that opts // in to managing/dumping the Kong plugin definition entity that AI Gateway // custom_policies convert to (and revert from) @@ -35,6 +41,53 @@ func newAiSubCmd() *cobra.Command { return aiSubCmd } +// convertAIGatewaySource converts an AI Gateway source to decK YAML. All ERROR +// issues are returned as a single joined error, because the converter leaves +// the affected entities out of the output and syncing that would prune them. +// Warnings are returned even on error, as they often explain it. +func convertAIGatewaySource(src []byte) ([]byte, []string, error) { + result, err := ai2kong.Convert(src, ai2kong.Options{MinRuntimeVersion: aiGatewayMinRuntimeVersion}) + if err != nil { + return nil, nil, err + } + + var warnings []string + var errs []error + for _, issue := range result.Issues { + if issue.Severity != ai2kong.Error { + warnings = append(warnings, formatAIIssue(issue)) + } else { + errs = append(errs, errors.New(formatAIIssue(issue))) + } + } + if len(errs) > 0 { + return nil, warnings, errors.Join(errs...) + } + + converted, err := result.Output.ToYAML() + if err != nil { + return nil, warnings, err + } + return converted, warnings, nil +} + +// formatAIIssue renders an issue's reason prefixed with the entity it came from +// (e.g. `model "gpt-4o": ...`), as the reason alone does not say which entity +// is affected. The prefix is omitted for whichever of kind and name is empty. +func formatAIIssue(issue ai2kong.ConversionIssue) string { + src := issue.Source + switch { + case src.Kind != "" && src.Name != "": + return fmt.Sprintf("%s %q: %s", src.Kind, src.Name, issue.Reason) + case src.Kind != "": + return fmt.Sprintf("%s: %s", src.Kind, issue.Reason) + case src.Name != "": + return fmt.Sprintf("%q: %s", src.Name, issue.Reason) + default: + return issue.Reason + } +} + // printAIWarnings writes conversion warnings, one per line, to w. When w is nil // it defaults to os.Stderr. It is a no-op when there are no warnings. func printAIWarnings(w io.Writer, warnings []string) { diff --git a/cmd/ai_sync.go b/cmd/ai_sync.go index e96bb02fc..b8c5169e3 100644 --- a/cmd/ai_sync.go +++ b/cmd/ai_sync.go @@ -8,7 +8,6 @@ import ( "slices" "dario.cat/mergo" - ai2kong "github.com/Kong/ai-deck-converter/convert" "github.com/kong/go-database-reconciler/pkg/cprint" "github.com/kong/go-database-reconciler/pkg/file" "github.com/kong/go-database-reconciler/pkg/utils" @@ -66,11 +65,11 @@ func buildAiSyncTargetContent(filenames []string) (*file.Content, error) { var merged file.Content for _, src := range sources { - convertedYAML, warnings, err := ai2kong.Convert(src.content, ai2kong.Options{}) + convertedYAML, warnings, err := convertAIGatewaySource(src.content) + reportAiConversionWarnings(warnings, aiSyncJSONOutput) if err != nil { return nil, fmt.Errorf("converting %s: %w", src.name, err) } - reportAiConversionWarnings(warnings, aiSyncJSONOutput) content, err := file.GetContentFromReader(bytes.NewReader(convertedYAML), file.EnvVarsSkip) if err != nil { diff --git a/cmd/ai_test.go b/cmd/ai_test.go index 4dab37b2f..9194a4d3f 100644 --- a/cmd/ai_test.go +++ b/cmd/ai_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "os" "path/filepath" + "strings" "testing" ai2kong "github.com/Kong/ai-deck-converter/convert" @@ -40,7 +41,7 @@ const aiGatewaySourceYAML = `models: balancer: algorithm: round-robin slots: 10000 -providers: +model_providers: - type: openai display_name: OpenAI Main name: openai-main @@ -267,15 +268,93 @@ func TestAiConvertJSONInputParity(t *testing.T) { jsonSource, err := yaml.YAMLToJSON([]byte(aiGatewaySourceYAML)) require.NoError(t, err) - fromYAML, _, err := ai2kong.Convert([]byte(aiGatewaySourceYAML), ai2kong.Options{OutputMode: "deck"}) + fromYAML, _, err := convertAIGatewaySource([]byte(aiGatewaySourceYAML)) require.NoError(t, err) - fromJSON, _, err := ai2kong.Convert(jsonSource, ai2kong.Options{OutputMode: "deck"}) + fromJSON, _, err := convertAIGatewaySource(jsonSource) require.NoError(t, err) require.True(t, bytes.Equal(fromYAML, fromJSON), "JSON and YAML inputs should convert to identical decK output") } +// TestFormatAIIssue verifies the entity prefix is built from whichever of the +// issue source's kind and name are set. +func TestFormatAIIssue(t *testing.T) { + tests := []struct { + name string + source ai2kong.ConversionIssueSource + want string + }{ + {"kind and name", ai2kong.ConversionIssueSource{Kind: "model", Name: "gpt-4o"}, `model "gpt-4o": boom`}, + {"kind only", ai2kong.ConversionIssueSource{Kind: "option"}, "option: boom"}, + {"name only", ai2kong.ConversionIssueSource{Name: "gpt-4o"}, `"gpt-4o": boom`}, + {"neither", ai2kong.ConversionIssueSource{}, "boom"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := formatAIIssue(ai2kong.ConversionIssue{Reason: "boom", Source: tt.source}) + require.Equal(t, tt.want, got) + }) + } +} + +// TestConvertAIGatewaySourceIssues verifies that an ERROR issue fails the +// conversion rather than yielding a document with the affected entity left out. +func TestConvertAIGatewaySourceIssues(t *testing.T) { + t.Run("error issue fails the conversion", func(t *testing.T) { + src := "models:\n - name: m1\n access:\n acls:\n allow: [a]\n deny: [b]\n" + out, warnings, err := convertAIGatewaySource([]byte(src)) + require.Error(t, err) + require.Nil(t, out) + require.Empty(t, warnings) + }) + + t.Run("all error issues are reported together", func(t *testing.T) { + src := "models:\n" + + " - name: m1\n access:\n acls:\n allow: [a]\n deny: [b]\n" + + " - name: m2\n access:\n acls:\n allow: [a]\n deny: [b]\n" + out, _, err := convertAIGatewaySource([]byte(src)) + require.Error(t, err) + require.Nil(t, out) + lines := strings.Split(err.Error(), "\n") + require.Len(t, lines, 2) + require.Contains(t, lines[0], `model "m1": `) + require.Contains(t, lines[1], `model "m2": `) + }) + + t.Run("warning issue is returned and does not fail", func(t *testing.T) { + const orphanTarget = " targets:\n" + + " - name: orphan\n" + + " provider: no-such-provider\n" + + " config:\n" + + " type: openai\n" + src := strings.Replace(aiGatewaySourceYAML, " targets:\n", orphanTarget, 1) + out, warnings, err := convertAIGatewaySource([]byte(src)) + require.NoError(t, err) + require.NotEmpty(t, out) + require.Len(t, warnings, 1) + require.Contains(t, warnings[0], "no-such-provider") + }) + + t.Run("warnings that explain an error are returned with it", func(t *testing.T) { + src := strings.Replace(aiGatewaySourceYAML, "provider: openai-main", "provider: no-such-provider", 1) + out, warnings, err := convertAIGatewaySource([]byte(src)) + require.Error(t, err) + require.Nil(t, out) + require.Len(t, warnings, 1) + require.Contains(t, warnings[0], "no-such-provider") + }) +} + +// TestConvertAIGatewaySourceInvalidInput verifies that a source the converter +// cannot parse is returned as an error, with no output and no warnings. +func TestConvertAIGatewaySourceInvalidInput(t *testing.T) { + out, warnings, err := convertAIGatewaySource([]byte("models: [")) + require.Error(t, err) + require.Nil(t, out) + require.Nil(t, warnings) +} + // TestAi2KongOutputFormats drives the ai2kong command end-to-end with JSON input // and asserts both YAML (default) and JSON output are valid and carry the AI tag, // and that an unknown format is rejected. diff --git a/cmd/file_ai2kong.go b/cmd/file_ai2kong.go index cdd63ec01..ae640ec03 100644 --- a/cmd/file_ai2kong.go +++ b/cmd/file_ai2kong.go @@ -5,7 +5,6 @@ import ( "os" "strings" - ai2kong "github.com/Kong/ai-deck-converter/convert" "github.com/kong/go-apiops/filebasics" "github.com/spf13/cobra" "sigs.k8s.io/yaml" @@ -59,15 +58,12 @@ func execute(cmd *cobra.Command, _ []string) error { return fmt.Errorf("failed to read source file: %w", err) } - converted, warnings, err := ai2kong.Convert(sourceContent, ai2kong.Options{ - OutputMode: "deck", - }) + converted, warnings, err := convertAIGatewaySource(sourceContent) + printAIWarnings(os.Stderr, warnings) if err != nil { return fmt.Errorf("conversion failed: %w", err) } - printAIWarnings(os.Stderr, warnings) - // Add the default select_tags to the converted document's _info section doc, err := addDefaultSelectTags(converted) if err != nil { diff --git a/go.mod b/go.mod index 17896ecef..fc3faaac7 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/kong/deck -go 1.26.10 +go 1.27.3 replace github.com/yudai/gojsondiff v1.0.0 => github.com/Kong/gojsondiff v1.3.0 @@ -8,21 +8,25 @@ replace gopkg.in/yaml.v3 v3.0.1 => github.com/Kong/yaml v1.0.0 require ( dario.cat/mergo v1.0.2 - github.com/Kong/ai-deck-converter v0.19.7 + github.com/Kong/ai-deck-converter v0.23.0 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/blang/semver/v4 v4.0.0 github.com/daveshanley/vacuum v0.30.6 github.com/ettle/strcase v0.2.0 github.com/fatih/color v1.19.0 github.com/google/go-cmp v0.7.0 + github.com/gosimple/slug v1.15.0 github.com/kong/go-apiops v0.4.6 github.com/kong/go-database-reconciler v1.43.0 github.com/kong/go-kong v0.80.0 + github.com/kong/kubernetes-configuration v1.5.3 + github.com/kong/kubernetes-testing-framework v0.47.2 github.com/mitchellh/go-homedir v1.1.0 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.12.1 + github.com/tidwall/gjson v1.20.0 golang.org/x/sync v0.24.0 k8s.io/api v0.35.4 k8s.io/apiextensions-apiserver v0.33.1 @@ -38,11 +42,14 @@ require ( charm.land/bubbletea/v2 v2.0.9 // indirect charm.land/lipgloss/v2 v2.0.6 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect + github.com/Kong/go-diff v1.2.2 // indirect + github.com/Kong/gojsondiff v1.3.2 // indirect github.com/Kong/sdk-konnect-go v0.3.1 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/semver/v3 v3.5.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/a-h/templ v0.3.1020 // indirect + github.com/adrg/strutil v0.3.0 // indirect github.com/alecthomas/chroma/v2 v2.27.0 // indirect github.com/avast/retry-go/v4 v4.6.0 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect @@ -60,11 +67,14 @@ require ( github.com/charmbracelet/x/windows v0.2.2 // indirect github.com/clipperhouse/displaywidth v0.11.0 // indirect github.com/clipperhouse/uax29/v2 v2.7.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/distribution/reference v0.6.0 // indirect github.com/dlclark/regexp2/v2 v2.2.1 // indirect github.com/docker/docker v28.0.0+incompatible // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect + github.com/dop251/goja v0.0.0-20260701091749-b07b74453ea9 // indirect + github.com/dop251/goja_nodejs v0.0.0-20260212111938-1f56ff5bcf14 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/ericlagergren/decimal v0.0.0-20240411145413-00de7ca16731 // indirect @@ -72,30 +82,58 @@ require ( github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/fatih/camelcase v1.0.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-errors/errors v1.4.2 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-openapi/jsonpointer v0.23.2 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.1 // indirect github.com/go-openapi/swag/jsonname v0.26.1 // indirect + github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect github.com/google/btree v1.1.3 // indirect + github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-querystring v1.2.0 // indirect + github.com/google/pprof v0.0.0-20250501235452-c0086092b71a // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect + github.com/gosimple/unidecode v1.0.1 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-memdb v1.3.5 // indirect github.com/hashicorp/go-retryablehttp v0.7.8 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect github.com/imdario/mergo v0.3.16 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kong/go-slugify v1.0.0 // indirect + github.com/kong/semver/v4 v4.0.1 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/lucasb-eyer/go-colorful v1.4.1 // indirect github.com/lucasjones/reggen v0.0.0-20200904144131-37ba4fa293bb // indirect + github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a // indirect + github.com/mailru/easyjson v0.9.1 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.27 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/spdystream v0.5.1 // indirect github.com/moby/term v0.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect + github.com/mozillazg/go-unidecode v0.2.0 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/ncruces/go-strftime v1.0.0 // indirect @@ -108,23 +146,40 @@ require ( github.com/pb33f/libopenapi v0.38.7 // indirect github.com/pb33f/libopenapi-validator v0.14.0 // indirect github.com/pb33f/ordered-map/v2 v2.3.1 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/sagikazarmark/locafero v0.11.0 // indirect github.com/samber/lo v1.53.0 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect github.com/sethvargo/go-password v0.3.1 // indirect github.com/shirou/gopsutil/v3 v3.24.5 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sirupsen/logrus v1.9.3 // 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/ssgelm/cookiejarparser v1.1.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.1 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect github.com/x448/float16 v0.8.4 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xlab/treeprint v1.2.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect github.com/yuin/goldmark v1.8.2 // indirect github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect go.opentelemetry.io/otel v1.33.0 // indirect @@ -135,95 +190,37 @@ require ( go.yaml.in/yaml/v3 v3.0.5 // indirect go.yaml.in/yaml/v4 v4.0.0-rc.6 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect + golang.org/x/exp v0.0.0-20260709172345-9ea1abe57597 // indirect + golang.org/x/mod v0.41.0 // indirect + golang.org/x/net v0.60.0 // indirect golang.org/x/oauth2 v0.34.0 // indirect + golang.org/x/sys v0.48.0 // indirect + golang.org/x/term v0.46.0 // indirect + golang.org/x/text v0.42.0 // indirect golang.org/x/time v0.14.0 // indirect + golang.org/x/tools v0.49.0 // indirect + google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/cli-runtime v0.31.0 // indirect k8s.io/component-base v0.33.1 // indirect k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b // indirect + k8s.io/klog/v2 v2.140.0 // indirect + k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect k8s.io/kubectl v0.31.0 // indirect k8s.io/streaming v0.36.3 // indirect + k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect modernc.org/libc v1.72.3 // indirect modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.11.0 // indirect modernc.org/sqlite v1.52.0 // indirect + sigs.k8s.io/controller-runtime v0.21.0 // indirect + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/kind v0.24.0 // indirect sigs.k8s.io/kustomize/api v0.17.3 // indirect sigs.k8s.io/kustomize/kyaml v0.17.2 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.3 // indirect ) - -require ( - github.com/Kong/go-diff v1.2.2 // indirect - github.com/Kong/gojsondiff v1.3.2 // indirect - github.com/adrg/strutil v0.3.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/dop251/goja v0.0.0-20260701091749-b07b74453ea9 // indirect - github.com/dop251/goja_nodejs v0.0.0-20260212111938-1f56ff5bcf14 // indirect - github.com/fsnotify/fsnotify v1.10.1 // indirect - github.com/go-logr/logr v1.4.3 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect - github.com/go-openapi/jsonpointer v0.23.2 // indirect - github.com/go-openapi/jsonreference v0.21.0 // indirect - github.com/go-openapi/swag v0.23.1 // indirect - github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/google/gnostic-models v0.7.0 // indirect - github.com/google/pprof v0.0.0-20250501235452-c0086092b71a // indirect - github.com/gosimple/slug v1.15.0 - github.com/gosimple/unidecode v1.0.1 // indirect - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-immutable-radix v1.3.1 // indirect - github.com/hashicorp/golang-lru v1.0.2 // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/kong/go-slugify v1.0.0 // indirect - github.com/kong/kubernetes-configuration v1.5.3 - github.com/kong/kubernetes-testing-framework v0.47.2 - github.com/kong/semver/v4 v4.0.1 // indirect - github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a // indirect - github.com/mailru/easyjson v0.9.1 // indirect - github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect - github.com/mozillazg/go-unidecode v0.2.0 // indirect - github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect - github.com/sagikazarmark/locafero v0.11.0 // indirect - github.com/shoenig/go-m1cpu v0.1.6 // 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/subosito/gotenv v1.6.0 // indirect - github.com/tidwall/gjson v1.20.0 - github.com/tidwall/match v1.1.1 // indirect - github.com/tidwall/pretty v1.2.1 // indirect - github.com/tklauser/go-sysconf v0.3.12 // indirect - github.com/tklauser/numcpus v0.6.1 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect - github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect - github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect - github.com/yusufpapurcu/wmi v1.2.4 // indirect - golang.org/x/exp v0.0.0-20260709172345-9ea1abe57597 // indirect - golang.org/x/mod v0.41.0 // indirect - golang.org/x/net v0.60.0 // indirect - golang.org/x/sys v0.48.0 // indirect - golang.org/x/term v0.46.0 // indirect - golang.org/x/text v0.42.0 // indirect - golang.org/x/tools v0.49.0 // indirect - google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect - gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/klog/v2 v2.140.0 // indirect - k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect - k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect - sigs.k8s.io/controller-runtime v0.21.0 // indirect - sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect -) diff --git a/go.sum b/go.sum index b49cf8c63..de895f952 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,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/ai-deck-converter v0.19.7 h1:LKr0+ycaWk0P7PflLkuB7Mynn2svplEYaRERS0gi5h8= -github.com/Kong/ai-deck-converter v0.19.7/go.mod h1:tsirPu/2guK1tjGyBueqk82ULcY0LYsjvMSl+pbJE0I= +github.com/Kong/ai-deck-converter v0.23.0 h1:htY7suf+/Pr9qL/B8acapnF6gvqegofroC/t4NqK5zI= +github.com/Kong/ai-deck-converter v0.23.0/go.mod h1:A5gqNV9mP2IsP8n+zhzH9SCu8v42uvYN2wQAC7KtTqQ= 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= diff --git a/kong2tf/builder_default_terraform.go b/kong2tf/builder_default_terraform.go index 3e02a3ad2..855f3e335 100644 --- a/kong2tf/builder_default_terraform.go +++ b/kong2tf/builder_default_terraform.go @@ -10,6 +10,8 @@ import ( "github.com/kong/go-database-reconciler/pkg/file" ) +const consumerIDKey = "consumer_id" + type DefaultTerraformBuider struct { content string provider Provider @@ -229,7 +231,7 @@ func (b *DefaultTerraformBuider) buildConsumers( importConfig{ controlPlaneID: controlPlaneID, importValues: map[string]*string{ - "consumer_id": consumer.ID, + consumerIDKey: consumer.ID, "consumer_group_id": cg.ID, }, }, @@ -239,12 +241,12 @@ func (b *DefaultTerraformBuider) buildConsumers( for _, acl := range consumer.ACLGroups { resourceName := "acl_" + strings.ReplaceAll(*acl.Group, "-", "_") b.content += b.generateResource("gateway_acl", resourceName, toMapAny(acl), map[string]string{ - "consumer_id": parentResourceName, + consumerIDKey: parentResourceName, }, importConfig{ controlPlaneID: controlPlaneID, importValues: map[string]*string{ "id": acl.ID, - "consumer_id": consumer.ID, + consumerIDKey: consumer.ID, }, }, []string{}) } @@ -260,12 +262,12 @@ func (b *DefaultTerraformBuider) buildConsumers( resourceName := "basic_auth_" + strings.ReplaceAll(*basicauth.Username, "-", "_") b.content += b.generateResource("gateway_basic_auth", resourceName, toMapAny(basicauth), map[string]string{ - "consumer_id": parentResourceName, + consumerIDKey: parentResourceName, }, importConfig{ controlPlaneID: controlPlaneID, importValues: map[string]*string{ "id": basicauth.ID, - "consumer_id": consumer.ID, + consumerIDKey: consumer.ID, }, }, lifecycle) } @@ -273,12 +275,12 @@ func (b *DefaultTerraformBuider) buildConsumers( for _, keyauth := range consumer.KeyAuths { resourceName := "key_auth_" + strings.ReplaceAll(*keyauth.Key, "-", "_") b.content += b.generateResource("gateway_key_auth", resourceName, toMapAny(keyauth), map[string]string{ - "consumer_id": parentResourceName, + consumerIDKey: parentResourceName, }, importConfig{ controlPlaneID: controlPlaneID, importValues: map[string]*string{ "id": keyauth.ID, - "consumer_id": consumer.ID, + consumerIDKey: consumer.ID, }, }, []string{}) } @@ -293,12 +295,12 @@ func (b *DefaultTerraformBuider) buildConsumers( } resourceName := "jwt_" + strings.ReplaceAll(*jwt.Key, "-", "_") b.content += b.generateResource("gateway_jwt", resourceName, toMapAny(jwt), map[string]string{ - "consumer_id": parentResourceName, + consumerIDKey: parentResourceName, }, importConfig{ controlPlaneID: controlPlaneID, importValues: map[string]*string{ "id": jwt.ID, - "consumer_id": consumer.ID, + consumerIDKey: consumer.ID, }, }, lifecycle) } @@ -306,12 +308,12 @@ func (b *DefaultTerraformBuider) buildConsumers( for _, hmacauth := range consumer.HMACAuths { resourceName := "hmac_auth_" + strings.ReplaceAll(*hmacauth.Username, "-", "_") b.content += b.generateResource("gateway_hmac_auth", resourceName, toMapAny(hmacauth), map[string]string{ - "consumer_id": parentResourceName, + consumerIDKey: parentResourceName, }, importConfig{ controlPlaneID: controlPlaneID, importValues: map[string]*string{ "id": hmacauth.ID, - "consumer_id": consumer.ID, + consumerIDKey: consumer.ID, }, }, []string{}) } @@ -357,7 +359,7 @@ func (b *DefaultTerraformBuider) buildConsumerGroups(content *file.Content, cont importConfig{ controlPlaneID: controlPlaneID, importValues: map[string]*string{ - "consumer_id": consumer.ID, + consumerIDKey: consumer.ID, "consumer_group_id": cg.ID, }, }, diff --git a/main.go b/main.go index 88c92d0f3..ce4d4b408 100644 --- a/main.go +++ b/main.go @@ -3,11 +3,9 @@ package main import ( "context" "fmt" - "math/rand" "os" "os/signal" "syscall" - "time" "github.com/kong/deck/cmd" ) @@ -30,7 +28,3 @@ func main() { ctx := registerSignalHandler() cmd.Execute(ctx) } - -func init() { - rand.Seed(time.Now().UTC().UnixNano()) -} diff --git a/sanitize/custom_entity_specific.go b/sanitize/custom_entity_specific.go index f8268cabe..ce790bfba 100644 --- a/sanitize/custom_entity_specific.go +++ b/sanitize/custom_entity_specific.go @@ -28,7 +28,7 @@ var ( ) func (s *Sanitizer) handleEntity(entityName string, fieldValue reflect.Value) error { - if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() { + if fieldValue.Kind() == reflect.Pointer && fieldValue.IsNil() { return nil } diff --git a/sanitize/sanitize.go b/sanitize/sanitize.go index 8fb45b42a..05e267210 100644 --- a/sanitize/sanitize.go +++ b/sanitize/sanitize.go @@ -63,7 +63,7 @@ func NewSanitizer(opts *SanitizerOptions) *Sanitizer { func (s *Sanitizer) Sanitize() (*file.Content, error) { content := reflect.ValueOf(s.content) - if content.Kind() == reflect.Ptr { + if content.Kind() == reflect.Pointer { content = content.Elem() } contentType := content.Type() @@ -92,7 +92,7 @@ func (s *Sanitizer) sanitizeField(field reflect.Value) { //nolint:exhaustive switch field.Kind() { - case reflect.Ptr: + case reflect.Pointer: if field.IsNil() { return } @@ -156,7 +156,7 @@ func (s *Sanitizer) sanitizeField(field reflect.Value) { continue } mapValue := iter.Value() - if mapValue.Kind() == reflect.Ptr { + if mapValue.Kind() == reflect.Pointer { mapValue = mapValue.Elem() } s.sanitizeField(mapValue) diff --git a/tests/integration/testdata/dump/009-skip-defaults/konnect/plugin-partial-2.expected.yaml b/tests/integration/testdata/dump/009-skip-defaults/konnect/plugin-partial-2.expected.yaml index e0132719d..64131d03e 100644 --- a/tests/integration/testdata/dump/009-skip-defaults/konnect/plugin-partial-2.expected.yaml +++ b/tests/integration/testdata/dump/009-skip-defaults/konnect/plugin-partial-2.expected.yaml @@ -15,6 +15,8 @@ plugins: - abc-client client_secret: - abc-secret + credential_claim: + - sub issuer: http://example-issuer.com enabled: true name: openid-connect diff --git a/tests/integration/testdata/file_ai2kong/01-models/output.yaml b/tests/integration/testdata/file_ai2kong/01-models/output.yaml index f392b409c..ce466147d 100644 --- a/tests/integration/testdata/file_ai2kong/01-models/output.yaml +++ b/tests/integration/testdata/file_ai2kong/01-models/output.yaml @@ -104,6 +104,7 @@ services: routes: - methods: - POST + - OPTIONS name: openai-chat paths: - /ai/chat/completions diff --git a/tests/integration/testdata/file_ai2kong/02-identity-and-policies/output.yaml b/tests/integration/testdata/file_ai2kong/02-identity-and-policies/output.yaml index cd2c2b28c..46b6d8573 100644 --- a/tests/integration/testdata/file_ai2kong/02-identity-and-policies/output.yaml +++ b/tests/integration/testdata/file_ai2kong/02-identity-and-policies/output.yaml @@ -79,6 +79,7 @@ services: routes: - methods: - POST + - OPTIONS name: openai-chat paths: - /ai/chat/completions diff --git a/tests/integration/testdata/file_ai2kong/05-auth-strategies/output.yaml b/tests/integration/testdata/file_ai2kong/05-auth-strategies/output.yaml index aa51680ae..e1b2b9f41 100644 --- a/tests/integration/testdata/file_ai2kong/05-auth-strategies/output.yaml +++ b/tests/integration/testdata/file_ai2kong/05-auth-strategies/output.yaml @@ -59,6 +59,7 @@ services: routes: - methods: - POST + - OPTIONS name: openai-chat paths: - /ai/chat/completions diff --git a/tests/integration/testdata/file_ai2kong/08-model-cost-lists/output.yaml b/tests/integration/testdata/file_ai2kong/08-model-cost-lists/output.yaml index bca84aaf1..95651aa74 100644 --- a/tests/integration/testdata/file_ai2kong/08-model-cost-lists/output.yaml +++ b/tests/integration/testdata/file_ai2kong/08-model-cost-lists/output.yaml @@ -74,6 +74,7 @@ services: routes: - methods: - POST + - OPTIONS name: openai-chat paths: - /ai/chat/completions diff --git a/tests/integration/testdata/file_ai2kong/10-auth-strategy-bearer-header/output.yaml b/tests/integration/testdata/file_ai2kong/10-auth-strategy-bearer-header/output.yaml index 517d03b24..0ffdc4d39 100644 --- a/tests/integration/testdata/file_ai2kong/10-auth-strategy-bearer-header/output.yaml +++ b/tests/integration/testdata/file_ai2kong/10-auth-strategy-bearer-header/output.yaml @@ -54,6 +54,7 @@ services: routes: - methods: - POST + - OPTIONS name: openai-chat paths: - /ai/chat/completions diff --git a/tests/integration/testdata/file_ai2kong/11-policy-condition/output.yaml b/tests/integration/testdata/file_ai2kong/11-policy-condition/output.yaml index 0936db714..12ca09aaa 100644 --- a/tests/integration/testdata/file_ai2kong/11-policy-condition/output.yaml +++ b/tests/integration/testdata/file_ai2kong/11-policy-condition/output.yaml @@ -50,6 +50,7 @@ services: routes: - methods: - POST + - OPTIONS name: openai-chat paths: - /ai/chat/completions diff --git a/tests/integration/testdata/file_ai2kong/12-skills-api/output.yaml b/tests/integration/testdata/file_ai2kong/12-skills-api/output.yaml index 8da7a5dc5..1f2cfdc0c 100644 --- a/tests/integration/testdata/file_ai2kong/12-skills-api/output.yaml +++ b/tests/integration/testdata/file_ai2kong/12-skills-api/output.yaml @@ -32,6 +32,7 @@ services: - GET - POST - DELETE + - OPTIONS name: openai-skills paths: - /ai/skills diff --git a/tests/integration/testdata/file_ai2kong/14-typesafe-decisions/output.yaml b/tests/integration/testdata/file_ai2kong/14-typesafe-decisions/output.yaml index 6a93303e1..c1b63579c 100644 --- a/tests/integration/testdata/file_ai2kong/14-typesafe-decisions/output.yaml +++ b/tests/integration/testdata/file_ai2kong/14-typesafe-decisions/output.yaml @@ -38,6 +38,7 @@ services: routes: - methods: - POST + - OPTIONS name: typesafe-decisions paths: - ~/ai/v1/systemone diff --git a/tests/integration/testdata/file_ai2kong/15-typesafe-decisions-multi-alias/output.yaml b/tests/integration/testdata/file_ai2kong/15-typesafe-decisions-multi-alias/output.yaml index cff7bfa62..775b075b7 100644 --- a/tests/integration/testdata/file_ai2kong/15-typesafe-decisions-multi-alias/output.yaml +++ b/tests/integration/testdata/file_ai2kong/15-typesafe-decisions-multi-alias/output.yaml @@ -64,6 +64,7 @@ services: routes: - methods: - POST + - OPTIONS name: typesafe-decisions paths: - ~/ai/v1/systemone diff --git a/validate/validate.go b/validate/validate.go index fa5c07a6a..00bd93806 100644 --- a/validate/validate.go +++ b/validate/validate.go @@ -206,7 +206,7 @@ func (v *Validator) Validate(formatVersion semver.Version) []error { for fieldName, entityName := range filteredEntityMap { // Use reflection to get the value of the field from v.state valueOfState := reflect.ValueOf(v.state) - if valueOfState.Kind() == reflect.Ptr { + if valueOfState.Kind() == reflect.Pointer { valueOfState = valueOfState.Elem() // Dereference if it's a pointer }