diff --git a/internal/dataplane/kong_client_test.go b/internal/dataplane/kong_client_test.go index 837e8722b3..8957bbc4ea 100644 --- a/internal/dataplane/kong_client_test.go +++ b/internal/dataplane/kong_client_test.go @@ -251,7 +251,7 @@ func (m *mockUpdateStrategy) Type() string { return "Mock" } -// mockConfigurationChangeDetector is a mock implementation of sendconfig.ConfigurationGetter. +// mockConfigurationChangeDetector is a mock implementation of sendconfig.ConfigurationChangeDetector. type mockConfigurationChangeDetector struct { hasConfigurationChanged bool status kong.Status diff --git a/internal/dataplane/kongstate/config_fetcher.go b/internal/dataplane/kongstate/config_fetcher.go index 28584236d2..e0b56eafc8 100644 --- a/internal/dataplane/kongstate/config_fetcher.go +++ b/internal/dataplane/kongstate/config_fetcher.go @@ -73,7 +73,9 @@ func KongRawStateToKongState(rawstate *utils.KongRawState) *KongState { Route: sanitizeKongRoute(*r), Plugins: []kong.Plugin{}, }) - kongState.Services[i].Routes[j].Plugins = rawPluginsToPlugins(pluginsByRoute[*r.ID]) + if r.ID != nil { + kongState.Services[i].Routes[j].Plugins = rawPluginsToPlugins(pluginsByRoute[*r.ID]) + } } kongState.Services[i].Plugins = rawPluginsToPlugins(pluginsByService[*s.ID]) } @@ -91,9 +93,103 @@ func KongRawStateToKongState(rawstate *utils.KongRawState) *KongState { kongState.CACertificates = rawCACertificatesToCACertificates(rawstate.CACertificates) kongState.Certificates = rawCertificatesToCertificates(rawstate.Certificates) + for i, consumer := range rawstate.Consumers { + kongState.Consumers = append(kongState.Consumers, Consumer{ + Consumer: sanitizeConsumer(*consumer), + }) + for _, keyAuth := range rawstate.KeyAuths { + if keyAuth.Consumer != nil { + if *keyAuth.Consumer.ID == *consumer.ID { + sanitizeAuth(keyAuth) + kongState.Consumers[i].KeyAuths = append(kongState.Consumers[i].KeyAuths, + &KeyAuth{ + KeyAuth: *keyAuth, + }, + ) + } + } + } + for _, hmacAuth := range rawstate.HMACAuths { + if hmacAuth.Consumer != nil { + if *hmacAuth.Consumer.ID == *consumer.ID { + sanitizeAuth(hmacAuth) + kongState.Consumers[i].HMACAuths = append(kongState.Consumers[i].HMACAuths, + &HMACAuth{ + HMACAuth: *hmacAuth, + }, + ) + } + } + } + for _, jwtAuth := range rawstate.JWTAuths { + if jwtAuth.Consumer != nil { + if *jwtAuth.Consumer.ID == *consumer.ID { + sanitizeAuth(jwtAuth) + kongState.Consumers[i].JWTAuths = append(kongState.Consumers[i].JWTAuths, + &JWTAuth{ + JWTAuth: *jwtAuth, + }, + ) + } + } + } + for _, basicAuth := range rawstate.BasicAuths { + if basicAuth.Consumer != nil { + if *basicAuth.Consumer.ID == *consumer.ID { + sanitizeAuth(basicAuth) + kongState.Consumers[i].BasicAuths = append(kongState.Consumers[i].BasicAuths, + &BasicAuth{ + BasicAuth: *basicAuth, + }, + ) + } + } + } + for _, aclGroup := range rawstate.ACLGroups { + if aclGroup.Consumer != nil { + if *aclGroup.Consumer.ID == *consumer.ID { + sanitizeAuth(aclGroup) + kongState.Consumers[i].ACLGroups = append(kongState.Consumers[i].ACLGroups, + &ACLGroup{ + ACLGroup: *aclGroup, + }, + ) + } + } + } + for _, oauth2Cred := range rawstate.Oauth2Creds { + if oauth2Cred.Consumer != nil { + if *oauth2Cred.Consumer.ID == *consumer.ID { + sanitizeAuth(oauth2Cred) + kongState.Consumers[i].Oauth2Creds = append(kongState.Consumers[i].Oauth2Creds, + &Oauth2Credential{ + Oauth2Credential: *oauth2Cred, + }, + ) + } + } + } + for _, mTLSAuth := range rawstate.MTLSAuths { + if mTLSAuth.Consumer != nil { + if *mTLSAuth.Consumer.ID == *consumer.ID { + sanitizeAuth(mTLSAuth) + kongState.Consumers[i].MTLSAuths = append(kongState.Consumers[i].MTLSAuths, + &MTLSAuth{ + MTLSAuth: *mTLSAuth, + }, + ) + } + } + } + } + return kongState } +// ----------------------------------------------------------------------------- +// KongRawState to KongState conversion functions +// ----------------------------------------------------------------------------- + func rawPluginsToPlugins(plugins []*kong.Plugin) []kong.Plugin { if len(plugins) == 0 { return nil @@ -126,7 +222,7 @@ func rawCertificatesToCertificates(certificates []*kong.Certificate) []Certifica for _, c := range certificates { certs = append(certs, Certificate{ - Certificate: *c, + Certificate: sanitizeCertificate(*c), }) } return certs @@ -139,14 +235,18 @@ func rawCACertificatesToCACertificates(caCertificates []*kong.CACertificate) []k certs := []kong.CACertificate{} for _, c := range caCertificates { - certs = append(certs, *c) + certs = append(certs, sanitizeCACertificate(*c)) } return certs } +// ----------------------------------------------------------------------------- +// Sanitization functions +// ----------------------------------------------------------------------------- + func sanitizeKongService(service kong.Service) kong.Service { - service.CreatedAt = nil service.ID = nil + service.CreatedAt = nil service.UpdatedAt = nil return service } @@ -177,3 +277,68 @@ func sanitizePlugin(plugin kong.Plugin) kong.Plugin { plugin.Route = nil return plugin } + +func sanitizeCertificate(certificate kong.Certificate) kong.Certificate { + certificate.ID = nil + certificate.CreatedAt = nil + return certificate +} + +func sanitizeCACertificate(caCertificate kong.CACertificate) kong.CACertificate { + caCertificate.ID = nil + caCertificate.CreatedAt = nil + return caCertificate +} + +func sanitizeConsumer(consumer kong.Consumer) kong.Consumer { + consumer.ID = nil + consumer.CreatedAt = nil + return consumer +} + +type authT interface { + *kong.KeyAuth | + *kong.HMACAuth | + *kong.JWTAuth | + *kong.BasicAuth | + *kong.ACLGroup | + *kong.Oauth2Credential | + *kong.MTLSAuth +} + +func sanitizeAuth[t authT](auth t) { + switch a := (interface{})(auth).(type) { + case *kong.KeyAuth: + a.ID = nil + a.CreatedAt = nil + a.Consumer = nil + case *kong.HMACAuth: + a.ID = nil + a.CreatedAt = nil + a.Consumer = nil + case *kong.JWTAuth: + a.ID = nil + a.CreatedAt = nil + a.Consumer = nil + case *kong.BasicAuth: + a.ID = nil + a.CreatedAt = nil + a.Consumer = nil + case *kong.ACLGroup: + a.ID = nil + a.CreatedAt = nil + a.Consumer = nil + case *kong.Oauth2Credential: + a.ID = nil + a.CreatedAt = nil + a.Consumer = nil + case *kong.MTLSAuth: + a.ID = nil + a.CreatedAt = nil + a.Consumer = nil + if a.CACertificate != nil { + a.CACertificate.ID = nil + a.CACertificate.CreatedAt = nil + } + } +} diff --git a/internal/dataplane/kongstate/config_fetcher_test.go b/internal/dataplane/kongstate/config_fetcher_test.go index a3fcfaa502..68d6454895 100644 --- a/internal/dataplane/kongstate/config_fetcher_test.go +++ b/internal/dataplane/kongstate/config_fetcher_test.go @@ -71,6 +71,87 @@ func TestKongRawStateToKongState(t *testing.T) { }, }, }, + Certificates: []*kong.Certificate{ + { + ID: kong.String("certificate"), + Cert: kong.String("cert"), + }, + }, + CACertificates: []*kong.CACertificate{ + { + ID: kong.String("CACertificate"), + Cert: kong.String("cert"), + }, + }, + Consumers: []*kong.Consumer{ + { + ID: kong.String("consumer"), + CustomID: kong.String("customID"), + }, + }, + KeyAuths: []*kong.KeyAuth{ + { + ID: kong.String("keyAuth"), + Key: kong.String("key"), + Consumer: &kong.Consumer{ + ID: kong.String("consumer"), + }, + }, + }, + HMACAuths: []*kong.HMACAuth{ + { + ID: kong.String("hmacAuth"), + Consumer: &kong.Consumer{ + ID: kong.String("consumer"), + }, + Username: kong.String("username"), + }, + }, + JWTAuths: []*kong.JWTAuth{ + { + ID: kong.String("jwtAuth"), + Consumer: &kong.Consumer{ + ID: kong.String("consumer"), + }, + Key: kong.String("key"), + }, + }, + BasicAuths: []*kong.BasicAuth{ + { + ID: kong.String("basicAuth"), + Consumer: &kong.Consumer{ + ID: kong.String("consumer"), + }, + Username: kong.String("username"), + }, + }, + ACLGroups: []*kong.ACLGroup{ + { + ID: kong.String("basicAuth"), + Consumer: &kong.Consumer{ + ID: kong.String("consumer"), + }, + Group: kong.String("group"), + }, + }, + Oauth2Creds: []*kong.Oauth2Credential{ + { + ID: kong.String("basicAuth"), + Consumer: &kong.Consumer{ + ID: kong.String("consumer"), + }, + Name: kong.String("name"), + }, + }, + MTLSAuths: []*kong.MTLSAuth{ + { + ID: kong.String("basicAuth"), + Consumer: &kong.Consumer{ + ID: kong.String("consumer"), + }, + SubjectName: kong.String("subjectName"), + }, + }, }, expectedKongState: &kongstate.KongState{ Services: []kongstate.Service{ @@ -111,10 +192,79 @@ func TestKongRawStateToKongState(t *testing.T) { }, }, }, + Certificates: []kongstate.Certificate{ + { + Certificate: kong.Certificate{ + Cert: kong.String("cert"), + }, + }, + }, + CACertificates: []kong.CACertificate{ + { + Cert: kong.String("cert"), + }, + }, + Consumers: []kongstate.Consumer{ + { + Consumer: kong.Consumer{ + CustomID: kong.String("customID"), + }, + KeyAuths: []*kongstate.KeyAuth{ + { + KeyAuth: kong.KeyAuth{ + Key: kong.String("key"), + }, + }, + }, + HMACAuths: []*kongstate.HMACAuth{ + { + HMACAuth: kong.HMACAuth{ + Username: kong.String("username"), + }, + }, + }, + JWTAuths: []*kongstate.JWTAuth{ + { + JWTAuth: kong.JWTAuth{ + Key: kong.String("key"), + }, + }, + }, + BasicAuths: []*kongstate.BasicAuth{ + { + BasicAuth: kong.BasicAuth{ + Username: kong.String("username"), + }, + }, + }, + ACLGroups: []*kongstate.ACLGroup{ + { + ACLGroup: kong.ACLGroup{ + Group: kong.String("group"), + }, + }, + }, + Oauth2Creds: []*kongstate.Oauth2Credential{ + { + Oauth2Credential: kong.Oauth2Credential{ + Name: kong.String("name"), + }, + }, + }, + MTLSAuths: []*kongstate.MTLSAuth{ + { + MTLSAuth: kong.MTLSAuth{ + SubjectName: kong.String("subjectName"), + }, + }, + }, + }, + }, }, }, } { t.Run(tt.name, func(t *testing.T) { + tt := tt state := kongstate.KongRawStateToKongState(&tt.kongRawState) require.Equal(t, tt.expectedKongState, state) }) @@ -124,7 +274,12 @@ func TestKongRawStateToKongState(t *testing.T) { func TestKongStateToKongRawState_Ensure(t *testing.T) { kongRawStateFieldsKICDoesntSupport := []string{ // These are fields that KIC explicitly doesn't support. - "Vault", + "SNIs", + "ConsumerGroups", + "CustomEntities", + "Vaults", + "RBACRoles", + "RBACEndpointPermissions", } allKongRawStateFields := func() []string { var fields []string @@ -135,37 +290,30 @@ func TestKongStateToKongRawState_Ensure(t *testing.T) { return fields }() - testCases := []struct { - testedFields []string - }{ - { - testedFields: []string{ - "Services", - "Routes", - "Upstreams", - "Targets", - "Plugins", - "Certificates", - "CACertificates", - }, - }, + testedFields := []string{ + "Services", + "Routes", + "Upstreams", + "Targets", + "Plugins", + "Certificates", + "CACertificates", + "Consumers", + "KeyAuths", + "HMACAuths", + "JWTAuths", + "BasicAuths", + "ACLGroups", + "Oauth2Creds", + "MTLSAuths", } - // Kinda meta test - ensure we have testcases covering all fields in KongRawState. + // Meta test - ensure we have testcases covering all fields in KongRawState. for _, field := range allKongRawStateFields { if lo.Contains(kongRawStateFieldsKICDoesntSupport, field) { - t.Logf("skipping field %s - unsupported explicitly", field) + t.Logf("skipping field %s - explicitly unsupported", field) continue } - testCoveringFieldExists := lo.ContainsBy(testCases, func(tc struct{ testedFields []string }) bool { - return lo.Contains(tc.testedFields, field) - }) - assert.True(t, testCoveringFieldExists, "no test covering field %s", field) - } - - // Run the tests. - for _, tc := range testCases { - // ... - _ = tc + assert.True(t, lo.Contains(testedFields, field), "field %s unsupported", field) } }