Skip to content

Commit

Permalink
feat: support online validation with Konnect (#1335)
Browse files Browse the repository at this point in the history
* feat: support online validation with Konnect

* tests: Added e2e tests for gateway validation with konnect.

Also, this change adds a conditional that ensures that rbac flag used
with konnect mode, gives an legible error to the user.

* chore: lint fix

* chore: removed workspace flag test as it is not supported with konnect

* tests: added rbac-resources file

* refactor: refactored tests and code to add all konnect validate usecases

* chore: fixed workspace setting for Konnect check

* chore: added mutually exclusive flags for konnect compatibility

* chore: fixed logical error

* chore: fixed descripancy in info due to preRun order in cobra

* chore: added corrections based on PR review

* removing unrelated code for vault validation

---------

Co-authored-by: Prashansa Kulshrestha <[email protected]>
  • Loading branch information
GGabriele and Prashansa-K authored Oct 21, 2024
1 parent b09aa3e commit e09888d
Show file tree
Hide file tree
Showing 8 changed files with 307 additions and 11 deletions.
43 changes: 32 additions & 11 deletions cmd/gateway_validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ var (

func executeValidate(cmd *cobra.Command, _ []string) error {
mode := getMode(nil)
if validateOnline && mode == modeKonnect {
return fmt.Errorf("online validation not yet supported in konnect mode")
}
_ = sendAnalytics("validate", "", mode)
// read target file
// this does json schema validation as well
Expand All @@ -45,7 +42,7 @@ func executeValidate(cmd *cobra.Command, _ []string) error {
ctx := cmd.Context()
var kongClient *kong.Client
if validateOnline {
kongClient, err = getKongClient(ctx, targetContent)
kongClient, err = getKongClient(ctx, targetContent, mode)
if err != nil {
return err
}
Expand Down Expand Up @@ -143,10 +140,14 @@ func executeValidate(cmd *cobra.Command, _ []string) error {
return err
}

if validateKonnectCompatibility {
if validateKonnectCompatibility || (mode == modeKonnect && validateOnline) {
if errs := validate.KonnectCompatibility(targetContent); len(errs) != 0 {
return validate.ErrorsWrapper{Errors: errs}
}

if validateCmdRBACResourcesOnly {
return fmt.Errorf("[rbac] not yet supported by konnect")
}
}

if validateOnline {
Expand Down Expand Up @@ -212,7 +213,7 @@ this command unless --online flag is used.
return preRunSilenceEventsFlag()
}

if validateOnline {
if online {
short = short + " (online)"
long = long + "Validates against the Kong API, via communication with Kong. This increases the\n" +
"time for validation but catches significant errors. No resource is created in Kong.\n" +
Expand Down Expand Up @@ -255,6 +256,9 @@ this command unless --online flag is used.
validateCmd.Flags().BoolVar(&validateKonnectCompatibility, "konnect-compatibility",
false, "validate that the state file(s) are ready to be deployed to Konnect")

validateCmd.MarkFlagsMutuallyExclusive("konnect-compatibility", "workspace")
validateCmd.MarkFlagsMutuallyExclusive("konnect-compatibility", "rbac-resources-only")

if err := ensureGetAllMethods(); err != nil {
panic(err.Error())
}
Expand Down Expand Up @@ -285,9 +289,14 @@ func validateWithKong(
return validator.Validate(parsedFormatVersion)
}

func getKongClient(ctx context.Context, targetContent *file.Content) (*kong.Client, error) {
func getKongClient(
ctx context.Context, targetContent *file.Content, mode mode,
) (*kong.Client, error) {
workspaceName := validateWorkspace
if validateWorkspace != "" {
if mode == modeKonnect {
return nil, fmt.Errorf("[workspaces] not supported by Konnect - use control planes instead")
}
// check if workspace exists
workspaceName := getWorkspaceName(validateWorkspace, targetContent, false)
workspaceExists, err := workspaceExists(ctx, rootConfig, workspaceName)
Expand All @@ -299,10 +308,22 @@ func getKongClient(ctx context.Context, targetContent *file.Content) (*kong.Clie
}
}

wsConfig := rootConfig.ForWorkspace(workspaceName)
kongClient, err := reconcilerUtils.GetKongClient(wsConfig)
if err != nil {
return nil, err
var (
kongClient *kong.Client
err error
)
if mode == modeKonnect {
kongClient, err = GetKongClientForKonnectMode(ctx, &konnectConfig)
if err != nil {
return nil, err
}
dumpConfig.KonnectControlPlane = konnectControlPlane
} else {
wsConfig := rootConfig.ForWorkspace(workspaceName)
kongClient, err = reconcilerUtils.GetKongClient(wsConfig)
if err != nil {
return nil, err
}
}
return kongClient, nil
}
Expand Down
18 changes: 18 additions & 0 deletions tests/integration/test_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,3 +374,21 @@ func render(opts ...string) (string, error) {

return stripansi.Strip(string(out)), cmdErr
}

func validate(online bool, opts ...string) error {
deckCmd := cmd.NewRootCmd()

var args []string
if online {
args = []string{"gateway", "validate"}
} else {
args = []string{"file", "validate"}
}

if len(opts) > 0 {
args = append(args, opts...)
}
deckCmd.SetArgs(args)

return deckCmd.ExecuteContext(context.Background())
}
18 changes: 18 additions & 0 deletions tests/integration/testdata/validate/konnect.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
_format_version: "3.0"
_konnect:
control_plane_name: default
services:
- connect_timeout: 60000
id: 58076db2-28b6-423b-ba39-a797193017f7
host: mockbin.org
name: svc1
port: 80
protocol: http
read_timeout: 60000
retries: 5
routes:
- name: r1
id: 87b6a97e-f3f7-4c47-857a-7464cb9e202b
https_redirect_status_code: 301
paths:
- /r1
18 changes: 18 additions & 0 deletions tests/integration/testdata/validate/konnect_1_1.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
_format_version: "1.1"
_konnect:
control_plane_name: default
services:
- connect_timeout: 60000
id: 58076db2-28b6-423b-ba39-a797193017f7
host: mockbin.org
name: svc1
port: 80
protocol: http
read_timeout: 60000
retries: 5
routes:
- name: r1
id: 87b6a97e-f3f7-4c47-857a-7464cb9e202b
https_redirect_status_code: 301
paths:
- /r1
16 changes: 16 additions & 0 deletions tests/integration/testdata/validate/konnect_invalid.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
_format_version: "3.0"
services:
- connect_timeout: 60000
id: 58076db2-28b6-423b-ba39-a797193017f7
host: mockbin.org
name: svc1
port: 80
protocol: http
read_timeout: 60000
retries: 5
routes:
- name: r1
id: 87b6a97e-f3f7-4c47-857a-7464cb9e202b
https_redirect_status_code: 301
paths:
- /r1
17 changes: 17 additions & 0 deletions tests/integration/testdata/validate/konnect_no_version.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
_konnect:
control_plane_name: default
services:
- connect_timeout: 60000
id: 58076db2-28b6-423b-ba39-a797193017f7
host: mockbin.org
name: svc1
port: 80
protocol: http
read_timeout: 60000
retries: 5
routes:
- name: r1
id: 87b6a97e-f3f7-4c47-857a-7464cb9e202b
https_redirect_status_code: 301
paths:
- /r1
93 changes: 93 additions & 0 deletions tests/integration/testdata/validate/rbac-resources.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
_format_version: "3.0"
_konnect:
control_plane_name: default
rbac_roles:
- comment: Full access to Dev Portal related endpoints in the workspace
endpoint_permissions:
- actions:
- read
- delete
- create
- update
endpoint: /developers
negative: false
workspace: default
- actions:
- read
- delete
- create
- update
endpoint: /developers/*
negative: false
workspace: default
- actions:
- read
- delete
- create
- update
endpoint: /files
negative: false
workspace: default
- actions:
- read
- delete
- create
- update
endpoint: /files/*
negative: false
workspace: default
- actions:
- read
- delete
- create
- update
endpoint: /kong
negative: false
workspace: default
- actions:
- read
- delete
- create
- update
endpoint: /rbac/*
negative: true
workspace: default
- actions:
- read
- delete
- create
- update
endpoint: /rbac/*/*
negative: true
workspace: default
- actions:
- read
- delete
- create
- update
endpoint: /rbac/*/*/*
negative: true
workspace: default
- actions:
- read
- delete
- create
- update
endpoint: /rbac/*/*/*/*
negative: true
workspace: default
- actions:
- read
- delete
- create
- update
endpoint: /rbac/*/*/*/*/*
negative: true
workspace: default
- actions:
- read
- update
endpoint: /workspaces/default
negative: false
workspace: default
name: workspace-portal-admin
95 changes: 95 additions & 0 deletions tests/integration/validate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//go:build integration

package integration

import (
"testing"

"github.com/stretchr/testify/assert"
)

const (
ONLINE = true
OFFLINE = false
)

func Test_Validate_Konnect(t *testing.T) {
setup(t)
runWhen(t, "konnect", "")

tests := []struct {
name string
stateFile string
additionalArgs []string
errorExpected bool
errorString string
}{
{
name: "validate with konnect",
stateFile: "testdata/validate/konnect.yaml",
additionalArgs: []string{},
errorExpected: false,
},
{
name: "validate with --konnect-compatibility",
stateFile: "testdata/validate/konnect.yaml",
additionalArgs: []string{"--konnect-compatibility"},
errorExpected: false,
},
{
name: "validate with 1.1 version file",
stateFile: "testdata/validate/konnect_1_1.yaml",
additionalArgs: []string{},
errorExpected: true,
errorString: "[version] decK file version must be '3.0' or greater",
},
{
name: "validate with no version in deck file",
stateFile: "testdata/validate/konnect_no_version.yaml",
additionalArgs: []string{},
errorExpected: true,
errorString: "[version] unable to determine decK file version",
},
{
name: "validate with --rbac-resources-only",
stateFile: "testdata/validate/rbac-resources.yaml",
additionalArgs: []string{"--rbac-resources-only"},
errorExpected: true,
errorString: "[rbac] not yet supported by konnect",
},
{
name: "validate with workspace set",
stateFile: "testdata/validate/konnect.yaml",
additionalArgs: []string{"--workspace=default"},
errorExpected: true,
errorString: "[workspaces] not supported by Konnect - use control planes instead",
},
{
name: "validate with no konnect config in file",
stateFile: "testdata/validate/konnect_invalid.yaml",
additionalArgs: []string{},
errorExpected: true,
errorString: "[konnect] section not specified - ensure details are set via cli flags",
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
validateOpts := append([]string{
tc.stateFile,
}, tc.additionalArgs...)

err := validate(ONLINE, validateOpts...)

if tc.errorExpected {
assert.Error(t, err)
if tc.errorString != "" {
assert.Contains(t, err.Error(), tc.errorString)
}
return
}

assert.NoError(t, err)
})
}
}

0 comments on commit e09888d

Please sign in to comment.