Skip to content

Commit

Permalink
feat: auto-generate rla's namespace on 'convert' subcommand (#1206)
Browse files Browse the repository at this point in the history
* feat: auto-generate rla's namespace on 'convert' subcommand

* test: correct integration test
  • Loading branch information
GGabriele authored Feb 15, 2024
1 parent d05d343 commit a18140f
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 6 deletions.
84 changes: 84 additions & 0 deletions convert/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package convert

import (
"context"
"crypto/rand"
"fmt"
"math/big"
"strings"

"github.com/blang/semver/v4"
Expand All @@ -27,6 +29,9 @@ const (
FormatKongGateway2x Format = "kong-gateway-2.x"
// FormatKongGateway3x represents the Kong gateway 3.x format.
FormatKongGateway3x Format = "kong-gateway-3.x"

rateLimitingAdvancedPluginName = "rate-limiting-advanced"
rlaNamespaceDefaultLength = 32
)

// AllFormats contains all available formats.
Expand Down Expand Up @@ -100,6 +105,22 @@ func Convert(
return err
}

func randomString(n int) (string, error) {
const charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzj"
charsetLength := big.NewInt(int64(len(charset)))

ret := make([]byte, n)
for i := 0; i < n; i++ {
num, err := rand.Int(rand.Reader, charsetLength)
if err != nil {
return "", fmt.Errorf("error generating random string: %w", err)
}
ret[i] = charset[num.Int64()]
}

return string(ret), nil
}

func convertKongGateway2xTo3x(input *file.Content, filename string) (*file.Content, error) {
if input == nil {
return nil, fmt.Errorf("input content is nil")
Expand Down Expand Up @@ -140,6 +161,11 @@ func convertKongGateway2xTo3x(input *file.Content, filename string) (*file.Conte
filename, changedRoutesLen, strings.Join(changedRoutes, "\n"))
}

// generate any missing required auto fields.
if err := generateAutoFields(outputContent); err != nil {
return nil, err
}

cprint.UpdatePrintf(
"From the '%s' config file,\n"+
"the _format_version field has been migrated from '%s' to '%s'.\n"+
Expand All @@ -153,6 +179,64 @@ func convertKongGateway2xTo3x(input *file.Content, filename string) (*file.Conte
return outputContent, nil
}

func generateAutoFields(content *file.Content) error {
for _, plugin := range content.Plugins {
if *plugin.Name == rateLimitingAdvancedPluginName {
plugin := plugin
if err := autoGenerateNamespaceForRLAPlugin(&plugin); err != nil {
return err
}
}
}

for _, service := range content.Services {
for _, plugin := range service.Plugins {
if *plugin.Name == rateLimitingAdvancedPluginName {
if err := autoGenerateNamespaceForRLAPlugin(plugin); err != nil {
return err
}
}
}
}

for _, route := range content.Routes {
for _, plugin := range route.Plugins {
if *plugin.Name == rateLimitingAdvancedPluginName {
if err := autoGenerateNamespaceForRLAPlugin(plugin); err != nil {
return err
}
}
}
}

for _, consumer := range content.Consumers {
for _, plugin := range consumer.Plugins {
if *plugin.Name == rateLimitingAdvancedPluginName {
if err := autoGenerateNamespaceForRLAPlugin(plugin); err != nil {
return err
}
}
}
}

return nil
}

func autoGenerateNamespaceForRLAPlugin(plugin *file.FPlugin) error {
if plugin.Config != nil {
ns, ok := plugin.Config["namespace"]
if !ok || ns == nil {
// namespace is not set, generate one.
randomNamespace, err := randomString(rlaNamespaceDefaultLength)
if err != nil {
return fmt.Errorf("error generating random namespace: %w", err)
}
plugin.Config["namespace"] = randomNamespace
}
}
return nil
}

func migrateRoutesPathFieldPre300(route *file.FRoute) (*file.FRoute, bool) {
var hasChanged bool
for _, path := range route.Paths {
Expand Down
75 changes: 75 additions & 0 deletions convert/convert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,3 +469,78 @@ func Test_convertKongGatewayToKonnect(t *testing.T) {
})
}
}

func Test_convertAutoFields(t *testing.T) {
content := &file.Content{
Services: []file.FService{
{
Service: kong.Service{
Name: kong.String("s1"),
Host: kong.String("httpbin.org"),
},
Plugins: []*file.FPlugin{
{
Plugin: kong.Plugin{
Name: kong.String("rate-limiting-advanced"),
Config: kong.Configuration{},
},
},
},
},
},
Routes: []file.FRoute{
{
Route: kong.Route{
Name: kong.String("r1"),
Paths: []*string{kong.String("/r1")},
},
Plugins: []*file.FPlugin{
{
Plugin: kong.Plugin{
Name: kong.String("rate-limiting-advanced"),
Config: kong.Configuration{},
},
},
},
},
},
Consumers: []file.FConsumer{
{
Consumer: kong.Consumer{
Username: kong.String("foo"),
},
Plugins: []*file.FPlugin{
{
Plugin: kong.Plugin{
Name: kong.String("rate-limiting-advanced"),
Config: kong.Configuration{},
},
},
},
},
},
Plugins: []file.FPlugin{
{
Plugin: kong.Plugin{
Name: kong.String("rate-limiting-advanced"),
Config: kong.Configuration{},
},
},
},
}

got, err := convertKongGateway2xTo3x(content, "-")
assert.NoError(t, err)

globalPluginConfig := got.Plugins[0].Config
assert.NotEmpty(t, globalPluginConfig["namespace"])

servicePluginConfig := got.Services[0].Plugins[0].Config
assert.NotEmpty(t, servicePluginConfig["namespace"])

routePluginConfig := got.Routes[0].Plugins[0].Config
assert.NotEmpty(t, routePluginConfig["namespace"])

consumerPluginConfig := got.Consumers[0].Plugins[0].Config
assert.NotEmpty(t, consumerPluginConfig["namespace"])
}
30 changes: 24 additions & 6 deletions tests/integration/sync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,24 @@ var (
},
}

plugin36 = []*kong.Plugin{
{
Name: kong.String("basic-auth"),
Protocols: []*string{
kong.String("grpc"),
kong.String("grpcs"),
kong.String("http"),
kong.String("https"),
},
Enabled: kong.Bool(true),
Config: kong.Configuration{
"anonymous": "58076db2-28b6-423b-ba39-a797193017f7",
"hide_credentials": false,
"realm": string("service"),
},
},
}

plugin_on_entities = []*kong.Plugin{ //nolint:revive,stylecheck
{
Name: kong.String("prometheus"),
Expand Down Expand Up @@ -1536,7 +1554,7 @@ func Test_Sync_BasicAuth_Plugin_From_2_0_5_Till_2_8_0(t *testing.T) {
}

// test scope:
// - 3.x
// - >=3.0 <3.6.0
func Test_Sync_BasicAuth_Plugin_From_3x(t *testing.T) {
// setup stage
client, err := getTestClient()
Expand All @@ -1560,7 +1578,7 @@ func Test_Sync_BasicAuth_Plugin_From_3x(t *testing.T) {
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
runWhenKongOrKonnect(t, ">=3.0.0")
runWhen(t, "kong", ">=3.0.0 <3.6.0")
setup(t)

sync(tc.kongFile)
Expand All @@ -1570,8 +1588,8 @@ func Test_Sync_BasicAuth_Plugin_From_3x(t *testing.T) {
}

// test scope:
// - konnect
func Test_Sync_BasicAuth_Plugin_Konnect(t *testing.T) {
// - 3.6+
func Test_Sync_BasicAuth_Plugin_From_36(t *testing.T) {
// setup stage
client, err := getTestClient()
if err != nil {
Expand All @@ -1588,13 +1606,13 @@ func Test_Sync_BasicAuth_Plugin_Konnect(t *testing.T) {
name: "create a plugin",
kongFile: "testdata/sync/003-create-a-plugin/kong3x.yaml",
expectedState: utils.KongRawState{
Plugins: plugin,
Plugins: plugin36,
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
runWhen(t, "konnect", "")
runWhenKongOrKonnect(t, ">=3.6.0")
setup(t)

sync(tc.kongFile)
Expand Down

0 comments on commit a18140f

Please sign in to comment.