From 253d55a7e375733a53c0b78882f05cfa5993dfa5 Mon Sep 17 00:00:00 2001 From: Prem Kumar Kalle Date: Tue, 7 Nov 2023 10:16:01 -0800 Subject: [PATCH] Compatibility tests to validate k8s and tanzu context mutual exclusion behavior Signed-off-by: Prem Kumar Kalle --- test/compatibility/core/api_constants.go | 1 + .../context/context_test.go | 34 ++++++++++ .../framework/context/context_commands.go | 65 +++++++++++++++++++ .../framework/context/context_helpers.go | 57 ++++++++++++++++ .../framework/context/context_options.go | 21 ++++++ .../framework/context/context_validators.go | 38 ++++++++++- .../framework/types/cfg_options.go | 2 +- .../framework/types/cfg_validators.go | 6 +- .../cmd/trigger_apis.go | 1 + .../cmd/trigger_context_apis.go | 34 ++++++++++ 10 files changed, 255 insertions(+), 4 deletions(-) diff --git a/test/compatibility/core/api_constants.go b/test/compatibility/core/api_constants.go index d1e44c57d..1983589cd 100644 --- a/test/compatibility/core/api_constants.go +++ b/test/compatibility/core/api_constants.go @@ -16,6 +16,7 @@ const ( SetCurrentContextAPI RuntimeAPIName = "SetCurrentContext" GetCurrentContextAPI RuntimeAPIName = "GetCurrentContext" RemoveCurrentContextAPI RuntimeAPIName = "RemoveCurrentContext" + GetActiveContextAPI RuntimeAPIName = "GetActiveContext" SetServerAPI RuntimeAPIName = "SetServer" AddServerAPI RuntimeAPIName = "AddServer" diff --git a/test/compatibility/framework/compatibilitytests/context/context_test.go b/test/compatibility/framework/compatibilitytests/context/context_test.go index af7c74a15..6ecac25e1 100644 --- a/test/compatibility/framework/compatibilitytests/context/context_test.go +++ b/test/compatibility/framework/compatibilitytests/context/context_test.go @@ -12,6 +12,7 @@ import ( "github.com/vmware-tanzu/tanzu-plugin-runtime/test/compatibility/framework/context" "github.com/vmware-tanzu/tanzu-plugin-runtime/test/compatibility/framework/executer" "github.com/vmware-tanzu/tanzu-plugin-runtime/test/compatibility/framework/legacyclientconfig" + "github.com/vmware-tanzu/tanzu-plugin-runtime/test/compatibility/framework/types" ) var _ = ginkgo.Describe("Cross-version Context APIs compatibility tests", func() { @@ -1254,6 +1255,39 @@ var _ = ginkgo.Describe("Cross-version Context APIs compatibility tests", func() executer.Execute(testCase) }) + }) + ginkgo.Context("Using tanzu and k8s context types objects on supported Runtime API versions to validate mutual exclusion behavior", func() { + ginkgo.It("Run SetContext, SetCurrentContext, GetActiveContext latest then SetContext,SetCurrentContext, GetCurrentContext ,GetActiveContext, v1.0.2 then DeleteContext latest then SetContext, SetCurrentContext, GetCurrentContext v1.0.2 then SetContext, SetCurrentContext, GetActiveContext latest then GetCurrentContextCommand v1.0.2", func() { + testCase := core.NewTestCase() + // When latest plugin version sets tanzu context as current and later old plugin API version sets k8s context as active, + // the k8s context type and tanzu context types should be active which CLI would inspect and remove the tanzu context type from current contexts + testCase.Add(context.SetContextCommand(context.WithContextType(types.ContextTypeTanzu))) + testCase.Add(context.SetCurrentContextCommand()) + testCase.Add(context.GetActiveContextCommand(context.WithContextType(types.ContextTypeTanzu))) + + testCase.Add(context.SetContextCommand(context.WithRuntimeVersion(core.Version102), context.WithContextName(common.CompatibilityTestTwo))) + testCase.Add(context.SetCurrentContextCommand(context.WithRuntimeVersion(core.Version102), context.WithContextName(common.CompatibilityTestTwo))) + testCase.Add(context.GetCurrentContextCommand(context.WithRuntimeVersion(core.Version102), context.WithContextName(common.CompatibilityTestTwo))) + + testCase.Add(context.GetActiveContextCommand(context.WithContextType(types.ContextTypeTanzu))) + testCase.Add(context.GetActiveContextCommand(context.WithContextName(common.CompatibilityTestTwo))) + + // When old plugin API version sets k8s context as active and later if latest plugin version sets tanzu context as current, + // the k8s context type should be removed from the current context list automatically + testCase.Add(context.DeleteContextCommand(context.WithRuntimeVersion(core.Version102))) + testCase.Add(context.DeleteContextCommand(context.WithRuntimeVersion(core.Version102), context.WithContextName(common.CompatibilityTestTwo))) + testCase.Add(context.SetContextCommand(context.WithRuntimeVersion(core.Version102), context.WithContextName(common.CompatibilityTestTwo))) + testCase.Add(context.SetCurrentContextCommand(context.WithRuntimeVersion(core.Version102), context.WithContextName(common.CompatibilityTestTwo))) + testCase.Add(context.GetCurrentContextCommand(context.WithRuntimeVersion(core.Version102), context.WithContextName(common.CompatibilityTestTwo))) + + testCase.Add(context.SetContextCommand(context.WithContextType(types.ContextTypeTanzu))) + testCase.Add(context.SetCurrentContextCommand()) + testCase.Add(context.GetActiveContextCommand(context.WithContextType(types.ContextTypeTanzu))) + testCase.Add(context.GetActiveContextCommand(context.WithError())) + testCase.Add(context.GetCurrentContextCommand(context.WithRuntimeVersion(core.Version102), context.WithError())) + + executer.Execute(testCase) + }) }) }) diff --git a/test/compatibility/framework/context/context_commands.go b/test/compatibility/framework/context/context_commands.go index 0ed068deb..f75a2382d 100644 --- a/test/compatibility/framework/context/context_commands.go +++ b/test/compatibility/framework/context/context_commands.go @@ -301,6 +301,71 @@ func NewGetCurrentContextCommand(inputOpts *GetCurrentContextInputOptions, outpu return c, nil } +// NewGetActiveContextCommand constructs a command to make a call to specific runtime version GetActiveContext API +// Input Parameter inputOpts has all input parameters which are required for Runtime GetActiveContext API +// Input Parameter: outputOpts has details about expected output from Runtime GetActiveContext API call +// Return: command to execute or error if any validations fails for GetActiveContextInputOptions or GetActiveContextOutputOptions +// This method does validate the input parameters GetActiveContextInputOptions or GetActiveContextOutputOptions based on Runtime API Version +// For more details about supported parameters refer to GetActiveContextInputOptions or GetActiveContextOutputOptions definition(and ContextOpts struct, which is embedded) +func NewGetActiveContextCommand(inputOpts *GetActiveContextInputOptions, outputOpts *GetActiveContextOutputOptions) (*core.Command, error) { + // Init the Command object + c := &core.Command{} + // Init the API object + api := &core.API{Name: core.GetActiveContextAPI} + + // Validate the Input Options + _, err := inputOpts.Validate() + if err != nil { + return nil, err + } + + // Set API version + api.Version = inputOpts.RuntimeVersion + + // Construct the context api arguments and output + api.Arguments = make(map[core.APIArgumentType]interface{}) + + if inputOpts.ContextType != "" { + api.Arguments[core.ContextType] = inputOpts.ContextType + } + + // Construct Output parameters + var res = core.Success + var content = "" + + if outputOpts.ContextOpts != nil { + // Validate the Output Options + _, err = outputOpts.Validate() + if err != nil { + return nil, err + } + + // Construct get active context output context opts + bytes, err := yaml.Marshal(outputOpts.ContextOpts) + if err != nil { + return nil, err + } + + content = string(bytes) + res = core.Success + } else if outputOpts.Error != "" { + res = core.Failed + content = outputOpts.Error + } + + api.Output = &core.Output{ + Result: res, + Content: content, + } + + if outputOpts.ValidationStrategy != "" { + api.Output.ValidationStrategy = outputOpts.ValidationStrategy + } + + c.APIs = append(c.APIs, api) + return c, nil +} + // NewRemoveCurrentContextCommand constructs a command to make a call to specific runtime version RemoveCurrentContext API // Input Parameter inputOpts has all input parameters which are required for Runtime RemoveCurrentContext API // Input Parameter: outputOpts has details about expected output from Runtime RemoveCurrentContext API call diff --git a/test/compatibility/framework/context/context_helpers.go b/test/compatibility/framework/context/context_helpers.go index da8637c9f..7dbbd775d 100644 --- a/test/compatibility/framework/context/context_helpers.go +++ b/test/compatibility/framework/context/context_helpers.go @@ -382,3 +382,60 @@ func SetCurrentContextCommand(opts ...CfgContextArgsOption) *core.Command { return cmd } + +func GetActiveContextCommand(opts ...CfgContextArgsOption) *core.Command { + args := &CfgContextArgs{ + RuntimeAPIVersion: &core.RuntimeAPIVersion{ + RuntimeVersion: core.VersionLatest, + }, + ContextName: common.CompatibilityTestOne, + Target: types.TargetK8s, + ContextType: types.ContextTypeK8s, + GlobalOpts: &types.GlobalServerOpts{ + Endpoint: common.DefaultEndpoint, + }, + } + + for _, opt := range opts { + opt(args) + } + + var inputOpts *GetActiveContextInputOptions + var outputOpts *GetActiveContextOutputOptions + + switch args.RuntimeAPIVersion.RuntimeVersion { + case core.VersionLatest: + inputOpts = &GetActiveContextInputOptions{ + RuntimeAPIVersion: args.RuntimeAPIVersion, + ContextType: args.ContextType, + } + if args.Error { + outputOpts = &GetActiveContextOutputOptions{ + RuntimeAPIVersion: args.RuntimeAPIVersion, + Error: fmt.Sprintf("no current context set for type \"%v\"", args.ContextType), + } + } else { + outputOpts = &GetActiveContextOutputOptions{ + RuntimeAPIVersion: args.RuntimeAPIVersion, + ContextOpts: &types.ContextOpts{ + Name: args.ContextName, + Target: types.Target(args.ContextType), + ContextType: args.ContextType, + GlobalOpts: args.GlobalOpts, + }, + ValidationStrategy: core.ValidationStrategyStrict, + } + } + default: + // add runtime version and context type for unsupported versions + inputOpts = &GetActiveContextInputOptions{ + RuntimeAPIVersion: args.RuntimeAPIVersion, + ContextType: args.ContextType, + } + } + + cmd, err := NewGetActiveContextCommand(inputOpts, outputOpts) + gomega.Expect(err).To(gomega.BeNil()) + + return cmd +} diff --git a/test/compatibility/framework/context/context_options.go b/test/compatibility/framework/context/context_options.go index 7ec02f0a9..8d2348919 100644 --- a/test/compatibility/framework/context/context_options.go +++ b/test/compatibility/framework/context/context_options.go @@ -40,6 +40,13 @@ func WithTarget(target types.Target) CfgContextArgsOption { func WithContextType(contextType types.ContextType) CfgContextArgsOption { return func(c *CfgContextArgs) { c.ContextType = contextType + c.Target = "" + } +} + +func WithSetCurrentContext() CfgContextArgsOption { + return func(c *CfgContextArgs) { + c.SetCurrentContext = true } } @@ -130,6 +137,20 @@ type GetCurrentContextOutputOptions struct { Error string // expected error message could be the sub string of actual error message } +// GetActiveContextInputOptions used to generate GetActiveContext command +type GetActiveContextInputOptions struct { + *core.RuntimeAPIVersion // required + ContextType types.ContextType // required for v1.1.0 +} + +// GetActiveContextOutputOptions used to generate GetActiveContext command +type GetActiveContextOutputOptions struct { + *core.RuntimeAPIVersion // required + *types.ContextOpts // For specific version options look into ContextOpts definition + ValidationStrategy core.ValidationStrategy // Type of validation to be performed i.e. exact or partial. default is partial + Error string // expected error message could be the sub string of actual error message +} + // RemoveCurrentContextInputOptions used to generate RemoveCurrentContext command type RemoveCurrentContextInputOptions struct { *core.RuntimeAPIVersion // required diff --git a/test/compatibility/framework/context/context_validators.go b/test/compatibility/framework/context/context_validators.go index 2609acdbe..738ed16ab 100644 --- a/test/compatibility/framework/context/context_validators.go +++ b/test/compatibility/framework/context/context_validators.go @@ -19,6 +19,10 @@ func (opts *GetCurrentContextInputOptions) ShouldNotIncludeContextType() bool { return opts.ContextType == "" } +func (opts *GetActiveContextInputOptions) ShouldIncludeContextType() bool { + return opts.ContextType != "" +} + func (opts *RemoveCurrentContextInputOptions) ShouldNotIncludeTarget() bool { return opts.Target == "" } @@ -32,7 +36,19 @@ func (opts *SetContextInputOptions) Validate() (bool, error) { } switch opts.RuntimeVersion { - case core.VersionLatest, core.Version102, core.Version090, core.Version0280: + case core.VersionLatest: + if !opts.ValidName() { + return false, fmt.Errorf("invalid 'name' for set context input options for the specified runtime version %v", opts.RuntimeVersion) + } + if !opts.ValidContextType() { + return false, fmt.Errorf("invalid 'ContextType' for set context input options for the specified runtime version %v", opts.RuntimeVersion) + } + if !opts.ValidGlobalOptsOrClusterOpts() { + return false, fmt.Errorf("invalid 'global or clusterOpts' for set context input options for the specified runtime version %v", opts.RuntimeVersion) + } + return true, nil + + case core.Version102, core.Version090, core.Version0280: if !opts.ValidName() { return false, fmt.Errorf("invalid 'name' for set context input options for the specified runtime version %v", opts.RuntimeVersion) } @@ -47,7 +63,7 @@ func (opts *SetContextInputOptions) Validate() (bool, error) { if !opts.ValidName() { return false, fmt.Errorf("invalid 'Name' for set context input options for the specified runtime version %v", opts.RuntimeVersion) } - if !opts.ValidContextType() { + if !opts.ValidType() { return false, fmt.Errorf("invalid 'ContextType' for set context input options for the specified runtime version %v", opts.RuntimeVersion) } if !opts.ValidGlobalOptsOrClusterOpts() { @@ -175,3 +191,21 @@ func (opts *GetCurrentContextOutputOptions) Validate() (bool, error) { return false, errors.New("GetCurrentContext API is not supported for the specified runtime version") } } + +// Validate the opts as per runtime version i.e. check whether the expected fields are supported for the runtime version specified +func (opts *GetActiveContextInputOptions) Validate() (bool, error) { + _, err := opts.RuntimeAPIVersion.Validate() + if err != nil { + return false, err + } + + switch opts.RuntimeVersion { + case core.VersionLatest: + if !opts.ShouldIncludeContextType() { + return false, fmt.Errorf("invalid get current context input options for the specified runtime version contextType is not provided %v", opts.RuntimeVersion) + } + return true, nil + default: + return false, fmt.Errorf("GetActiveContext API is not supported for the specified runtime version %v", opts.RuntimeVersion) + } +} diff --git a/test/compatibility/framework/types/cfg_options.go b/test/compatibility/framework/types/cfg_options.go index 73e52f87d..05f638e1c 100644 --- a/test/compatibility/framework/types/cfg_options.go +++ b/test/compatibility/framework/types/cfg_options.go @@ -48,7 +48,7 @@ const ( ContextTypeK8s ContextType = "kubernetes" // ContextTypeTMC is a Tanzu Mission Control type of context. - ContextTypeTMC ContextType = "misson-control" + ContextTypeTMC ContextType = "mission-control" // ContextTypeTanzu is a Tanzu control plane type of context. ContextTypeTanzu ContextType = "tanzu" diff --git a/test/compatibility/framework/types/cfg_validators.go b/test/compatibility/framework/types/cfg_validators.go index 3f82e629d..8514c11dc 100644 --- a/test/compatibility/framework/types/cfg_validators.go +++ b/test/compatibility/framework/types/cfg_validators.go @@ -55,9 +55,13 @@ func (opts *ContextOpts) ValidTarget() bool { return opts.Target != "" && (opts.Target == TargetK8s || opts.Target == TargetTMC) } -func (opts *ContextOpts) ValidContextType() bool { +// ValidType validates legacy context type +func (opts *ContextOpts) ValidType() bool { return opts.Type != "" && (opts.Type == CtxTypeK8s || opts.Type == CtxTypeTMC) } +func (opts *ContextOpts) ValidContextType() bool { + return opts.ContextType != "" && (opts.ContextType == ContextTypeK8s || opts.ContextType == ContextTypeTMC || opts.ContextType == ContextTypeTanzu) +} func (opts *ContextOpts) ValidGlobalOptsOrClusterOpts() bool { return (opts.GlobalOpts != nil && opts.GlobalOpts.Endpoint != "") || (opts.ClusterOpts != nil && opts.ClusterOpts.Endpoint != "") diff --git a/test/compatibility/testplugins/runtime-test-plugin-latest/cmd/trigger_apis.go b/test/compatibility/testplugins/runtime-test-plugin-latest/cmd/trigger_apis.go index d5556869f..d919878b5 100644 --- a/test/compatibility/testplugins/runtime-test-plugin-latest/cmd/trigger_apis.go +++ b/test/compatibility/testplugins/runtime-test-plugin-latest/cmd/trigger_apis.go @@ -19,6 +19,7 @@ var apiHandlers = map[core.RuntimeAPIName]func(*core.API) *core.APIResponse{ core.SetCurrentContextAPI: triggerSetCurrentContextAPI, core.GetCurrentContextAPI: triggerGetCurrentContextAPI, core.RemoveCurrentContextAPI: triggerRemoveCurrentContextAPI, + core.GetActiveContextAPI: triggerGetActiveContextAPI, // Server APIs core.SetServerAPI: triggerSetServerAPI, diff --git a/test/compatibility/testplugins/runtime-test-plugin-latest/cmd/trigger_context_apis.go b/test/compatibility/testplugins/runtime-test-plugin-latest/cmd/trigger_context_apis.go index 4f6918104..55463fcfd 100644 --- a/test/compatibility/testplugins/runtime-test-plugin-latest/cmd/trigger_context_apis.go +++ b/test/compatibility/testplugins/runtime-test-plugin-latest/cmd/trigger_context_apis.go @@ -82,6 +82,20 @@ func triggerGetCurrentContextAPI(api *core.API) *core.APIResponse { return getCurrentContext(configtypes.Target(target)) } +// triggerGetActiveContextAPI trigger Runtime GetActiveContext API +func triggerGetActiveContextAPI(api *core.API) *core.APIResponse { + // Parse arguments needed to trigger the Runtime GetActiveContext API + ctxTypeStr, err := core.ParseStr(api.Arguments[core.ContextType]) + if err != nil { + return &core.APIResponse{ + ResponseType: core.ErrorResponse, + ResponseBody: fmt.Errorf("failed to parse string from argument %v with error %v ", core.ContextType, err.Error()), + } + } + // Trigger GetActiveContext API + return getActiveContext(configtypes.ContextType(ctxTypeStr)) +} + // triggerRemoveCurrentContextAPI trigger Runtime RemoveCurrentContext API func triggerRemoveCurrentContextAPI(api *core.API) *core.APIResponse { // Parse arguments needed to trigger the Runtime RemoveCurrentContext API @@ -178,6 +192,26 @@ func getCurrentContext(target configtypes.Target) *core.APIResponse { } } +func getActiveContext(contextType configtypes.ContextType) *core.APIResponse { + ctx, err := configlib.GetActiveContext(contextType) + if err != nil { + return &core.APIResponse{ + ResponseType: core.ErrorResponse, + ResponseBody: err.Error(), + } + } + if ctx == nil { + return &core.APIResponse{ + ResponseType: core.ErrorResponse, + ResponseBody: fmt.Errorf("context %s not found", contextType), + } + } + return &core.APIResponse{ + ResponseType: core.MapResponse, + ResponseBody: ctx, + } +} + func removeCurrentContext(target configtypes.Target) *core.APIResponse { err := configlib.RemoveCurrentContext(target) //nolint:staticcheck // Deprecated if err != nil {