Skip to content

Commit

Permalink
feat: add --skip-ca-certificates flag (#617)
Browse files Browse the repository at this point in the history
* feat: add --skip-ca-certificates flag

Since CA certificates are 'global' entities in Kong,
they cannot be managed on a per-workspace basis,
making it hard to be handled declaratively with decK.

This introduces a new --skip-ca-certificates to
sync/dump/diff/reset to make sure CA certs are
ignored when needed.

* tests: add integration tests for --skip-ca-certificates
  • Loading branch information
GGabriele committed Mar 17, 2022
1 parent e78a4de commit c94ddb2
Show file tree
Hide file tree
Showing 11 changed files with 291 additions and 90 deletions.
3 changes: 3 additions & 0 deletions cmd/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
if dumpConfig.SkipConsumers {
targetContent.Consumers = []file.FConsumer{}
}
if dumpConfig.SkipCACerts {
targetContent.CACertificates = []file.FCACertificate{}
}

rootClient, err := utils.GetKongClient(rootConfig)
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions cmd/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ that will be created, updated, or deleted.
false, "return exit code 2 if there is a diff present,\n"+
"exit code 0 if no diff is found,\n"+
"and exit code 1 if an error occurs.")
diffCmd.Flags().BoolVar(&dumpConfig.SkipCACerts, "skip-ca-certificates",
false, "do not diff CA certificates.")
addSilenceEventsFlag(diffCmd.Flags())
return diffCmd
}
2 changes: 2 additions & 0 deletions cmd/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,5 +166,7 @@ configure Kong.`,
false, "export only the RBAC resources (Kong Enterprise only).")
dumpCmd.Flags().BoolVar(&assumeYes, "yes",
false, "assume 'yes' to prompts and run non-interactively.")
dumpCmd.Flags().BoolVar(&dumpConfig.SkipCACerts, "skip-ca-certificates",
false, "do not dump CA certificates.")
return dumpCmd
}
2 changes: 2 additions & 0 deletions cmd/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ By default, this command will ask for confirmation.`,
"When this setting has multiple tag values, entities must match every tag.")
resetCmd.Flags().BoolVar(&dumpConfig.RBACResourcesOnly, "rbac-resources-only",
false, "reset only the RBAC resources (Kong Enterprise only).")
resetCmd.Flags().BoolVar(&dumpConfig.SkipCACerts, "skip-ca-certificates",
false, "do not reset CA certificates.")

return resetCmd
}
2 changes: 2 additions & 0 deletions cmd/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ to get Kong's state in sync with the input state.`,
0, "artificial delay (in seconds) that is injected between insert operations \n"+
"for related entities (usually for Cassandra deployments).\n"+
"See 'db_update_propagation' in kong.conf.")
syncCmd.Flags().BoolVar(&dumpConfig.SkipCACerts, "skip-ca-certificates",
false, "do not sync CA certificates.")
addSilenceEventsFlag(syncCmd.Flags())
return syncCmd
}
21 changes: 13 additions & 8 deletions dump/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ type Config struct {
// are not exported.
SkipConsumers bool

// If true, CA certificates are not exported.
SkipCACerts bool

// SelectorTags can be used to export entities tagged with only specific
// tags.
SelectorTags []string
Expand Down Expand Up @@ -185,14 +188,16 @@ func getProxyConfiguration(ctx context.Context, group *errgroup.Group,
return nil
})

group.Go(func() error {
caCerts, err := GetAllCACertificates(ctx, client, config.SelectorTags)
if err != nil {
return fmt.Errorf("ca-certificates: %w", err)
}
state.CACertificates = caCerts
return nil
})
if !config.SkipCACerts {
group.Go(func() error {
caCerts, err := GetAllCACertificates(ctx, client, config.SelectorTags)
if err != nil {
return fmt.Errorf("ca-certificates: %w", err)
}
state.CACertificates = caCerts
return nil
})
}

group.Go(func() error {
snis, err := GetAllSNIs(ctx, client, config.SelectorTags)
Expand Down
68 changes: 68 additions & 0 deletions tests/integration/reset_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//go:build integration

package integration

import (
"testing"

"github.com/kong/deck/utils"
"github.com/kong/go-kong/kong"
)

var caCert = &kong.CACertificate{
CertDigest: kong.String("b865971cecadd7bac9487901c9269c1fa903b3a3b521a927c5e2513f692ac61e"),
Cert: kong.String(`-----BEGIN CERTIFICATE-----
MIICvDCCAaSgAwIBAgIJAID17vZt1yWyMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV
BAMMCEhlbGxvTmV3MB4XDTIyMDMxNTE5MTgzOVoXDTIyMDQxNDE5MTgzOVowEzER
MA8GA1UEAwwISGVsbG9OZXcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQDL1l6EB2rDPjKDoJX52VsnO8bdKnoGq2s5Er7piVQjYBA6U7NzEJwvsaL9uG1p
/OFud8uwJFCm0NF1DxkNA+qpUvaBXBnn4htbXE20C7HwAWCUU0TUWgTpGYC0EkGZ
VlbXoQ1SewK+AERjdBKqa0U9Wk0gkD0kVc2UfO7rxU7w6nkoFPgBI1IlJZXM5TVg
1AeJDrdgUSa/fsja5qOYVcwGiUgqMr3nMs1jBJRgwhC0ELF1lFaANouqPC4KweLE
FNgam69AZallFNZOKVh6vJLKBfE9I8TM5yBpRllhKAaUv1qWlPFYxoIPvnFzQPku
ExGbYR6asSXwq6UHxREIOno1AgMBAAGjEzARMA8GA1UdEwQIMAYBAf8CAQAwDQYJ
KoZIhvcNAQELBQADggEBAHXc6lc6BGzdjwWX8XViBxY1NnK5HxNfD+rP7/JJ4m33
zoTteY+KRcKo6t49TDqfpnVfCGunnoGOFP5ATY29vUavigICw7SGGLKWIM38c0bH
bx14/d/LQd2LaNd/cemTDkF3XJi3OdrGJPNOVLfX0InqbmwBzariWCwzufwHGxwR
WpOh8Qv2kFPuFVwlQNPNMhV7qsa/NM77Wo4Q6kA5V3aYSnF+KbWF3by/SqUC5JMz
cbvPj0Yzt97v7FpOILcDcMWjxuUnvuUYvGuB5tzBEe91s3ZTUK0A5moYOYkTHUlX
9CkGSwFE+jBTxUBPKzm3MVoK2cGoX8gEpzcYSwjM8Ws=
-----END CERTIFICATE-----`),
}

func Test_Reset_SkipCACert(t *testing.T) {
// setup stage
client, err := getTestClient()
if err != nil {
t.Errorf(err.Error())
}

tests := []struct {
name string
kongFile string
expectedState utils.KongRawState
}{
{
name: "reset with --skip-ca-certificates should ignore CA certs",
kongFile: "testdata/reset/001-skip-ca-cert/kong.yaml",
expectedState: utils.KongRawState{
CACertificates: []*kong.CACertificate{caCert},
},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
// ca_certificates first appeared in 1.3, but we limit to 2.7+
// here because the schema changed and the entities aren't the same
// across all versions, even though the skip functionality works the same.
kong.RunWhenKong(t, ">=2.7.0")
teardown := setup(t)
defer teardown(t)

sync(tc.kongFile)
reset(t, "--skip-ca-certificates")
testKongState(t, client, tc.expectedState, nil)
})
}
}
119 changes: 37 additions & 82 deletions tests/integration/sync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,10 @@
package integration

import (
"context"
"os"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/kong/deck/cmd"
"github.com/kong/deck/dump"
"github.com/kong/deck/utils"
"github.com/kong/go-kong/kong"
)
Expand Down Expand Up @@ -222,84 +218,6 @@ var (
}
)

func getKongAddress() string {
address := os.Getenv("DECK_KONG_ADDR")
if address != "" {
return address
}
return "http://localhost:8001"
}

func getTestClient() (*kong.Client, error) {
return utils.GetKongClient(utils.KongClientConfig{
Address: getKongAddress(),
})
}

func sortSlices(x, y interface{}) bool {
var xName, yName string
switch xEntity := x.(type) {
case *kong.Service:
yEntity := y.(*kong.Service)
xName = *xEntity.Name
yName = *yEntity.Name
case *kong.Route:
yEntity := y.(*kong.Route)
xName = *xEntity.Name
yName = *yEntity.Name
case *kong.Plugin:
yEntity := y.(*kong.Plugin)
xName = *xEntity.Name
yName = *yEntity.Name
}
return xName < yName
}

func testKongState(t *testing.T, client *kong.Client,
expectedState utils.KongRawState, ignoreFields []cmp.Option) {
// Get entities from Kong
ctx := context.Background()
kongState, err := dump.Get(ctx, client, dump.Config{})
if err != nil {
t.Errorf(err.Error())
}

opt := []cmp.Option{
cmpopts.IgnoreFields(kong.Service{}, "ID", "CreatedAt", "UpdatedAt"),
cmpopts.IgnoreFields(kong.Route{}, "ID", "CreatedAt", "UpdatedAt"),
cmpopts.IgnoreFields(kong.Plugin{}, "ID", "CreatedAt"),
cmpopts.IgnoreFields(kong.Upstream{}, "ID", "CreatedAt"),
cmpopts.IgnoreFields(kong.Target{}, "ID", "CreatedAt"),
cmpopts.SortSlices(sortSlices),
cmpopts.EquateEmpty(),
}
opt = append(opt, ignoreFields...)

if diff := cmp.Diff(kongState, &expectedState, opt...); diff != "" {
t.Errorf(diff)
}
}

func reset(t *testing.T) {
deckCmd := cmd.NewRootCmd()
deckCmd.SetArgs([]string{"reset", "--force"})
if err := deckCmd.Execute(); err != nil {
t.Fatalf(err.Error(), "failed to reset Kong's state")
}
}

func setup(t *testing.T) func(t *testing.T) {
return func(t *testing.T) {
reset(t)
}
}

func sync(kongFile string) {
deckCmd := cmd.NewRootCmd()
deckCmd.SetArgs([]string{"sync", "-s", kongFile})
deckCmd.ExecuteContext(context.Background())
}

// test scope:
// - 1.4.3
func Test_Sync_ServicesRoutes_Till_1_4_3(t *testing.T) {
Expand Down Expand Up @@ -852,3 +770,40 @@ func Test_Sync_RateLimitingPlugin(t *testing.T) {
})
}
}

func Test_Sync_SkipCACert(t *testing.T) {
// setup stage
client, err := getTestClient()
if err != nil {
t.Errorf(err.Error())
}

tests := []struct {
name string
kongFile string
expectedState utils.KongRawState
}{
{
name: "syncing with --skip-ca-certificates should ignore CA certs",
kongFile: "testdata/sync/008-skip-ca-cert/kong.yaml",
expectedState: utils.KongRawState{
Services: svc1_207,
CACertificates: []*kong.CACertificate{},
},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
// ca_certificates first appeared in 1.3, but we limit to 2.7+
// here because the schema changed and the entities aren't the same
// across all versions, even though the skip functionality works the same.
kong.RunWhenKong(t, ">=2.7.0")
teardown := setup(t)
defer teardown(t)

sync(tc.kongFile, "--skip-ca-certificates")
testKongState(t, client, tc.expectedState, nil)
})
}
}
102 changes: 102 additions & 0 deletions tests/integration/test_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//nolint:unused,deadcode
package integration

import (
"context"
"os"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/kong/deck/cmd"
"github.com/kong/deck/dump"
"github.com/kong/deck/utils"
"github.com/kong/go-kong/kong"
)

func getKongAddress() string {
address := os.Getenv("DECK_KONG_ADDR")
if address != "" {
return address
}
return "http://localhost:8001"
}

func getTestClient() (*kong.Client, error) {
return utils.GetKongClient(utils.KongClientConfig{
Address: getKongAddress(),
})
}

func sortSlices(x, y interface{}) bool {
var xName, yName string
switch xEntity := x.(type) {
case *kong.Service:
yEntity := y.(*kong.Service)
xName = *xEntity.Name
yName = *yEntity.Name
case *kong.Route:
yEntity := y.(*kong.Route)
xName = *xEntity.Name
yName = *yEntity.Name
case *kong.Plugin:
yEntity := y.(*kong.Plugin)
xName = *xEntity.Name
yName = *yEntity.Name
}
return xName < yName
}

func testKongState(t *testing.T, client *kong.Client,
expectedState utils.KongRawState, ignoreFields []cmp.Option) {
// Get entities from Kong
ctx := context.Background()
kongState, err := dump.Get(ctx, client, dump.Config{})
if err != nil {
t.Errorf(err.Error())
}

opt := []cmp.Option{
cmpopts.IgnoreFields(kong.Service{}, "ID", "CreatedAt", "UpdatedAt"),
cmpopts.IgnoreFields(kong.Route{}, "ID", "CreatedAt", "UpdatedAt"),
cmpopts.IgnoreFields(kong.Plugin{}, "ID", "CreatedAt"),
cmpopts.IgnoreFields(kong.Upstream{}, "ID", "CreatedAt"),
cmpopts.IgnoreFields(kong.Target{}, "ID", "CreatedAt"),
cmpopts.IgnoreFields(kong.CACertificate{}, "ID", "CreatedAt"),
cmpopts.SortSlices(sortSlices),
cmpopts.EquateEmpty(),
}
opt = append(opt, ignoreFields...)

if diff := cmp.Diff(kongState, &expectedState, opt...); diff != "" {
t.Errorf(diff)
}
}

func reset(t *testing.T, opts ...string) {
deckCmd := cmd.NewRootCmd()
args := []string{"reset", "--force"}
if len(opts) > 0 {
args = append(args, opts...)
}
deckCmd.SetArgs(args)
if err := deckCmd.Execute(); err != nil {
t.Fatalf(err.Error(), "failed to reset Kong's state")
}
}

func setup(t *testing.T) func(t *testing.T) {
return func(t *testing.T) {
reset(t)
}
}

func sync(kongFile string, opts ...string) {
deckCmd := cmd.NewRootCmd()
args := []string{"sync", "-s", kongFile}
if len(opts) > 0 {
args = append(args, opts...)
}
deckCmd.SetArgs(args)
deckCmd.ExecuteContext(context.Background()) //nolint:errcheck
}
Loading

0 comments on commit c94ddb2

Please sign in to comment.