From 00e7c4ac583a435154300745cd6e44fb2965f510 Mon Sep 17 00:00:00 2001 From: Antoine Jacquemin Date: Thu, 28 Nov 2024 20:09:37 +0100 Subject: [PATCH] list refactor --- cmd/gateway_validate.go | 30 +++++- tests/integration/validate_test.go | 4 +- validate/validate.go | 161 ++++++++++++++--------------- 3 files changed, 104 insertions(+), 91 deletions(-) diff --git a/cmd/gateway_validate.go b/cmd/gateway_validate.go index 37fe97e2d..561c7fe57 100644 --- a/cmd/gateway_validate.go +++ b/cmd/gateway_validate.go @@ -17,9 +17,9 @@ import ( ) var ( - validateCmdKongStateFile []string + validateCmdKongStateFile []string + validateCmdOnlineEntitiesFilter []string validateCmdRBACResourcesOnly bool - validateCmdCheckOnlinePluginsOnly bool validateOnline bool validateWorkspace string validateParallelism int @@ -211,6 +211,26 @@ this command unless --online flag is used. if len(validateCmdKongStateFile) == 0 { validateCmdKongStateFile = []string{"-"} } + + if validateCmdOnlineEntitiesFilter != nil { + // Iterate over the input values and validate them against the keys in entityMap + for _, value := range validateCmdOnlineEntitiesFilter { + // Check if the value is valid by comparing it with keys in entityMap + isValid := false + for key := range validate.EntityMap { + if value == key { + isValid = true + break + } + } + + if !isValid { + // Generate an error message stating that the value should be part of Kong entities + return fmt.Errorf("invalid value '%s' for --check-online-plugins-only; it should be a valid Kong entity", + value) + } + } + } return preRunSilenceEventsFlag() } @@ -238,8 +258,8 @@ this command unless --online flag is used. validateCmd.Flags().BoolVar(&validateCmdRBACResourcesOnly, "rbac-resources-only", false, "indicate that the state file(s) contains RBAC resources only (Kong Enterprise only).") - validateCmd.Flags().BoolVar(&validateCmdCheckOnlinePluginsOnly, "check-online-plugins-only", - false, "indicate that the online validation will be done only on plugins.") + validateCmd.Flags().StringSliceVarP(&validateCmdOnlineEntitiesFilter, "online-entities-list", + "", []string{}, "indicate the list of entitied that should be validate online validation.") if deprecated { validateCmd.Flags().StringSliceVarP(&validateCmdKongStateFile, "state", "s", []string{"kong.yaml"}, "file(s) containing Kong's configuration.\n"+ @@ -287,7 +307,7 @@ func validateWithKong( Client: kongClient, Parallelism: validateParallelism, RBACResourcesOnly: validateCmdRBACResourcesOnly, - CheckOnlinePluginsOnly: validateCmdCheckOnlinePluginsOnly, + OnlineEntitiesFilter: validateCmdOnlineEntitiesFilter, } validator := validate.NewValidator(opts) return validator.Validate(parsedFormatVersion) diff --git a/tests/integration/validate_test.go b/tests/integration/validate_test.go index 6b2468137..7aacae87c 100644 --- a/tests/integration/validate_test.go +++ b/tests/integration/validate_test.go @@ -134,9 +134,9 @@ func Test_Validate_File(t *testing.T) { additionalArgs: []string{"--rbac-resources-only"}, }, { - name: "file validate with --check-online-plugins-only", + name: "file validate with --online-entities-list", stateFile: "testdata/validate/kong3x.yaml", - additionalArgs: []string{"--check-online-plugins-only"}, + additionalArgs: []string{"--online-entities-list Service,Routes"}, }, } diff --git a/validate/validate.go b/validate/validate.go index baeb572a6..a03135605 100644 --- a/validate/validate.go +++ b/validate/validate.go @@ -16,31 +16,55 @@ import ( ) type Validator struct { - ctx context.Context - state *state.KongState - client *kong.Client - parallelism int - rbacResourcesOnly bool - CheckOnlinePluginsOnly bool + ctx context.Context + state *state.KongState + client *kong.Client + parallelism int + rbacResourcesOnly bool + onlineEntitiesFilter []string } type ValidatorOpts struct { - Ctx context.Context - State *state.KongState - Client *kong.Client - Parallelism int - RBACResourcesOnly bool - CheckOnlinePluginsOnly bool + Ctx context.Context + State *state.KongState + Client *kong.Client + Parallelism int + RBACResourcesOnly bool + OnlineEntitiesFilter []string +} + +// Define a map of entity object field names and their corresponding string names +var EntityMap = map[string]string{ + "RBACEndpointPermissions": "rbac-endpointpermission", + "RBACRoles": "rbac-role", + "Plugins": "plugins", + "Services": "services", + "ACLGroups": "acls", + "BasicAuths": "basicauth_credentials", + "CACertificates": "ca_certificates", + "Certificates": "certificates", + "Consumers": "consumers", + "Documents": "documents", + "HMACAuths": "hmacauth_credentials", + "JWTAuths": "jwt_secrets", + "KeyAuths": "keyauth_credentials", + "Oauth2Creds": "oauth2_credentials", + "Routes": "routes", + "SNIs": "snis", + "Targets": "targets", + "Upstreams": "upstreams", + "FilterChains": "filter_chains", + "Vaults": "vaults", } func NewValidator(opt ValidatorOpts) *Validator { return &Validator{ - ctx: opt.Ctx, - state: opt.State, - client: opt.Client, - parallelism: opt.Parallelism, - rbacResourcesOnly: opt.RBACResourcesOnly, - CheckOnlinePluginsOnly: opt.CheckOnlinePluginsOnly, + ctx: opt.Ctx, + state: opt.State, + client: opt.Client, + parallelism: opt.Parallelism, + rbacResourcesOnly: opt.RBACResourcesOnly, + onlineEntitiesFilter: opt.OnlineEntitiesFilter, } } @@ -122,76 +146,45 @@ func (v *Validator) entities(obj interface{}, entityType string) []error { func (v *Validator) Validate(formatVersion semver.Version) []error { allErr := []error{} - // validate RBAC resources first. - if err := v.entities(v.state.RBACEndpointPermissions, "rbac-endpointpermission"); err != nil { - allErr = append(allErr, err...) - } - if err := v.entities(v.state.RBACRoles, "rbac-role"); err != nil { - allErr = append(allErr, err...) - } if v.rbacResourcesOnly { + // validate RBAC resources first. + if err := v.entities(v.state.RBACEndpointPermissions, "rbac-endpointpermission"); err != nil { + allErr = append(allErr, err...) + } + if err := v.entities(v.state.RBACRoles, "rbac-role"); err != nil { + allErr = append(allErr, err...) + } return allErr } - // validate Plugins resources then. - if err := v.entities(v.state.Plugins, "plugins"); err != nil { - allErr = append(allErr, err...) - } - if v.CheckOnlinePluginsOnly { - return allErr - } - - if err := v.entities(v.state.Services, "services"); err != nil { - allErr = append(allErr, err...) - } - if err := v.entities(v.state.ACLGroups, "acls"); err != nil { - allErr = append(allErr, err...) - } - if err := v.entities(v.state.BasicAuths, "basicauth_credentials"); err != nil { - allErr = append(allErr, err...) - } - if err := v.entities(v.state.CACertificates, "ca_certificates"); err != nil { - allErr = append(allErr, err...) - } - if err := v.entities(v.state.Certificates, "certificates"); err != nil { - allErr = append(allErr, err...) - } - if err := v.entities(v.state.Consumers, "consumers"); err != nil { - allErr = append(allErr, err...) - } - if err := v.entities(v.state.Documents, "documents"); err != nil { - allErr = append(allErr, err...) - } - if err := v.entities(v.state.HMACAuths, "hmacauth_credentials"); err != nil { - allErr = append(allErr, err...) - } - if err := v.entities(v.state.JWTAuths, "jwt_secrets"); err != nil { - allErr = append(allErr, err...) - } - if err := v.entities(v.state.KeyAuths, "keyauth_credentials"); err != nil { - allErr = append(allErr, err...) - } - if err := v.entities(v.state.Oauth2Creds, "oauth2_credentials"); err != nil { - allErr = append(allErr, err...) - } - if err := v.entities(v.state.Routes, "routes"); err != nil { - allErr = append(allErr, err...) - } - if err := v.entities(v.state.SNIs, "snis"); err != nil { - allErr = append(allErr, err...) - } - if err := v.entities(v.state.Targets, "targets"); err != nil { - allErr = append(allErr, err...) - } - if err := v.entities(v.state.Upstreams, "upstreams"); err != nil { - allErr = append(allErr, err...) - } - if err := v.entities(v.state.FilterChains, "filter_chains"); err != nil { - allErr = append(allErr, err...) - } - if err := v.entities(v.state.Vaults, "vaults"); err != nil { - allErr = append(allErr, err...) - } + // Create a copy of entityMap with only the specififed resources to check online. + filteredEntityMap := make(map[string]string) + if v.onlineEntitiesFilter != nil { + for _, value := range v.onlineEntitiesFilter { + for key, entityName := range EntityMap { + if value == entityName { + filteredEntityMap[key] = entityName + } + } + } + } else { + // If no filter is specified, use the original entityMap. + filteredEntityMap = EntityMap + } + + + // Validate each entity using the filtered entityMap + for fieldName, entityName := range filteredEntityMap { + // Use reflection to get the value of the field from v.state + fieldValue := reflect.ValueOf(v.state).FieldByName(fieldName) + if fieldValue.IsValid() && fieldValue.CanInterface() { + if err := v.entities(fieldValue.Interface(), entityName); err != nil { + allErr = append(allErr, err...) + } + } else { + allErr = append(allErr, fmt.Errorf("invalid field '%s' in state", fieldName)) + } + } // validate routes format with Kong 3.x parsed30, err := semver.ParseTolerant(utils.FormatVersion30)