From 8c9c9637e38340deb19a3eb439df01b5c86a505c Mon Sep 17 00:00:00 2001 From: Antoine Jacquemin Date: Mon, 22 Jan 2024 10:52:36 +0800 Subject: [PATCH 1/6] deps(apiops): bump to v0.1.28 adds 'namespace' command --- cmd/file_namespace.go | 138 ++++++++++++++++++++++++++++++++++++++++++ cmd/root.go | 1 + go.mod | 2 +- go.sum | 14 +++-- 4 files changed, 148 insertions(+), 7 deletions(-) create mode 100644 cmd/file_namespace.go diff --git a/cmd/file_namespace.go b/cmd/file_namespace.go new file mode 100644 index 000000000..0f3b4cbef --- /dev/null +++ b/cmd/file_namespace.go @@ -0,0 +1,138 @@ +package cmd + +import ( + "fmt" + "log" + "strings" + + "github.com/kong/go-apiops/deckformat" + "github.com/kong/go-apiops/filebasics" + "github.com/kong/go-apiops/jsonbasics" + "github.com/kong/go-apiops/logbasics" + "github.com/kong/go-apiops/namespace" + "github.com/kong/go-apiops/yamlbasics" + "github.com/spf13/cobra" +) + +var ( + cmdNamespaceInputFilename string + cmdNamespaceOutputFilename string + cmdNamespaceOutputFormat string + cmdNamespaceSelectors []string + cmdNamespacePathPrefix string +) + +// Executes the CLI command "namespace" +func executeNamespace(cmd *cobra.Command, _ []string) error { + verbosity, _ := cmd.Flags().GetInt("verbose") + logbasics.Initialize(log.LstdFlags, verbosity) + _ = sendAnalytics("file-namespace", "", modeLocal) + + cmdNamespaceOutputFormat = strings.ToUpper(cmdNamespaceOutputFormat) + + trackInfo := deckformat.HistoryNewEntry("namespace") + trackInfo["input"] = cmdNamespaceInputFilename + trackInfo["output"] = cmdNamespaceOutputFilename + trackInfo["selectors"] = cmdNamespaceSelectors + trackInfo["path-prefix"] = cmdNamespacePathPrefix + + // do the work: read/namespace/write + data, err := filebasics.DeserializeFile(cmdNamespaceInputFilename) + if err != nil { + return fmt.Errorf("failed to read input file '%s'; %w", cmdNamespaceInputFilename, err) + } + deckformat.HistoryAppend(data, trackInfo) + + yamlNode := jsonbasics.ConvertToYamlNode(data) + + // var selectors yamlbasics.SelectorSet + selectors, err := yamlbasics.NewSelectorSet(cmdNamespaceSelectors) + if err != nil { + return err + } + + err = namespace.Apply(yamlNode, selectors, cmdNamespacePathPrefix) + if err != nil { + return fmt.Errorf("failed to apply the namespace: %w", err) + } + + data = jsonbasics.ConvertToJSONobject(yamlNode) + + return filebasics.WriteSerializedFile(cmdNamespaceOutputFilename, data, + filebasics.OutputFormat(cmdNamespaceOutputFormat)) +} + +// +// +// Define the CLI data for the namespace command +// +// + +func newNamespaceCmd() *cobra.Command { + namespaceCmd := &cobra.Command{ + Use: "namespace [flags]", + Short: "Apply a namespace to routes in a decK file by prefixing the path", + Long: `Apply a namespace to routes in a decK file by prefixing the path. + +By prefixing paths with a specific segment, colliding paths to services can be +namespaced to prevent the collisions. Eg. 2 API definitions that both expose a +'/list' path. By prefixing one with '/addressbook' and the other with '/cookbook' +the resulting paths '/addressbook/list' and '/cookbook/list' can be exposed without +colliding. + +To remove the prefix from the path before the request is routed to the service, the +following approaches are used: +- if the route has 'strip_path=true' then the added prefix will already be stripped +- if the related service has a 'path' property that matches the prefix, then the + 'service.path' property is updated to remove the prefix +- a "pre-function" plugin will be added to remove the prefix from the path + +`, + RunE: executeNamespace, + Example: `# Apply namespace to a deckfile +deck file namespace --namespace=/my-namespace --state=deckfile.yaml + +# Apply namespace to a deckfile, and write to a new file +# Example file 'kong.yaml': +routes: +- paths: + - ~/tracks/system$ + strip_path: true +- paths: + - ~/list$ + strip_path: false + +# Apply namespace to the deckfile, and write to stdout: +cat kong.yaml | deck file namespace --path-prefix=/kong + +# Output: +routes: +- paths: + - ~/kong/tracks/system$ + strip_path: true +- paths: + - ~/kong/list$ + strip_path: false + plugins: + - name: pre-function + config: + access: + - "local ns='/kong' -- this strips the '/kong' namespace from the path\nlocal " + +`, + } + + namespaceCmd.Flags().StringVarP(&cmdNamespaceInputFilename, "state", "s", "-", + "decK file to process. Use - to read from stdin.") + namespaceCmd.Flags().StringVarP(&cmdNamespaceOutputFilename, "output-file", "o", "-", + "Output file to write. Use - to write to stdout.") + namespaceCmd.Flags().StringVarP(&cmdNamespaceOutputFormat, "format", "", "yaml", + "Output format: yaml or json.") + namespaceCmd.Flags().StringArrayVarP(&cmdNamespaceSelectors, "selector", "", []string{}, + "json-pointer identifying element to patch. Repeat for multiple selectors. Defaults "+ + "to selecting all routes.") + namespaceCmd.Flags().StringVarP(&cmdNamespacePathPrefix, "path-prefix", "p", "", + "The path based namespace to apply.") + + return namespaceCmd +} diff --git a/cmd/root.go b/cmd/root.go index 0b0f534e3..c5844bd51 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -249,6 +249,7 @@ It can be used to export, import, or sync entities to Kong.`, fileCmd.AddCommand(newOpenapi2KongCmd()) fileCmd.AddCommand(newFileRenderCmd()) fileCmd.AddCommand(newLintCmd()) + fileCmd.AddCommand(newNamespaceCmd()) fileCmd.AddCommand(newConvertCmd(false)) fileCmd.AddCommand(newValidateCmd(false, false)) // file-based validation } diff --git a/go.mod b/go.mod index bcdb1dda7..48d5b949e 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/daveshanley/vacuum v0.5.0 github.com/fatih/color v1.15.0 github.com/google/go-cmp v0.6.0 - github.com/kong/go-apiops v0.1.27 + github.com/kong/go-apiops v0.1.28 github.com/kong/go-database-reconciler v1.3.1 github.com/kong/go-kong v0.50.0 github.com/mitchellh/go-homedir v1.1.0 diff --git a/go.sum b/go.sum index 33ed8f63d..9153cf54b 100644 --- a/go.sum +++ b/go.sum @@ -180,8 +180,8 @@ github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuOb github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/kong/go-apiops v0.1.27 h1:Jy9HSBtGtO0r0UpXSFenCfOUA0QmeX4i8iGQU0vnC+Q= -github.com/kong/go-apiops v0.1.27/go.mod h1:TYRNVbQ/lw6D3AUJBVP1w4zmMlJ59K83q+zCFa02uZ4= +github.com/kong/go-apiops v0.1.28 h1:Gniizfl4abbo+9wSx1oORN3GhEgklP4CQpzdcM5J5LM= +github.com/kong/go-apiops v0.1.28/go.mod h1:ZNdiTZyVrAssB4wjEYWV7BfpcV9UME9LxnDDZhMPuNU= github.com/kong/go-database-reconciler v1.3.1 h1:lppM6Tc7aGIpGMWax1zDgE0H4/ghSbB4c3Vt/E0OnzM= github.com/kong/go-database-reconciler v1.3.1/go.mod h1:kcK/+GpWqC4v0QJIKCfb1iZ3u+bKvQBfI1AxtayXdYs= github.com/kong/go-kong v0.50.0 h1:HDKn3o/02AH4cURvjzS09gqC4bHZDva5H8JJwYi7T1U= @@ -240,15 +240,15 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108 github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs= -github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM= +github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= +github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= -github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo= +github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0= github.com/pb33f/libopenapi v0.13.11 h1:CHRT15/iakHcwRvr9y7bf63UPekXa8FB1Sc4D4BZ7NU= github.com/pb33f/libopenapi v0.13.11/go.mod h1:Lv2eEtsAtbRFlF8hjH82L8SIGoUNgemMVoKoB6A9THk= github.com/pb33f/libopenapi-validator v0.0.28 h1:XOKGLuRLkHtkiPvm4x1JZgqVqFyD2tPx15qx+aSeaBE= @@ -350,6 +350,8 @@ github.com/yudai/pp v2.0.2-0.20150410014804-be8315415630+incompatible/go.mod h1: github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= +github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= From 7c28ddbbe08a0880eb8a6d4b0b4c38a7b35b2629 Mon Sep 17 00:00:00 2001 From: Thijs Schreijer Date: Tue, 23 Jan 2024 17:43:35 +0100 Subject: [PATCH 2/6] review: fix review comments - fixed example in cli - updated error message on invalid path-prefix --- cmd/file_namespace.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cmd/file_namespace.go b/cmd/file_namespace.go index 0f3b4cbef..4dabd0b5c 100644 --- a/cmd/file_namespace.go +++ b/cmd/file_namespace.go @@ -28,6 +28,11 @@ func executeNamespace(cmd *cobra.Command, _ []string) error { logbasics.Initialize(log.LstdFlags, verbosity) _ = sendAnalytics("file-namespace", "", modeLocal) + err := namespace.CheckNamespace(cmdNamespacePathPrefix) + if err != nil { + return fmt.Errorf("invalid path-prefix '%s': %w", cmdNamespacePathPrefix, err) + } + cmdNamespaceOutputFormat = strings.ToUpper(cmdNamespaceOutputFormat) trackInfo := deckformat.HistoryNewEntry("namespace") @@ -90,7 +95,7 @@ following approaches are used: `, RunE: executeNamespace, Example: `# Apply namespace to a deckfile -deck file namespace --namespace=/my-namespace --state=deckfile.yaml +deck file namespace --path-prefix=/kong --state=deckfile.yaml # Apply namespace to a deckfile, and write to a new file # Example file 'kong.yaml': From b39368d6bd2bd15a6cc4bad66debc949ad333cff Mon Sep 17 00:00:00 2001 From: Thijs Schreijer Date: Wed, 24 Jan 2024 15:47:14 +0100 Subject: [PATCH 3/6] add flag --allow-empty-selctors --- cmd/file_namespace.go | 15 +++++++++------ go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/cmd/file_namespace.go b/cmd/file_namespace.go index 4dabd0b5c..959487fbe 100644 --- a/cmd/file_namespace.go +++ b/cmd/file_namespace.go @@ -15,11 +15,12 @@ import ( ) var ( - cmdNamespaceInputFilename string - cmdNamespaceOutputFilename string - cmdNamespaceOutputFormat string - cmdNamespaceSelectors []string - cmdNamespacePathPrefix string + cmdNamespaceInputFilename string + cmdNamespaceOutputFilename string + cmdNamespaceOutputFormat string + cmdNamespaceSelectors []string + cmdNamespacePathPrefix string + cmdNamespaceAllowEmptySelectors bool ) // Executes the CLI command "namespace" @@ -56,7 +57,7 @@ func executeNamespace(cmd *cobra.Command, _ []string) error { return err } - err = namespace.Apply(yamlNode, selectors, cmdNamespacePathPrefix) + err = namespace.Apply(yamlNode, selectors, cmdNamespacePathPrefix, cmdNamespaceAllowEmptySelectors) if err != nil { return fmt.Errorf("failed to apply the namespace: %w", err) } @@ -138,6 +139,8 @@ routes: "to selecting all routes.") namespaceCmd.Flags().StringVarP(&cmdNamespacePathPrefix, "path-prefix", "p", "", "The path based namespace to apply.") + namespaceCmd.Flags().BoolVarP(&cmdNamespaceAllowEmptySelectors, "allow-empty-selectors", + "", false, "do not error out if the selectors return empty") return namespaceCmd } diff --git a/go.mod b/go.mod index 48d5b949e..f18934418 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/daveshanley/vacuum v0.5.0 github.com/fatih/color v1.15.0 github.com/google/go-cmp v0.6.0 - github.com/kong/go-apiops v0.1.28 + github.com/kong/go-apiops v0.1.29 github.com/kong/go-database-reconciler v1.3.1 github.com/kong/go-kong v0.50.0 github.com/mitchellh/go-homedir v1.1.0 diff --git a/go.sum b/go.sum index 9153cf54b..d8de26bac 100644 --- a/go.sum +++ b/go.sum @@ -180,8 +180,8 @@ github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuOb github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/kong/go-apiops v0.1.28 h1:Gniizfl4abbo+9wSx1oORN3GhEgklP4CQpzdcM5J5LM= -github.com/kong/go-apiops v0.1.28/go.mod h1:ZNdiTZyVrAssB4wjEYWV7BfpcV9UME9LxnDDZhMPuNU= +github.com/kong/go-apiops v0.1.29 h1:c+AB8MmGIr+K01Afm4GB2xaOmJnD/8KWMJQkr9qssnc= +github.com/kong/go-apiops v0.1.29/go.mod h1:ZNdiTZyVrAssB4wjEYWV7BfpcV9UME9LxnDDZhMPuNU= github.com/kong/go-database-reconciler v1.3.1 h1:lppM6Tc7aGIpGMWax1zDgE0H4/ghSbB4c3Vt/E0OnzM= github.com/kong/go-database-reconciler v1.3.1/go.mod h1:kcK/+GpWqC4v0QJIKCfb1iZ3u+bKvQBfI1AxtayXdYs= github.com/kong/go-kong v0.50.0 h1:HDKn3o/02AH4cURvjzS09gqC4bHZDva5H8JJwYi7T1U= From 2f407a287090c2228ebfbd540da20f7c50bf8406 Mon Sep 17 00:00:00 2001 From: Thijs Schreijer Date: Wed, 24 Jan 2024 16:53:56 +0100 Subject: [PATCH 4/6] hide flag --- cmd/file_namespace.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/file_namespace.go b/cmd/file_namespace.go index 959487fbe..78c3b67f7 100644 --- a/cmd/file_namespace.go +++ b/cmd/file_namespace.go @@ -141,6 +141,7 @@ routes: "The path based namespace to apply.") namespaceCmd.Flags().BoolVarP(&cmdNamespaceAllowEmptySelectors, "allow-empty-selectors", "", false, "do not error out if the selectors return empty") + namespaceCmd.Flags().Lookup("allow-empty-selectors").Hidden = true return namespaceCmd } From cfd70e8e9af659e3635bbfe11cd115a1867f67dd Mon Sep 17 00:00:00 2001 From: Rick Spurgeon Date: Wed, 24 Jan 2024 11:35:28 -0600 Subject: [PATCH 5/6] Tidy: Unhide allow-empty-selectors flag and hint to it in the error message --- cmd/file_namespace.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/file_namespace.go b/cmd/file_namespace.go index 78c3b67f7..d0f755a77 100644 --- a/cmd/file_namespace.go +++ b/cmd/file_namespace.go @@ -59,7 +59,7 @@ func executeNamespace(cmd *cobra.Command, _ []string) error { err = namespace.Apply(yamlNode, selectors, cmdNamespacePathPrefix, cmdNamespaceAllowEmptySelectors) if err != nil { - return fmt.Errorf("failed to apply the namespace: %w", err) + return fmt.Errorf("failed to apply the namespace: %w. Use --allow-empty-selectors to suppress this error.", err) } data = jsonbasics.ConvertToJSONobject(yamlNode) @@ -141,7 +141,6 @@ routes: "The path based namespace to apply.") namespaceCmd.Flags().BoolVarP(&cmdNamespaceAllowEmptySelectors, "allow-empty-selectors", "", false, "do not error out if the selectors return empty") - namespaceCmd.Flags().Lookup("allow-empty-selectors").Hidden = true return namespaceCmd } From 6247e51aafc66327eaf9c217436d02d63fc3ec41 Mon Sep 17 00:00:00 2001 From: Thijs Schreijer Date: Wed, 24 Jan 2024 18:57:31 +0100 Subject: [PATCH 6/6] revert error message, doesn't apply all error messages --- cmd/file_namespace.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/file_namespace.go b/cmd/file_namespace.go index d0f755a77..959487fbe 100644 --- a/cmd/file_namespace.go +++ b/cmd/file_namespace.go @@ -59,7 +59,7 @@ func executeNamespace(cmd *cobra.Command, _ []string) error { err = namespace.Apply(yamlNode, selectors, cmdNamespacePathPrefix, cmdNamespaceAllowEmptySelectors) if err != nil { - return fmt.Errorf("failed to apply the namespace: %w. Use --allow-empty-selectors to suppress this error.", err) + return fmt.Errorf("failed to apply the namespace: %w", err) } data = jsonbasics.ConvertToJSONobject(yamlNode)