From 19b98d3203c535392ec405d2272d3521c91614d7 Mon Sep 17 00:00:00 2001 From: Chandra Pamuluri Date: Mon, 24 Apr 2023 16:50:25 -0500 Subject: [PATCH] Plugin sync e2e test cases implementation for tmc target --- pkg/constants/env_variables.go | 1 + pkg/pluginmanager/default_discoveries.go | 8 + test/e2e/Makefile | 69 +- test/e2e/airgapped/airgapped_suite_test.go | 9 +- test/e2e/airgapped/airgapped_test.go | 24 +- test/e2e/coexistence/cli_coexistence_test.go | 25 +- .../context/tmc/context_tmc_lifecycle_test.go | 11 +- test/e2e/framework/cmd_exec_operations.go | 10 +- .../framework/config_lifecycle_operations.go | 7 + .../framework/context_create_operations.go | 13 +- test/e2e/framework/framework.go | 73 ++- test/e2e/framework/framework_helper.go | 161 ++++- test/e2e/framework/output_handling.go | 37 ++ test/e2e/framework/plugin_operations.go | 9 - .../plugin_lifecycle_helper.go | 12 +- .../plugin_lifecycle_suite_test.go | 8 +- .../plugin_sync_k8s_lifecycle_suite_test.go} | 51 +- .../plugin_sync_k8s_lifecycle_test.go} | 160 +++-- .../plugin_sync_tmc_lifecycle_suite_test.go | 118 ++++ .../tmc/plugin_sync_tmc_lifecycle_test.go | 619 ++++++++++++++++++ 20 files changed, 1242 insertions(+), 183 deletions(-) rename test/e2e/plugin_sync/{plugin_sync_lifecycle_suite_test.go => k8s/plugin_sync_k8s_lifecycle_suite_test.go} (71%) rename test/e2e/plugin_sync/{plugin_sync_lifecycle_test.go => k8s/plugin_sync_k8s_lifecycle_test.go} (70%) create mode 100644 test/e2e/plugin_sync/tmc/plugin_sync_tmc_lifecycle_suite_test.go create mode 100644 test/e2e/plugin_sync/tmc/plugin_sync_tmc_lifecycle_test.go diff --git a/pkg/constants/env_variables.go b/pkg/constants/env_variables.go index c832d2882..d2664c9df 100644 --- a/pkg/constants/env_variables.go +++ b/pkg/constants/env_variables.go @@ -18,4 +18,5 @@ const ( PublicKeyPathForPluginDiscoveryImageSignature = "TANZU_CLI_PLUGIN_DISCOVERY_IMAGE_SIGNATURE_PUBLIC_KEY_PATH" SuppressSkipSignatureVerificationWarning = "TANZU_CLI_SUPPRESS_SKIP_SIGNATURE_VERIFICATION_WARNING" CEIPOptInUserPromptAnswer = "TANZU_CLI_CEIP_OPT_IN_PROMPT_ANSWER" + E2ETestEnvironment = "TANZU_CLI_E2E_TEST_ENVIRONMENT" ) diff --git a/pkg/pluginmanager/default_discoveries.go b/pkg/pluginmanager/default_discoveries.go index 368236722..748a73454 100644 --- a/pkg/pluginmanager/default_discoveries.go +++ b/pkg/pluginmanager/default_discoveries.go @@ -5,11 +5,15 @@ package pluginmanager import ( "fmt" + "os" "strings" + "github.com/vmware-tanzu/tanzu-cli/pkg/constants" configtypes "github.com/vmware-tanzu/tanzu-plugin-runtime/config/types" ) +const True = "true" + func defaultDiscoverySourceBasedOnServer(server *configtypes.Server) []configtypes.PluginDiscovery { // nolint:staticcheck // Deprecated var defaultDiscoveries []configtypes.PluginDiscovery // If current server type is management-cluster, then add @@ -57,6 +61,10 @@ func defaultDiscoverySourceForTMCTargetedContext(context *configtypes.Context) c } func appendURLScheme(endpoint string) string { + // At present, the e2e test environment lacks support for HTTPS, thus hardcoding HTTPS is being skipped. + if os.Getenv(constants.E2ETestEnvironment) == True { + return endpoint + } e := strings.Split(endpoint, ":")[0] if !strings.Contains(e, "https") { return fmt.Sprintf("https://%s", e) diff --git a/test/e2e/Makefile b/test/e2e/Makefile index 36a05cf74..1b83d010b 100644 --- a/test/e2e/Makefile +++ b/test/e2e/Makefile @@ -43,9 +43,12 @@ ifndef TANZU_CLI_E2E_AIRGAPPED_REPO TANZU_CLI_E2E_AIRGAPPED_REPO = localhost:6001/tanzu-cli/plugins/ endif -.PHONY: e2e-cli-core-all ## Execute all CLI Core E2E Tests -e2e-cli-core-all: e2e-cli-lifecycle e2e-cli-config e2e-cli-plugin-compatibility-test e2e-context-k8s-tests e2e-context-tmc-test e2e-cli-plugin-lifecycle-sync-test e2e-airgapped-tests +# Set the plugin group name for the plugins used to execute E2E test cases. +E2E_TEST_USE_PLGINS_FROM_PLUGIN_GROUP_FOR_TMC ?= vmware-tmc/v9.9.9 +E2E_TEST_USE_PLGINS_FROM_PLUGIN_GROUP_FOR_K8S ?= vmware-tkg/v9.9.9 +.PHONY: e2e-cli-core-all ## Execute all CLI Core E2E Tests +e2e-cli-core-all: e2e-cli-lifecycle e2e-cli-config e2e-plugin-compatibility-test e2e-plugin-lifecycle-test e2e-plugin-sync-tmc e2e-plugin-sync-k8s e2e-context-tmc-test e2e-context-k8s-tests e2e-airgapped-tests .PHONY: e2e-cli-lifecycle ## Execute CLI life cycle specific e2e tests e2e-cli-lifecycle: @@ -57,35 +60,48 @@ e2e-cli-config: export TANZU_CLI_CEIP_OPT_IN_PROMPT_ANSWER="No" ; \ ${GO} test ${ROOT_DIR}/test/e2e/config -timeout ${E2E_TEST_TIMEOUT} -race -coverprofile ${E2E_TEST_OUTPUT} ${GOTEST_VERBOSE} ; \ -.PHONY: e2e-cli-plugin-compatibility-test ## Execute CLI Core Plugin Compatibility E2E test cases -e2e-cli-plugin-compatibility-test: - @if [ "${TANZU_CLI_E2E_TEST_CENTRAL_REPO_URL}" = "" ]; then \ - echo "***Skipping Plugin Compatibility test cases because environment variables TANZU_CLI_E2E_TEST_CENTRAL_REPO_URL is not set***" ; \ - else \ - export TANZU_CLI_E2E_TEST_CENTRAL_REPO_URL=$(TANZU_CLI_E2E_TEST_CENTRAL_REPO_URL) ; \ - export TANZU_CLI_PLUGIN_DISCOVERY_IMAGE_SIGNATURE_VERIFICATION_SKIP_LIST=$(TANZU_CLI_E2E_TEST_CENTRAL_REPO_URL) ; \ - export TANZU_CLI_CEIP_OPT_IN_PROMPT_ANSWER="No" ; \ - ${GO} test ${ROOT_DIR}/test/e2e/plugins_compatibility -timeout ${E2E_TEST_TIMEOUT} -race -coverprofile ${E2E_TEST_OUTPUT} ${GOTEST_VERBOSE} ; \ - fi +.PHONY: e2e-plugin-compatibility-test ## Execute CLI Core Plugin Compatibility E2E test cases +e2e-plugin-compatibility-test: + export TANZU_CLI_E2E_TEST_CENTRAL_REPO_URL=$(TANZU_CLI_E2E_TEST_CENTRAL_REPO_URL) ; \ + export TANZU_CLI_PLUGIN_DISCOVERY_IMAGE_SIGNATURE_VERIFICATION_SKIP_LIST=$(TANZU_CLI_E2E_TEST_CENTRAL_REPO_URL) ; \ + export TANZU_CLI_CEIP_OPT_IN_PROMPT_ANSWER="No" ; \ + ${GO} test ${ROOT_DIR}/test/e2e/plugins_compatibility -timeout ${E2E_TEST_TIMEOUT} -race -coverprofile ${E2E_TEST_OUTPUT} ${GOTEST_VERBOSE} ; \ + +.PHONY: e2e-plugin-lifecycle-test ## Execute CLI Core Plugin life cycle E2E test cases +e2e-plugin-lifecycle-test: + export TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_URL=$(TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_URL) ; \ + export TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_PLUGIN_DISCOVERY_IMAGE_SIGNATURE_PUBLIC_KEY_PATH=$(TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_PLUGIN_DISCOVERY_IMAGE_SIGNATURE_PUBLIC_KEY_PATH) ; \ + export TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_HOST=${TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_HOST} ; \ + export TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_CA_CERT_PATH=${TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_CA_CERT_PATH} ; \ + export TANZU_CLI_CEIP_OPT_IN_PROMPT_ANSWER="No" ; \ + ${GO} test ${ROOT_DIR}/test/e2e/plugin_lifecycle -timeout ${E2E_TEST_TIMEOUT} -race -coverprofile ${E2E_TEST_OUTPUT} ${GOTEST_VERBOSE} ; \ + +.PHONY: e2e-plugin-sync-k8s ## Execute CLI Core Plugin sync E2E test cases for k8s target +e2e-plugin-sync-k8s: + export TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_URL=$(TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_URL) ; \ + export TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_PLUGIN_DISCOVERY_IMAGE_SIGNATURE_PUBLIC_KEY_PATH=$(TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_PLUGIN_DISCOVERY_IMAGE_SIGNATURE_PUBLIC_KEY_PATH) ; \ + export TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_HOST=${TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_HOST} ; \ + export TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_CA_CERT_PATH=${TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_CA_CERT_PATH} ; \ + export TANZU_CLI_CEIP_OPT_IN_PROMPT_ANSWER="No" ; \ + ${GO} test ${ROOT_DIR}/test/e2e/plugin_sync/k8s -timeout ${E2E_TEST_TIMEOUT} -race -coverprofile ${E2E_TEST_OUTPUT} ${GOTEST_VERBOSE} ; \ -.PHONY: e2e-cli-plugin-lifecycle-sync-test ## Execute CLI Core Plugin life cycle E2E test cases -e2e-cli-plugin-lifecycle-sync-test: - @if [ "${TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_URL}" = "" ]; then \ - echo "***Skipping Plugin life cycle test cases because environment variables TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_URL is not set***" ; \ + +## To run TMC tests, we need to set environment variable TANZU_API_TOKEN, in case of github workflow, its been set as environment variable +.PHONY: e2e-plugin-sync-tmc ## Execute CLI Core Plugin sync E2E test cases for tmc target +e2e-plugin-sync-tmc: + @if [ "${TANZU_API_TOKEN}" = "" ]; then \ + echo "***Skipping TMC specific plugin sync e2e test cases because environment variables TANZU_API_TOKEN is not set***" ; \ else \ export TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_URL=$(TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_URL) ; \ export TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_PLUGIN_DISCOVERY_IMAGE_SIGNATURE_PUBLIC_KEY_PATH=$(TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_PLUGIN_DISCOVERY_IMAGE_SIGNATURE_PUBLIC_KEY_PATH) ; \ export TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_HOST=${TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_HOST} ; \ export TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_CA_CERT_PATH=${TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_CA_CERT_PATH} ; \ export TANZU_CLI_CEIP_OPT_IN_PROMPT_ANSWER="No" ; \ - ${GO} test ${ROOT_DIR}/test/e2e/plugin_lifecycle -timeout ${E2E_TEST_TIMEOUT} -race -coverprofile ${E2E_TEST_OUTPUT} ${GOTEST_VERBOSE} ; \ - ${GO} test ${ROOT_DIR}/test/e2e/plugin_sync -timeout ${E2E_TEST_TIMEOUT} -race -coverprofile ${E2E_TEST_OUTPUT} ${GOTEST_VERBOSE} ; \ + export TANZU_CLI_E2E_TEST_ENVIRONMENT="true" ; \ + export TANZU_API_TOKEN=$(TANZU_API_TOKEN) ; \ + ${GO} test -p 1 ${ROOT_DIR}/test/e2e/plugin_sync/tmc -timeout ${E2E_TEST_TIMEOUT} -race -coverprofile ${E2E_TEST_OUTPUT} ${GOTEST_VERBOSE} ; \ fi -.PHONY: e2e-context-k8s-tests ## Execute CLI context life cycle e2e tests for k8s target -e2e-context-k8s-tests: - export TANZU_CLI_CEIP_OPT_IN_PROMPT_ANSWER="No" ; \ - ${GO} test `go list ${ROOT_DIR}/test/e2e/context/... | grep -v test/e2e/context/tmc` -timeout ${E2E_TEST_TIMEOUT} -race -coverprofile ${E2E_TEST_OUTPUT} ${GOTEST_VERBOSE} ## To run TMC tests, we need to set environment variables TANZU_API_TOKEN and TANZU_CLI_TMC_UNSTABLE_URL, in case of github workflow, these are set as github environment variables .PHONY: e2e-context-tmc-test ## Execute CLI context life cycle e2e tests for tmc target @@ -97,6 +113,15 @@ e2e-context-tmc-test: ${GO} test ${ROOT_DIR}/test/e2e/context/tmc -timeout ${E2E_TEST_TIMEOUT} -race -coverprofile ${E2E_TEST_OUTPUT} ${GOTEST_VERBOSE} ; \ fi +.PHONY: e2e-context-k8s-tests ## Execute CLI context life cycle e2e tests for k8s target +e2e-context-k8s-tests: + export TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_URL=$(TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_URL) ; \ + export TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_PLUGIN_DISCOVERY_IMAGE_SIGNATURE_PUBLIC_KEY_PATH=$(TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_PLUGIN_DISCOVERY_IMAGE_SIGNATURE_PUBLIC_KEY_PATH) ; \ + export TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_HOST=${TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_HOST} ; \ + export TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_CA_CERT_PATH=${TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_CA_CERT_PATH} ; \ + export TANZU_CLI_CEIP_OPT_IN_PROMPT_ANSWER="No" ; \ + ${GO} test `go list ${ROOT_DIR}/test/e2e/context/... | grep -v test/e2e/context/tmc` -timeout ${E2E_TEST_TIMEOUT} -race -coverprofile ${E2E_TEST_OUTPUT} ${GOTEST_VERBOSE} + .PHONY: e2e-airgapped-tests ## Execute CLI airgapped tests e2e-airgapped-tests: export TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_URL=$(TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_IMAGE_FOR_AIRGAPPED) ; \ diff --git a/test/e2e/airgapped/airgapped_suite_test.go b/test/e2e/airgapped/airgapped_suite_test.go index fe821d08c..ece0e5a51 100644 --- a/test/e2e/airgapped/airgapped_suite_test.go +++ b/test/e2e/airgapped/airgapped_suite_test.go @@ -33,6 +33,7 @@ var ( pluginGroupToPluginListMap map[string][]*framework.PluginInfo pluginSourceName string tempDir string + err error ) // BeforeSuite initializes and set up the environment to execute the airgapped tests @@ -68,10 +69,11 @@ var _ = BeforeSuite(func() { // set up the local central repository discovery image public key path e2eTestLocalCentralRepoPluginDiscoveryImageSignaturePublicKeyPath := os.Getenv(framework.TanzuCliE2ETestLocalCentralRepositoryPluginDiscoveryImageSignaturePublicKeyPath) Expect(e2eTestLocalCentralRepoPluginDiscoveryImageSignaturePublicKeyPath).NotTo(BeEmpty(), fmt.Sprintf("environment variable %s should set with local central repository discovery image signature public key path", framework.TanzuCliE2ETestLocalCentralRepositoryPluginDiscoveryImageSignaturePublicKeyPath)) - os.Setenv("TANZU_CLI_PLUGIN_DISCOVERY_IMAGE_SIGNATURE_PUBLIC_KEY_PATH", e2eTestLocalCentralRepoPluginDiscoveryImageSignaturePublicKeyPath) + os.Setenv(framework.TanzuCliPluginDiscoveryImageSignaturePublicKeyPath, e2eTestLocalCentralRepoPluginDiscoveryImageSignaturePublicKeyPath) // search plugin groups and make sure there plugin groups available - pluginGroups = pluginlifecyclee2e.SearchAllPluginGroups(tf) + pluginGroups, err = pluginlifecyclee2e.SearchAllPluginGroups(tf) + Expect(err).To(BeNil(), framework.NoErrorForPluginGroupSearch) // Make sure airgapped plugin inventory image doesn't exists before starting the tests _, _, err = carvelhelpers.GetImageDigest(e2eAirgappedCentralRepoImage) @@ -81,7 +83,8 @@ var _ = BeforeSuite(func() { Expect(framework.IsAllPluginGroupsExists(pluginGroups, framework.PluginGroupsForLifeCycleTests)).Should(BeTrue(), "all required plugin groups for life cycle tests should exists in plugin group search output") // search plugins and make sure there are plugins available - pluginsSearchList = pluginlifecyclee2e.SearchAllPlugins(tf) + pluginsSearchList, err = pluginlifecyclee2e.SearchAllPlugins(tf) + Expect(err).To(BeNil(), framework.NoErrorForPluginSearch) Expect(len(pluginsSearchList)).Should(BeNumerically(">", 0)) // check all required plugins are available in the central repository with plugin search output before running airgapped tests diff --git a/test/e2e/airgapped/airgapped_test.go b/test/e2e/airgapped/airgapped_test.go index 74876e815..ca9aa8b2b 100644 --- a/test/e2e/airgapped/airgapped_test.go +++ b/test/e2e/airgapped/airgapped_test.go @@ -41,7 +41,8 @@ var _ = framework.CLICoreDescribe("[Tests:E2E][Feature:Airgapped-Plugin-Download // Test case: Validate that the correct plugins and plugin group exists with `tanzu plugin search` and `tanzu plugin group search` output It("validate the plugins from group 'vmware-tkg/v0.0.1' exists", func() { // search plugin groups - pluginGroups = pluginlifecyclee2e.SearchAllPluginGroups(tf) + pluginGroups, err = pluginlifecyclee2e.SearchAllPluginGroups(tf) + Expect(err).To(BeNil(), framework.NoErrorForPluginGroupSearch) // check all expected plugin groups are available in the `plugin group search` output from the airgapped repository expectedPluginGroups := []*framework.PluginGroup{{Group: "vmware-tkg/v0.0.1"}} Expect(framework.IsAllPluginGroupsExists(pluginGroups, expectedPluginGroups)).Should(BeTrue(), "all required plugin groups for life cycle tests should exists in plugin group search output") @@ -49,7 +50,8 @@ var _ = framework.CLICoreDescribe("[Tests:E2E][Feature:Airgapped-Plugin-Download // search plugins and make sure correct number of plugins available // check expected plugins are available in the `plugin search` output from the airgapped repository expectedPlugins := pluginsForPG_TKG_001 - pluginsSearchList = pluginlifecyclee2e.SearchAllPlugins(tf) + pluginsSearchList, err = pluginlifecyclee2e.SearchAllPlugins(tf) + Expect(err).To(BeNil(), framework.NoErrorForPluginSearch) Expect(len(pluginsSearchList)).To(Equal(len(expectedPlugins))) Expect(framework.CheckAllPluginsExists(pluginsSearchList, expectedPlugins)).To(BeTrue()) }) @@ -91,14 +93,16 @@ var _ = framework.CLICoreDescribe("[Tests:E2E][Feature:Airgapped-Plugin-Download It("validate the plugins from group 'vmware-tmc/v9.9.9' exists", func() { // search plugin groups and make sure there plugin groups available - pluginGroups = pluginlifecyclee2e.SearchAllPluginGroups(tf) + pluginGroups, err = pluginlifecyclee2e.SearchAllPluginGroups(tf) + Expect(err).To(BeNil(), framework.NoErrorForPluginGroupSearch) // check all expected plugin groups are available in plugin group search output expectedPluginGroups := []*framework.PluginGroup{{Group: "vmware-tkg/v0.0.1"}, {Group: "vmware-tmc/v9.9.9"}} Expect(framework.IsAllPluginGroupsExists(pluginGroups, expectedPluginGroups)).Should(BeTrue(), "all required plugin groups for life cycle tests should exists in plugin group search output") // search plugins and make sure correct number of plugins available // check expected plugins are available in the `plugin search` output from the airgapped repository - pluginsSearchList = pluginlifecyclee2e.SearchAllPlugins(tf) + pluginsSearchList, err = pluginlifecyclee2e.SearchAllPlugins(tf) + Expect(err).To(BeNil(), framework.NoErrorForPluginGroupSearch) expectedPlugins := append(pluginsForPG_TKG_001, pluginsForPG_TMC_999...) Expect(len(pluginsSearchList)).To(Equal(len(expectedPlugins))) Expect(framework.CheckAllPluginsExists(pluginsSearchList, expectedPlugins)).To(BeTrue()) @@ -132,7 +136,8 @@ var _ = framework.CLICoreDescribe("[Tests:E2E][Feature:Airgapped-Plugin-Download It("validate the plugins from group 'vmware-tmc/v0.0.1' exists", func() { // search plugin groups - pluginGroups = pluginlifecyclee2e.SearchAllPluginGroups(tf) + pluginGroups, err = pluginlifecyclee2e.SearchAllPluginGroups(tf) + Expect(err).To(BeNil(), framework.NoErrorForPluginGroupSearch) // check all expected plugin groups are available in the `plugin group search` output from the airgapped repository expectedPluginGroups := []*framework.PluginGroup{{Group: "vmware-tkg/v0.0.1"}, {Group: "vmware-tmc/v9.9.9"}, {Group: "vmware-tmc/v0.0.1"}} Expect(framework.IsAllPluginGroupsExists(pluginGroups, expectedPluginGroups)).Should(BeTrue(), "all required plugin groups for life cycle tests should exists in plugin group search output") @@ -140,7 +145,8 @@ var _ = framework.CLICoreDescribe("[Tests:E2E][Feature:Airgapped-Plugin-Download // search plugins and make sure correct number of plugins available // check expected plugins are available in the `plugin search` output from the airgapped repository expectedPlugins := append(pluginsForPG_TKG_001, pluginsForPG_TMC_999...) - pluginsSearchList = pluginlifecyclee2e.SearchAllPlugins(tf) + pluginsSearchList, err = pluginlifecyclee2e.SearchAllPlugins(tf) + Expect(err).To(BeNil(), framework.NoErrorForPluginGroupSearch) Expect(len(pluginsSearchList)).To(Equal(len(expectedPlugins))) Expect(framework.CheckAllPluginsExists(pluginsSearchList, expectedPlugins)).To(BeTrue()) }) @@ -174,7 +180,8 @@ var _ = framework.CLICoreDescribe("[Tests:E2E][Feature:Airgapped-Plugin-Download // Test case: validate that all plugins and plugin groups exists It("validate that all plugins and plugin groups exists", func() { // search plugin groups and make sure there plugin groups available - pluginGroups = pluginlifecyclee2e.SearchAllPluginGroups(tf) + pluginGroups, err = pluginlifecyclee2e.SearchAllPluginGroups(tf) + Expect(err).To(BeNil(), framework.NoErrorForPluginGroupSearch) // check all expected plugin groups are available in the `plugin group search` output from the airgapped repository expectedPluginGroups := []*framework.PluginGroup{{Group: "vmware-tkg/v0.0.1"}, {Group: "vmware-tkg/v9.9.9"}, {Group: "vmware-tmc/v9.9.9"}, {Group: "vmware-tmc/v0.0.1"}} Expect(framework.IsAllPluginGroupsExists(pluginGroups, expectedPluginGroups)).Should(BeTrue(), "all required plugin groups for life cycle tests should exists in plugin group search output") @@ -183,7 +190,8 @@ var _ = framework.CLICoreDescribe("[Tests:E2E][Feature:Airgapped-Plugin-Download // check expected plugins are available in the `plugin search` output from the airgapped repository expectedPlugins := append(pluginsForPG_TKG_999, pluginsForPG_TMC_999...) expectedPlugins = append(expectedPlugins, pluginsNotInAnyPG_999...) - pluginsSearchList = pluginlifecyclee2e.SearchAllPlugins(tf) + pluginsSearchList, err = pluginlifecyclee2e.SearchAllPlugins(tf) + Expect(err).To(BeNil(), framework.NoErrorForPluginGroupSearch) Expect(len(pluginsSearchList)).To(Equal(len(expectedPlugins))) Expect(framework.CheckAllPluginsExists(pluginsSearchList, expectedPlugins)).To(BeTrue()) }) diff --git a/test/e2e/coexistence/cli_coexistence_test.go b/test/e2e/coexistence/cli_coexistence_test.go index 47fe1237e..5708e07f5 100644 --- a/test/e2e/coexistence/cli_coexistence_test.go +++ b/test/e2e/coexistence/cli_coexistence_test.go @@ -93,14 +93,15 @@ var _ = ginkgo.Describe("CLI Coexistence Tests", func() { setTestLocalCentralRepoCertConfig([]framework.E2EOption{framework.WithTanzuCommandPrefix(framework.TzPrefix)}) // set up the local central repository discovery image signature public key path - os.Setenv("TANZU_CLI_PLUGIN_DISCOVERY_IMAGE_SIGNATURE_PUBLIC_KEY_PATH", e2eTestLocalCentralRepoPluginDiscoveryImageSignaturePublicKeyPath) + os.Setenv(framework.TanzuCliPluginDiscoveryImageSignaturePublicKeyPath, e2eTestLocalCentralRepoPluginDiscoveryImageSignaturePublicKeyPath) ginkgo.By("Update plugin discovery source with test central repo") _, err = tf.PluginCmd.UpdatePluginDiscoverySource(&framework.DiscoveryOptions{Name: "default", SourceType: framework.SourceType, URI: e2eTestLocalCentralRepoURL}, framework.WithTanzuCommandPrefix(framework.TzPrefix)) gomega.Expect(err).To(gomega.BeNil(), "should not get any error for plugin source update") ginkgo.By("Search plugins and make sure there are plugins available using new Tanzu CLI") - pluginsSearchList := pluginlifecyclee2e.SearchAllPlugins(tf, framework.WithTanzuCommandPrefix(framework.TzPrefix)) + pluginsSearchList, err := pluginlifecyclee2e.SearchAllPlugins(tf, framework.WithTanzuCommandPrefix(framework.TzPrefix)) + gomega.Expect(err).To(gomega.BeNil(), framework.NoErrorForPluginSearch) gomega.Expect(len(pluginsSearchList)).Should(gomega.BeNumerically(">", 0)) ginkgo.By("Installing few plugins using new Tanzu CLI") @@ -168,14 +169,15 @@ var _ = ginkgo.Describe("CLI Coexistence Tests", func() { setTestLocalCentralRepoCertConfig([]framework.E2EOption{}) // set up the local central repository discovery image signature public key path - os.Setenv("TANZU_CLI_PLUGIN_DISCOVERY_IMAGE_SIGNATURE_PUBLIC_KEY_PATH", e2eTestLocalCentralRepoPluginDiscoveryImageSignaturePublicKeyPath) + os.Setenv(framework.TanzuCliPluginDiscoveryImageSignaturePublicKeyPath, e2eTestLocalCentralRepoPluginDiscoveryImageSignaturePublicKeyPath) ginkgo.By("Update plugin discovery source with test central repo") _, err = tf.PluginCmd.UpdatePluginDiscoverySource(&framework.DiscoveryOptions{Name: "default", SourceType: framework.SourceType, URI: e2eTestLocalCentralRepoURL}) gomega.Expect(err).To(gomega.BeNil(), "should not get any error for plugin source update") ginkgo.By("Search plugins and make sure there are plugins available using new Tanzu CLI") - pluginsSearchList := pluginlifecyclee2e.SearchAllPlugins(tf) + pluginsSearchList, err := pluginlifecyclee2e.SearchAllPlugins(tf) + gomega.Expect(err).To(gomega.BeNil(), framework.NoErrorForPluginSearch) gomega.Expect(len(pluginsSearchList)).Should(gomega.BeNumerically(">", 0)) ginkgo.By("Installing few plugins using new Tanzu CLI") @@ -238,14 +240,15 @@ var _ = ginkgo.Describe("CLI Coexistence Tests", func() { setTestLocalCentralRepoCertConfig([]framework.E2EOption{}) // set up the local central repository discovery image signature public key path - os.Setenv("TANZU_CLI_PLUGIN_DISCOVERY_IMAGE_SIGNATURE_PUBLIC_KEY_PATH", e2eTestLocalCentralRepoPluginDiscoveryImageSignaturePublicKeyPath) + os.Setenv(framework.TanzuCliPluginDiscoveryImageSignaturePublicKeyPath, e2eTestLocalCentralRepoPluginDiscoveryImageSignaturePublicKeyPath) ginkgo.By("Update plugin discovery source with test central repo") _, err = tf.PluginCmd.UpdatePluginDiscoverySource(&framework.DiscoveryOptions{Name: "default", SourceType: framework.SourceType, URI: e2eTestLocalCentralRepoURL}) gomega.Expect(err).To(gomega.BeNil(), "should not get any error for plugin source update") ginkgo.By("Search plugins and make sure there are plugins available using new Tanzu CLI") - pluginsSearchList := pluginlifecyclee2e.SearchAllPlugins(tf) + pluginsSearchList, err := pluginlifecyclee2e.SearchAllPlugins(tf) + gomega.Expect(err).To(gomega.BeNil(), framework.NoErrorForPluginSearch) gomega.Expect(len(pluginsSearchList)).Should(gomega.BeNumerically(">", 0)) ginkgo.By("Install few plugins using new Tanzu CLI") @@ -318,14 +321,15 @@ var _ = ginkgo.Describe("CLI Coexistence Tests", func() { setTestLocalCentralRepoCertConfig([]framework.E2EOption{framework.WithTanzuCommandPrefix(framework.TzPrefix)}) // set up the local central repository discovery image signature public key path - os.Setenv("TANZU_CLI_PLUGIN_DISCOVERY_IMAGE_SIGNATURE_PUBLIC_KEY_PATH", e2eTestLocalCentralRepoPluginDiscoveryImageSignaturePublicKeyPath) + os.Setenv(framework.TanzuCliPluginDiscoveryImageSignaturePublicKeyPath, e2eTestLocalCentralRepoPluginDiscoveryImageSignaturePublicKeyPath) ginkgo.By("Update plugin discovery source with test central repo") _, err = tf.PluginCmd.UpdatePluginDiscoverySource(&framework.DiscoveryOptions{Name: "default", SourceType: framework.SourceType, URI: e2eTestLocalCentralRepoURL}, framework.WithTanzuCommandPrefix(framework.TzPrefix)) gomega.Expect(err).To(gomega.BeNil(), "should not get any error for plugin source update") ginkgo.By("search plugins and make sure there are plugins available") - pluginsSearchList := pluginlifecyclee2e.SearchAllPlugins(tf, framework.WithTanzuCommandPrefix(framework.TzPrefix)) + pluginsSearchList, err := pluginlifecyclee2e.SearchAllPlugins(tf, framework.WithTanzuCommandPrefix(framework.TzPrefix)) + gomega.Expect(err).To(gomega.BeNil(), framework.NoErrorForPluginSearch) gomega.Expect(len(pluginsSearchList)).Should(gomega.BeNumerically(">", 0)) ginkgo.By("Install few plugins using new Tanzu CLI") @@ -398,14 +402,15 @@ var _ = ginkgo.Describe("CLI Coexistence Tests", func() { setTestLocalCentralRepoCertConfig([]framework.E2EOption{}) // set up the local central repository discovery image signature public key path - os.Setenv("TANZU_CLI_PLUGIN_DISCOVERY_IMAGE_SIGNATURE_PUBLIC_KEY_PATH", e2eTestLocalCentralRepoPluginDiscoveryImageSignaturePublicKeyPath) + os.Setenv(framework.TanzuCliPluginDiscoveryImageSignaturePublicKeyPath, e2eTestLocalCentralRepoPluginDiscoveryImageSignaturePublicKeyPath) ginkgo.By("Update plugin discovery source with test central repo") _, err = tf.PluginCmd.UpdatePluginDiscoverySource(&framework.DiscoveryOptions{Name: "default", SourceType: framework.SourceType, URI: e2eTestLocalCentralRepoURL}) gomega.Expect(err).To(gomega.BeNil(), "should not get any error for plugin source update") ginkgo.By("search plugins and make sure there are plugins available using new Tanzu CLI") - pluginsSearchList := pluginlifecyclee2e.SearchAllPlugins(tf) + pluginsSearchList, err := pluginlifecyclee2e.SearchAllPlugins(tf) + gomega.Expect(err).To(gomega.BeNil(), framework.NoErrorForPluginSearch) gomega.Expect(len(pluginsSearchList)).Should(gomega.BeNumerically(">", 0)) ginkgo.By("Install few plugins using new Tanzu CLI") diff --git a/test/e2e/context/tmc/context_tmc_lifecycle_test.go b/test/e2e/context/tmc/context_tmc_lifecycle_test.go index c8ef061f9..269baa871 100644 --- a/test/e2e/context/tmc/context_tmc_lifecycle_test.go +++ b/test/e2e/context/tmc/context_tmc_lifecycle_test.go @@ -66,7 +66,7 @@ var _ = framework.CLICoreDescribe("[Tests:E2E][Feature:Context-lifecycle-tmc]", // Test case: b. Create context for TMC target with TMC cluster URL as endpoint It("create tmc context with endpoint", func() { ctxName := ContextNamePrefix + framework.RandomString(4) - err := tf.ContextCmd.CreateContextWithEndPointStaging(ctxName, clusterInfo.EndPoint) + _, err := tf.ContextCmd.CreateContextWithEndPointStaging(ctxName, clusterInfo.EndPoint) Expect(err).To(BeNil(), "context should create without any error") Expect(framework.IsContextExists(tf, ctxName)).To(BeTrue(), fmt.Sprintf(ContextShouldExistsAsCreated, ctxName)) contextNames = append(contextNames, ctxName) @@ -74,7 +74,7 @@ var _ = framework.CLICoreDescribe("[Tests:E2E][Feature:Context-lifecycle-tmc]", // Test case: c. (negative test) Create context for TMC target with TMC cluster "incorrect" URL as endpoint It("create tmc context with incorrect endpoint", func() { ctxName := ContextNamePrefix + framework.RandomString(4) - err := tf.ContextCmd.CreateContextWithEndPointStaging(ctxName, framework.RandomString(4)) + _, err := tf.ContextCmd.CreateContextWithEndPointStaging(ctxName, framework.RandomString(4)) Expect(err).ToNot(BeNil()) Expect(strings.Contains(err.Error(), framework.FailedToCreateContext)).To(BeTrue()) Expect(framework.IsContextExists(tf, ctxName)).To(BeFalse(), fmt.Sprintf(ContextShouldNotExists, ctxName)) @@ -84,8 +84,9 @@ var _ = framework.CLICoreDescribe("[Tests:E2E][Feature:Context-lifecycle-tmc]", err := os.Setenv(framework.TanzuAPIToken, framework.RandomString(4)) Expect(err).To(BeNil(), "There should not be any error in setting environment variables.") ctxName := framework.RandomString(4) - err = tf.ContextCmd.CreateContextWithEndPointStaging(ctxName, clusterInfo.EndPoint) - Expect(err).ToNot(BeNil(), "Create context should fail as the TANZU_API_TOKEN was set incorrectly") + _, err = tf.ContextCmd.CreateContextWithEndPointStaging(ctxName, clusterInfo.EndPoint) + os.Setenv(framework.TanzuAPIToken, clusterInfo.APIKey) + Expect(err).ToNot(BeNil()) Expect(strings.Contains(err.Error(), framework.FailedToCreateContext)).To(BeTrue()) Expect(framework.IsContextExists(tf, ctxName)).To(BeFalse(), fmt.Sprintf(ContextShouldNotExists, ctxName)) err = os.Setenv(framework.TanzuAPIToken, clusterInfo.APIKey) @@ -94,7 +95,7 @@ var _ = framework.CLICoreDescribe("[Tests:E2E][Feature:Context-lifecycle-tmc]", // Test case: e. Create context for TMC target with TMC cluster URL as endpoint, and validate the active context, should be recently create context It("create tmc context with endpoint and check active context", func() { ctxName := ContextNamePrefix + framework.RandomString(4) - err := tf.ContextCmd.CreateContextWithEndPointStaging(ctxName, clusterInfo.EndPoint) + _, err := tf.ContextCmd.CreateContextWithEndPointStaging(ctxName, clusterInfo.EndPoint) Expect(err).To(BeNil(), "context should create without any error") Expect(framework.IsContextExists(tf, ctxName)).To(BeTrue(), fmt.Sprintf(ContextShouldExistsAsCreated, ctxName)) contextNames = append(contextNames, ctxName) diff --git a/test/e2e/framework/cmd_exec_operations.go b/test/e2e/framework/cmd_exec_operations.go index fc176fd70..e11ec8f3e 100644 --- a/test/e2e/framework/cmd_exec_operations.go +++ b/test/e2e/framework/cmd_exec_operations.go @@ -34,20 +34,24 @@ func NewCmdOps() CmdOps { // TanzuCmdExec executes the tanzu command by default uses `tanzu` prefix func (co *cmdOps) TanzuCmdExec(command string, opts ...E2EOption) (stdOut, stdErr *bytes.Buffer, err error) { - // Default options + // Get Default options, and initialize Tanzu Command Prefix value as 'tanzu' options := NewE2EOptions( WithTanzuCommandPrefix(TanzuPrefix), ) - // Apply provided options + // Apply the options provided, which allow the user to override the Tanzu command prefix value, such as 'tz' for _, opt := range opts { opt(options) } - // Verify if the tanzu prefix is set if not set it with default tanzu prefix value + // Verify whether the Tanzu prefix is set and if not, set it to the default Tanzu prefix value if strings.Index(command, "%s") == 0 { command = fmt.Sprintf(command, options.TanzuCommandPrefix) } + // If any additional flags are added, then append them to the command + if options.AdditionalFlags != "" { + command += options.AdditionalFlags + } return co.Exec(command) } diff --git a/test/e2e/framework/config_lifecycle_operations.go b/test/e2e/framework/config_lifecycle_operations.go index 9315ac976..1ba3426c9 100644 --- a/test/e2e/framework/config_lifecycle_operations.go +++ b/test/e2e/framework/config_lifecycle_operations.go @@ -52,6 +52,9 @@ type ConfigCertOps interface { // ConfigCertDelete deletes cert config for a host, and returns error info ConfigCertDelete(host string, opts ...E2EOption) error + + // ConfigCertList list cert + ConfigCertList(opts ...E2EOption) ([]*CertDetails, error) } type ConfigCmdOps interface { @@ -187,3 +190,7 @@ func (co *configOps) ConfigCertDelete(host string, opts ...E2EOption) error { _, _, err := co.cmdExe.TanzuCmdExec(certDeleteCmd, opts...) return err } + +func (co *configOps) ConfigCertList(opts ...E2EOption) ([]*CertDetails, error) { + return ExecuteCmdAndBuildJSONOutput[CertDetails](co.cmdExe, ConfigCertList, opts...) +} diff --git a/test/e2e/framework/context_create_operations.go b/test/e2e/framework/context_create_operations.go index d2ceb3829..fe8c8b934 100644 --- a/test/e2e/framework/context_create_operations.go +++ b/test/e2e/framework/context_create_operations.go @@ -15,8 +15,8 @@ import ( type ContextCreateOps interface { // CreateContextWithEndPoint creates a context with a given endpoint URL CreateContextWithEndPoint(contextName, endpoint string, opts ...E2EOption) error - // CreateContextWithEndPointStaging creates a context with a given endpoint URL for staging - CreateContextWithEndPointStaging(contextName, endpoint string, opts ...E2EOption) error + // CreateContextWithEndPointStaging creates a context with a given endpoint URL for staging, returns stdout and error + CreateContextWithEndPointStaging(contextName, endpoint string, opts ...E2EOption) (string, error) // CreateContextWithKubeconfig creates a context with the given kubeconfig file path and a context from the kubeconfig file CreateContextWithKubeconfig(contextName, kubeconfigPath, kubeContext string, opts ...E2EOption) error // CreateContextWithDefaultKubeconfig creates a context with the default kubeconfig file and a given input context name if it exists in the default kubeconfig file @@ -45,15 +45,16 @@ func (cc *contextCreateOps) CreateContextWithEndPoint(contextName, endpoint stri return err } -func (cc *contextCreateOps) CreateContextWithEndPointStaging(contextName, endpoint string, opts ...E2EOption) error { +func (cc *contextCreateOps) CreateContextWithEndPointStaging(contextName, endpoint string, opts ...E2EOption) (string, error) { createContextCmd := fmt.Sprintf(CreateContextWithEndPointStaging, "%s", endpoint, contextName) - out, _, err := cc.cmdExe.TanzuCmdExec(createContextCmd, opts...) + out, stdErr, err := cc.cmdExe.TanzuCmdExec(createContextCmd, opts...) + log.Infof("out:%s stdErr:%s", out.String(), stdErr.String()) if err != nil { log.Info(fmt.Sprintf(FailedToCreateContextWithStdout, out.String())) - return errors.Wrap(err, fmt.Sprintf(FailedToCreateContextWithStdout, out.String())) + return out.String(), errors.Wrap(err, fmt.Sprintf(FailedToCreateContextWithStdout, out.String())) } log.Infof(ContextCreated, contextName) - return err + return out.String(), err } func (cc *contextCreateOps) CreateContextWithKubeconfig(contextName, kubeconfigPath, kubeContext string, opts ...E2EOption) error { diff --git a/test/e2e/framework/framework.go b/test/e2e/framework/framework.go index 4da4123e6..42c7787f4 100644 --- a/test/e2e/framework/framework.go +++ b/test/e2e/framework/framework.go @@ -7,6 +7,7 @@ package framework import ( "os" "path/filepath" + "time" "github.com/onsi/ginkgo/v2" ) @@ -28,6 +29,7 @@ const ( ConfigServerDelete = "%s config server delete %s -y" ConfigCertAdd = "%s config cert add --host %s --ca-certificate %s --skip-cert-verify %s --insecure %s" ConfigCertDelete = "%s config cert delete %s" + ConfigCertList = "%s config cert list -o json" // Plugin commands UpdatePluginSource = "%s plugin source update %s --uri %s" @@ -56,6 +58,7 @@ const ( TanzuCliE2ETestCentralRepositoryURL = "TANZU_CLI_E2E_TEST_CENTRAL_REPO_URL" TanzuCliE2ETestLocalCentralRepositoryURL = "TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_URL" TanzuCliE2ETestLocalCentralRepositoryPluginDiscoveryImageSignaturePublicKeyPath = "TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_PLUGIN_DISCOVERY_IMAGE_SIGNATURE_PUBLIC_KEY_PATH" + TanzuCliPluginDiscoveryImageSignaturePublicKeyPath = "TANZU_CLI_PLUGIN_DISCOVERY_IMAGE_SIGNATURE_PUBLIC_KEY_PATH" TanzuCliE2ETestLocalCentralRepositoryHost = "TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_HOST" TanzuCliE2ETestLocalCentralRepositoryCACertPath = "TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_CA_CERT_PATH" TanzuCliE2ETestAirgappedRepo = "TANZU_CLI_E2E_AIRGAPPED_REPO" @@ -68,6 +71,9 @@ const ( CLICoexistenceNewTanzuCLIVersion = "TANZU_CLI_BUILD_VERSION" CLICoexistenceTanzuCEIPParticipation = "TANZU_CLI_CEIP_OPT_IN_PROMPT_ANSWER" + // This skips hardcoding HTTPS in CLI Core when the E2E tests mock the TMC endpoint + CLIE2ETestEnvironment = "TANZU_CLI_E2E_TEST_ENVIRONMENT" + // General constants True = "true" Installed = "installed" @@ -110,13 +116,15 @@ const ( GlobalTarget = "global" KubernetesTarget = "kubernetes" MissionControlTarget = "mission-control" + TMCPluginGroupPrefix = "vmware-tmc" + K8SPluginGroupPrefix = "vmware-tkg" // log info ExecutingCommand = "Executing command: %s" FileContent = "file: %s , content: %s" // Error messages - UnableToFindPluginForTarget = "unable to find plugin '%s'" + UnableToFindPluginForTarget = "unable to find plugin '%s' with version '%s'" UnableToFindPluginWithVersionForTarget = "unable to find plugin '%s' with version '%s' for target '%s'" UnableToFindPlugin = "unable to find plugin '%s'" InvalidTargetSpecified = "invalid target specified. Please specify correct value of `--target` or `-t` flag from 'global/kubernetes/k8s/mission-control/tmc'" @@ -125,6 +133,8 @@ const ( ErrorLogForCommandWithErrStdErrAndStdOut = "error while executing command:'%s', error:'%s' stdErr:'%s' stdOut: '%s'" FailedToConstructJSONNodeFromOutputAndErrInfo = "failed to construct json node from output:'%s' error:'%s' " FailedToConstructJSONNodeFromOutput = "failed to construct json node from output:'%s'" + NoErrorForPluginGroupSearch = "should not get any error for plugin group search" + NoErrorForPluginSearch = "should not get any error for plugin search" // config related constants FailedToCreateContext = "failed to create context" @@ -133,24 +143,41 @@ const ( ContextDeleted = "context %s deleted successfully" ConfigServerDeleted = "config server %s deleted successfully" FailedToDeleteContext = "failed to delete context" + ContextPrefixK8s = "plugin-sync-k8s-" + ContextPrefixTMC = "plugin-sync-tmc-" + + // TestDir is the directory under $HOME, created during framework initialization, and the $HOME updated as $HOME/$TestDir, to create all Tanzu CLI specific files + // and not to disturb any existing Tanzu CLI files + TestDir = ".tanzu-cli-e2e" + + // TestPluginsDir is the directory under $HOME/$TestDir, to store test plugins for E2E tests + TestPluginsDir = ".e2e-test-plugins" + + // TempDirInTestDirPath is the directory under $HOME/$TestDir, to create temporary files (if any) for E2E test execution + TempDirInTestDirPath = "temp" + + ConfigFolder = ".config" + TanzuFolder = "tanzu" + TanzuPluginsFolder = "tanzu-plugins" + ConfigFile = "config.yaml" + ConfigNGFile = "config-ng.yaml" + K8SCRDFileName = "cli.tanzu.vmware.com_cliplugins.yaml" + Config = "config" + + WiredMockHTTPServerStartCmd = "docker run --rm -d -p 8080:8080 -p 8443:8443 --name %s -v %s:/home/wiremock rodolpheche/wiremock:2.25.1" + HttpMockServerStopCmd = "docker container stop %s" + HttpMockServerName = "wiremock" + defaultTimeout = 5 * time.Second + + TMCEndpointForPlugins = "/v1alpha1/system/binaries/plugins" + TMCMockServerEndpoint = "http://localhost:8080" + TMCPluginsMockServerEndpoint = "http://localhost:8080/v1alpha1/system/binaries/plugins" + HttpContentType = "application/json; charset=utf-8" + + // k8s CRD file + K8SCRDFilePath = "../../framework/config/cli.tanzu.vmware.com_cliplugins.yaml" ) -// TestDir is the directory under $HOME, created during framework initialization, and the $HOME updated as $HOME/$TestDir, to create all Tanzu CLI specific files -// and not to disturb any existing Tanzu CLI files -const TestDir = ".tanzu-cli-e2e" - -// TestPluginsDir is the directory under $HOME/$TestDir, to store test plugins for E2E tests -const TestPluginsDir = ".e2e-test-plugins" - -// TempDirInTestDirPath is the directory under $HOME/$TestDir, to create temporary files (if any) for E2E test execution -const TempDirInTestDirPath = "temp" - -const ConfigFolder = ".config" -const TanzuFolder = "tanzu" -const TanzuPluginsFolder = "tanzu-plugins" -const ConfigFile = "config.yaml" -const ConfigNGFile = "config-ng.yaml" - var ( // TestDirPath is the absolute directory path for the E2E test execution uses to create all Tanzu CLI specific files (config, local plugins etc) TestDirPath string @@ -184,6 +211,7 @@ func CLICoreDescribe(text string, body func()) bool { // Framework has all helper functions to write CLI e2e test cases type Framework struct { + Exec CmdOps CliOps Config ConfigCmdOps KindCluster ClusterOps @@ -197,6 +225,7 @@ type Framework struct { type E2EOptions struct { TanzuCommandPrefix string CLIOptions + AdditionalFlags string } // CLIOptions used to configure certain options that are used in CLI lifecycle operations @@ -239,8 +268,16 @@ func WithOverride(override bool) E2EOption { } } +// AddAdditionalFlagAndValue is to add any additional flag with value (if any) to the end of tanzu command +func AddAdditionalFlagAndValue(flagWithValue string) E2EOption { + return func(opts *E2EOptions) { + opts.AdditionalFlags = opts.AdditionalFlags + " " + flagWithValue + } +} + func NewFramework() *Framework { return &Framework{ + Exec: NewCmdOps(), CliOps: NewCliOps(), Config: NewConfOps(), KindCluster: NewKindCluster(NewDocker()), @@ -263,7 +300,7 @@ func init() { // Create a directory (if not exists) $HOME/.tanzu-cli-e2e/.config/tanzu-plugins/discovery/standalone TestStandalonePluginsPath = filepath.Join(filepath.Join(filepath.Join(filepath.Join(TestDirPath, ConfigFolder), TanzuPluginsFolder), "discovery"), "standalone") _ = CreateDir(TestStandalonePluginsPath) - // Create a directory (if not exists) $HOME/.tanzu-cli-e2e/test + // Create a directory (if not exists) $HOME/.tanzu-cli-e2e/temp _ = CreateDir(FullPathForTempDir) // TODO:cpamuluri: need to move plugins info to configuration file with positive and negative use cases - github issue: https://github.com/vmware-tanzu/tanzu-cli/issues/122 diff --git a/test/e2e/framework/framework_helper.go b/test/e2e/framework/framework_helper.go index 5de486ab6..07956178f 100644 --- a/test/e2e/framework/framework_helper.go +++ b/test/e2e/framework/framework_helper.go @@ -5,12 +5,16 @@ package framework import ( + "context" "crypto/rand" "encoding/json" "fmt" "math/big" + "net/http" "os" "path/filepath" + "strings" + "time" "github.com/onsi/gomega" "github.com/pkg/errors" @@ -102,7 +106,7 @@ func GetHomeDir() string { } // ExecuteCmdAndBuildJSONOutput is generic function to execute given command and build JSON output and return -func ExecuteCmdAndBuildJSONOutput[T PluginInfo | PluginSearch | PluginGroup | PluginSourceInfo | types.ClientConfig | Server | ContextListInfo](cmdExe CmdOps, cmd string, opts ...E2EOption) ([]*T, error) { +func ExecuteCmdAndBuildJSONOutput[T PluginInfo | PluginSearch | PluginGroup | PluginSourceInfo | types.ClientConfig | Server | ContextListInfo | CertDetails](cmdExe CmdOps, cmd string, opts ...E2EOption) ([]*T, error) { out, stdErr, err := cmdExe.TanzuCmdExec(cmd, opts...) if err != nil { @@ -337,3 +341,158 @@ func GetPluginsList(tf *Framework, installedOnly bool) ([]*PluginInfo, error) { } return out, nil } + +// GetPluginGroupWhichStartsWithGivenPrefix takes plugin groups list and prefix string +// returns first plugin group which starts with the given prefix +func GetPluginGroupWhichStartsWithGivenPrefix(pgs []*PluginGroup, prefix string) string { + for _, pg := range pgs { + if strings.Contains(pg.Group, prefix) { + return pg.Group + } + } + return "" +} + +// StartMockServer starts the http mock server (rodolpheche/wiremock) +func StartMockServer(tf *Framework, mappingDir, containerName string) error { + err := StopContainer(tf, containerName) + if err != nil && !strings.Contains(err.Error(), "No such container") { + return err + } + startServerCmd := fmt.Sprintf(WiredMockHTTPServerStartCmd, containerName, mappingDir) + _, _, err = tf.Exec.Exec(startServerCmd) + // tests are failing randomly if not wait for some time after HTTP mock server started + time.Sleep(2 * time.Second) + return err +} + +// StopContainer stops the given docker container +func StopContainer(tf *Framework, containerName string) error { + cmd := fmt.Sprintf(HttpMockServerStopCmd, containerName) + _, _, err := tf.Exec.Exec(cmd) + return err +} + +// ConvertPluginsInfoToTMCEndpointMockResponse takes the plugins info and converts to TMC endpoint response to mock http calls +func ConvertPluginsInfoToTMCEndpointMockResponse(plugins []*PluginInfo) (*TMCPluginsMockRequestResponseMapping, error) { + tmcPlugins := &TMCPluginsResponse{} + tmcPlugins.PluginsInfo = TMCPluginsInfo{} + tmcPlugins.PluginsInfo.Plugins = make([]TMCPlugin, 0) + for i, _ := range plugins { + tmcPlugin := TMCPlugin{} + tmcPlugin.Name = plugins[i].Name + tmcPlugin.Description = plugins[i].Description + tmcPlugin.RecommendedVersion = plugins[i].Version + tmcPlugins.PluginsInfo.Plugins = append(tmcPlugins.PluginsInfo.Plugins, tmcPlugin) + } + m := &TMCPluginsMockRequestResponseMapping{} + m.Request.Method = "GET" + m.Request.URL = TMCEndpointForPlugins + m.Response.Status = 200 + m.Response.Headers.ContentType = HttpContentType + m.Response.Headers.Accept = HttpContentType + content, err := json.Marshal(tmcPlugins.PluginsInfo) + if err != nil { + log.Error(err, "error while processing input type to json") + return m, err + } + m.Response.Body = string(content) + return m, nil +} + +// WriteToFileInJSONFormat creates (if not exists) and writes the given input type to given file in json format +func WriteToFileInJSONFormat(input any, filePath string) error { + content, err := json.Marshal(input) + if err != nil { + log.Error(err, "error while processing input type to json") + return err + } + err = CreateOrTruncateFile(filePath) + if err != nil { + log.Error(err, fmt.Sprintf("error while creating truncating file %s", filePath)) + return err + } + f, err := os.OpenFile(filePath, os.O_WRONLY, 0644) + if err != nil { + log.Error(err, fmt.Sprintf("error while opening file %s", filePath)) + return err + } + defer f.Close() + _, err = f.Write(content) + if err != nil { + log.Error(err, fmt.Sprintf("error while writing to file %s", filePath)) + return err + } + return nil +} + +// CreateOrTruncateFile creates a given file if not exists +func CreateOrTruncateFile(filePath string) error { + // check if file exists + var _, err = os.Stat(filePath) + // create file if not exists + if os.IsNotExist(err) { + var file, err = os.Create(filePath) + if err != nil { + return err + } + defer file.Close() + } else { + return os.Truncate(filePath, 0) + } + return nil +} + +// CreateDir creates given directory if not exists +func CreateDir(dir string) error { + err := os.MkdirAll(dir, 0750) + if err != nil && !os.IsExist(err) { + log.Fatal(err, fmt.Sprintf("error while creating directory: %s", dir)) + return err + } + return nil +} + +// UpdatePluginDiscoverySource updates the plugin discovery source with given url +func UpdatePluginDiscoverySource(tf *Framework, repoURL string) error { + // setup the test central repo + _, err := tf.PluginCmd.UpdatePluginDiscoverySource(&DiscoveryOptions{Name: "default", SourceType: SourceType, URI: repoURL}) + return err +} + +// ApplyConfigOnKindCluster applies the config files on kind cluster +func ApplyConfigOnKindCluster(tf *Framework, clusterInfo *ClusterInfo, confFilePaths []string) error { + for _, pluginCRFilePaths := range confFilePaths { + err := tf.KindCluster.ApplyConfig(clusterInfo.ClusterKubeContext, pluginCRFilePaths) + if err != nil { + return err + } + } + return nil +} + +// GetHTTPCall queries http GET call on given url +func GetHTTPCall(url string, v interface{}) error { + ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) + defer cancel() + req, err := http.NewRequestWithContext(ctx, "GET", url, http.NoBody) + if err != nil { + return err + } + req.Header.Set("Content-Type", HttpContentType) + req.Header.Set("Accept", HttpContentType) + client := &http.Client{} + response, err := client.Do(req) + if err != nil { + log.Error(err, "error for GET call") + return err + } + defer response.Body.Close() + if response.StatusCode < http.StatusOK || response.StatusCode >= http.StatusBadRequest { + return fmt.Errorf("API error, status code: %d", response.StatusCode) + } + if err := json.NewDecoder(response.Body).Decode(v); err != nil { + return err + } + return nil +} diff --git a/test/e2e/framework/output_handling.go b/test/e2e/framework/output_handling.go index 84e3498fd..4fd95747f 100644 --- a/test/e2e/framework/output_handling.go +++ b/test/e2e/framework/output_handling.go @@ -57,3 +57,40 @@ type Server struct { Path string `json:"path"` Type string `json:"type"` } + +// TMCPluginsResponse is to mock the tmc endpoint response for plugins info +type TMCPluginsResponse struct { + PluginsInfo TMCPluginsInfo `yaml:"pluginsInfo"` +} + +type TMCPlugin struct { + Name string `yaml:"name"` + Description string `yaml:"description"` + RecommendedVersion string `yaml:"recommendedVersion"` +} + +type TMCPluginsInfo struct { + Plugins []TMCPlugin `yaml:"plugins"` +} + +type TMCPluginsMockRequestResponseMapping struct { + Request struct { + Method string `json:"method"` + URL string `json:"url"` + } `json:"request"` + Response struct { + Status int `json:"status"` + Body string `json:"body"` + Headers struct { + ContentType string `json:"Content-Type"` + Accept string `json:"Accept"` + } `json:"headers"` + } `json:"response"` +} + +type CertDetails struct { + CaCertificate string `json:"ca-certificate"` + Host string `json:"host"` + Insecure string `json:"insecure"` + SkipCertVerification string `json:"skip-cert-verification"` +} diff --git a/test/e2e/framework/plugin_operations.go b/test/e2e/framework/plugin_operations.go index e5a849388..377b6f655 100644 --- a/test/e2e/framework/plugin_operations.go +++ b/test/e2e/framework/plugin_operations.go @@ -270,15 +270,6 @@ func (sp *scriptBasedPlugins) generatePluginBinary(pm *PluginMeta) (string, erro return filePath, nil } -// CreateDir creates directory if not exists -func CreateDir(dir string) error { - err := os.MkdirAll(dir, 0750) - if err != nil && !os.IsExist(err) { - return err - } - return nil -} - // generatePluginDiscoveryOverlay creates plugin overly object for given plugin metadata func (po *localOCIPluginOps) generatePluginDiscoveryOverlay(pm *PluginMeta) (plugin *CLIPlugin, err error) { plugin = &CLIPlugin{} diff --git a/test/e2e/plugin_lifecycle/plugin_lifecycle_helper.go b/test/e2e/plugin_lifecycle/plugin_lifecycle_helper.go index 1944030e6..980f94c9a 100644 --- a/test/e2e/plugin_lifecycle/plugin_lifecycle_helper.go +++ b/test/e2e/plugin_lifecycle/plugin_lifecycle_helper.go @@ -5,8 +5,6 @@ package pluginlifecyclee2e import ( - "github.com/onsi/gomega" - "github.com/vmware-tanzu/tanzu-cli/test/e2e/framework" ) @@ -25,15 +23,13 @@ func CheckAllLegacyPluginsExists(superList, subList []*framework.PluginInfo) boo } // SearchAllPlugins runs the plugin search command and returns all the plugins from the search output -func SearchAllPlugins(tf *framework.Framework, opts ...framework.E2EOption) []*framework.PluginInfo { +func SearchAllPlugins(tf *framework.Framework, opts ...framework.E2EOption) ([]*framework.PluginInfo, error) { pluginsSearchList, err := tf.PluginCmd.SearchPlugins("", opts...) - gomega.Expect(err).To(gomega.BeNil(), "should not get any error for plugin search") - return pluginsSearchList + return pluginsSearchList, err } // SearchAllPluginGroups runs the plugin group search command and returns all the plugin groups -func SearchAllPluginGroups(tf *framework.Framework, opts ...framework.E2EOption) []*framework.PluginGroup { +func SearchAllPluginGroups(tf *framework.Framework, opts ...framework.E2EOption) ([]*framework.PluginGroup, error) { pluginGroups, err := tf.PluginCmd.SearchPluginGroups("", opts...) - gomega.Expect(err).To(gomega.BeNil(), "should not get any error for plugin search") - return pluginGroups + return pluginGroups, err } diff --git a/test/e2e/plugin_lifecycle/plugin_lifecycle_suite_test.go b/test/e2e/plugin_lifecycle/plugin_lifecycle_suite_test.go index 92e910906..dcba9e89f 100644 --- a/test/e2e/plugin_lifecycle/plugin_lifecycle_suite_test.go +++ b/test/e2e/plugin_lifecycle/plugin_lifecycle_suite_test.go @@ -53,16 +53,18 @@ var _ = BeforeSuite(func() { // set up the local central repository discovery image public key path e2eTestLocalCentralRepoPluginDiscoveryImageSignaturePublicKeyPath := os.Getenv(framework.TanzuCliE2ETestLocalCentralRepositoryPluginDiscoveryImageSignaturePublicKeyPath) Expect(e2eTestLocalCentralRepoPluginDiscoveryImageSignaturePublicKeyPath).NotTo(BeEmpty(), fmt.Sprintf("environment variable %s should set with local central repository discovery image signature public key path", framework.TanzuCliE2ETestLocalCentralRepositoryPluginDiscoveryImageSignaturePublicKeyPath)) - os.Setenv("TANZU_CLI_PLUGIN_DISCOVERY_IMAGE_SIGNATURE_PUBLIC_KEY_PATH", e2eTestLocalCentralRepoPluginDiscoveryImageSignaturePublicKeyPath) + os.Setenv(framework.TanzuCliPluginDiscoveryImageSignaturePublicKeyPath, e2eTestLocalCentralRepoPluginDiscoveryImageSignaturePublicKeyPath) // search plugin groups and make sure there plugin groups available - pluginGroups = SearchAllPluginGroups(tf) + pluginGroups, err = SearchAllPluginGroups(tf) + Expect(err).To(BeNil(), framework.NoErrorForPluginGroupSearch) // check all required plugin groups (framework.PluginGroupsForLifeCycleTests) need for life cycle test are available in plugin group search output Expect(framework.IsAllPluginGroupsExists(pluginGroups, framework.PluginGroupsForLifeCycleTests)).Should(BeTrue(), "all required plugin groups for life cycle tests should exists in plugin group search output") // search plugins and make sure there are plugins available - pluginsSearchList = SearchAllPlugins(tf) + pluginsSearchList, err = SearchAllPlugins(tf) + Expect(err).To(BeNil(), framework.NoErrorForPluginSearch) Expect(len(pluginsSearchList)).Should(BeNumerically(">", 0)) // check all required plugins (framework.PluginsForLifeCycleTests) for plugin life cycle e2e are available in plugin search output diff --git a/test/e2e/plugin_sync/plugin_sync_lifecycle_suite_test.go b/test/e2e/plugin_sync/k8s/plugin_sync_k8s_lifecycle_suite_test.go similarity index 71% rename from test/e2e/plugin_sync/plugin_sync_lifecycle_suite_test.go rename to test/e2e/plugin_sync/k8s/plugin_sync_k8s_lifecycle_suite_test.go index 2be84363d..2198b07b1 100644 --- a/test/e2e/plugin_sync/plugin_sync_lifecycle_suite_test.go +++ b/test/e2e/plugin_sync/k8s/plugin_sync_k8s_lifecycle_suite_test.go @@ -1,8 +1,8 @@ // Copyright 2023 VMware, Inc. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -// pluginsynce2e provides plugin sync command specific E2E test cases -package pluginsynce2e +// pluginsynce2ek8s provides plugin sync command specific E2E test cases +package pluginsynce2ek8s import ( "fmt" @@ -19,18 +19,20 @@ import ( func TestPluginSyncLifecycle(t *testing.T) { RegisterFailHandler(Fail) - RunSpecs(t, "Plugin-Sync-Lifecycle E2E Test Suite") + RunSpecs(t, "Plugin-Sync-k8s-Lifecycle E2E Test Suite") } var ( - tf *framework.Framework - e2eTestLocalCentralRepoURL string - pluginsSearchList []*framework.PluginInfo - pluginGroups []*framework.PluginGroup - pluginGroupToPluginListMap map[string][]*framework.PluginInfo + tf *framework.Framework + e2eTestLocalCentralRepoURL string + pluginsSearchList []*framework.PluginInfo + pluginGroups []*framework.PluginGroup + pluginGroupToPluginListMap map[string][]*framework.PluginInfo + usePluginsFromPluginGroup string + e2eTestLocalCentralRepoPluginHost string + e2eTestLocalCentralRepoCACertPath string ) -const CRDFilePath = "../framework/config/cli.tanzu.vmware.com_cliplugins.yaml" const numberOfPluginsToInstall = 3 // BeforeSuite initializes and set up the environment to execute the plugin life cycle and plugin group life cycle end-to-end test cases @@ -45,30 +47,40 @@ var _ = BeforeSuite(func() { _, err := tf.PluginCmd.UpdatePluginDiscoverySource(&framework.DiscoveryOptions{Name: "default", SourceType: framework.SourceType, URI: e2eTestLocalCentralRepoURL}) Expect(err).To(BeNil(), "should not get any error for plugin source update") - e2eTestLocalCentralRepoPluginHost := os.Getenv(framework.TanzuCliE2ETestLocalCentralRepositoryHost) + e2eTestLocalCentralRepoPluginHost = os.Getenv(framework.TanzuCliE2ETestLocalCentralRepositoryHost) Expect(e2eTestLocalCentralRepoPluginHost).NotTo(BeEmpty(), fmt.Sprintf("environment variable %s should set with local central repository host", framework.TanzuCliE2ETestLocalCentralRepositoryHost)) - e2eTestLocalCentralRepoCACertPath := os.Getenv(framework.TanzuCliE2ETestLocalCentralRepositoryCACertPath) + e2eTestLocalCentralRepoCACertPath = os.Getenv(framework.TanzuCliE2ETestLocalCentralRepositoryCACertPath) Expect(e2eTestLocalCentralRepoCACertPath).NotTo(BeEmpty(), fmt.Sprintf("environment variable %s should set with local central repository CA cert path", framework.TanzuCliE2ETestLocalCentralRepositoryCACertPath)) - // set up the CA cert fort local central repository + // set up the CA cert for local central repository _ = tf.Config.ConfigCertDelete(e2eTestLocalCentralRepoPluginHost) _, err = tf.Config.ConfigCertAdd(&framework.CertAddOptions{Host: e2eTestLocalCentralRepoPluginHost, CACertificatePath: e2eTestLocalCentralRepoCACertPath, SkipCertVerify: "false", Insecure: "false"}) - Expect(err).To(BeNil()) + Expect(err).To(BeNil(), "should not be any error for cert add") + // list and validate the cert added + list, err := tf.Config.ConfigCertList() + Expect(err).To(BeNil(), "should not be any error for cert list") + Expect(len(list)).To(Equal(1), "should not be any error for cert list") // set up the local central repository discovery image public key path e2eTestLocalCentralRepoPluginDiscoveryImageSignaturePublicKeyPath := os.Getenv(framework.TanzuCliE2ETestLocalCentralRepositoryPluginDiscoveryImageSignaturePublicKeyPath) Expect(e2eTestLocalCentralRepoPluginDiscoveryImageSignaturePublicKeyPath).NotTo(BeEmpty(), fmt.Sprintf("environment variable %s should set with local central repository discovery image signature public key path", framework.TanzuCliE2ETestLocalCentralRepositoryPluginDiscoveryImageSignaturePublicKeyPath)) - os.Setenv("TANZU_CLI_PLUGIN_DISCOVERY_IMAGE_SIGNATURE_PUBLIC_KEY_PATH", e2eTestLocalCentralRepoPluginDiscoveryImageSignaturePublicKeyPath) + os.Setenv(framework.TanzuCliPluginDiscoveryImageSignaturePublicKeyPath, e2eTestLocalCentralRepoPluginDiscoveryImageSignaturePublicKeyPath) // search plugin groups and make sure there plugin groups available - pluginGroups = helper.SearchAllPluginGroups(tf) + pluginGroups, err = helper.SearchAllPluginGroups(tf) + Expect(err).To(BeNil(), framework.NoErrorForPluginGroupSearch) // check all required plugin groups (framework.PluginGroupsForLifeCycleTests) need for life cycle test are available in plugin group search output Expect(framework.IsAllPluginGroupsExists(pluginGroups, framework.PluginGroupsForLifeCycleTests)).Should(BeTrue(), "all required plugin groups for life cycle tests should exists in plugin group search output") + // get k8s plugin group + usePluginsFromPluginGroup = framework.GetPluginGroupWhichStartsWithGivenPrefix(framework.PluginGroupsForLifeCycleTests, framework.K8SPluginGroupPrefix) + Expect(usePluginsFromPluginGroup).NotTo(BeEmpty(), "there should be a k8s specific plugin group") + // search plugins and make sure there are plugins available - pluginsSearchList = helper.SearchAllPlugins(tf) + pluginsSearchList, err = helper.SearchAllPlugins(tf) + Expect(err).To(BeNil(), framework.NoErrorForPluginSearch) Expect(len(pluginsSearchList)).Should(BeNumerically(">", 0)) // check all required plugins (framework.PluginsForLifeCycleTests) for plugin life cycle e2e are available in plugin search output @@ -90,10 +102,3 @@ var _ = AfterSuite(func() { err := os.RemoveAll(framework.FullPathForTempDir) // delete an entire directory Expect(err).To(BeNil(), "should not get any error while deleting temp directory") }) - -func ApplyConfigOnKindCluster(tf *framework.Framework, clusterInfo *framework.ClusterInfo, confFilePaths []string) { - for _, pluginCRFilePaths := range confFilePaths { - err := tf.KindCluster.ApplyConfig(clusterInfo.ClusterKubeContext, pluginCRFilePaths) - Expect(err).To(BeNil(), "should not get any error for config apply") - } -} diff --git a/test/e2e/plugin_sync/plugin_sync_lifecycle_test.go b/test/e2e/plugin_sync/k8s/plugin_sync_k8s_lifecycle_test.go similarity index 70% rename from test/e2e/plugin_sync/plugin_sync_lifecycle_test.go rename to test/e2e/plugin_sync/k8s/plugin_sync_k8s_lifecycle_test.go index 62bcd263a..f58a756cb 100644 --- a/test/e2e/plugin_sync/plugin_sync_lifecycle_test.go +++ b/test/e2e/plugin_sync/k8s/plugin_sync_k8s_lifecycle_test.go @@ -1,8 +1,8 @@ // Copyright 2023 VMware, Inc. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -// pluginsynce2e provides plugin sync command specific E2E test cases -package pluginsynce2e +// pluginsynce2ek8s provides plugin sync command specific E2E test cases +package pluginsynce2ek8s import ( "fmt" @@ -10,11 +10,33 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/vmware-tanzu/tanzu-cli/test/e2e/framework" + f "github.com/vmware-tanzu/tanzu-cli/test/e2e/framework" "github.com/vmware-tanzu/tanzu-plugin-runtime/config/types" ) -var _ = framework.CLICoreDescribe("[Tests:E2E][Feature:Plugin-sync-lifecycle]", func() { +var _ = f.CLICoreDescribe("[Tests:E2E][Feature:Plugin-sync-lifecycle]", func() { + + // cleanup and initialize the config files + Context("Delete config files and initialize", func() { + It("Delete config files and initialize", func() { + err := tf.Config.DeleteCLIConfigurationFiles() + Expect(err).To(BeNil()) + // call init + err = tf.Config.ConfigInit() + Expect(err).To(BeNil()) + + // update plugin discovery source + err = f.UpdatePluginDiscoverySource(tf, e2eTestLocalCentralRepoURL) + Expect(err).To(BeNil(), "should not get any error for plugin source update") + + // Add Cert + _, err = tf.Config.ConfigCertAdd(&f.CertAddOptions{Host: e2eTestLocalCentralRepoPluginHost, CACertificatePath: e2eTestLocalCentralRepoCACertPath, SkipCertVerify: "false", Insecure: "false"}) + Expect(err).To(BeNil(), "should not be any error for cert add") + list, err := tf.Config.ConfigCertList() + Expect(err).To(BeNil(), "should not be any error for cert list") + Expect(len(list)).To(Equal(1), "should not be any error for cert list") + }) + }) // Use case 1: create a KIND cluster, don't apply CRD and CRs, create context, make sure no plugins are installed // a. create k8s context for the KIND cluster @@ -22,19 +44,19 @@ var _ = framework.CLICoreDescribe("[Tests:E2E][Feature:Plugin-sync-lifecycle]", // c. list plugins and make sure no plugins installed // d. delete current context and KIND cluster Context("Use case: Install KIND Cluster, create context and validate plugin sync", func() { - var clusterInfo *framework.ClusterInfo + var clusterInfo *f.ClusterInfo var contextName string var err error // Test case: a. create k8s context for the KIND cluster It("create KIND cluster", func() { // Create KIND cluster, which is used in test cases to create context's - clusterInfo, err = framework.CreateKindCluster(tf, "sync-e2e-"+framework.RandomNumber(4)) + clusterInfo, err = f.CreateKindCluster(tf, f.ContextPrefixK8s+f.RandomNumber(4)) Expect(err).To(BeNil(), "should not get any error for KIND cluster creation") }) // Test case: b. create context and validate current active context It("create context with kubeconfig and context", func() { By("create context with kubeconfig and context") - contextName = "sync-e2e-" + framework.RandomString(4) + contextName = f.ContextPrefixK8s + f.RandomString(4) err := tf.ContextCmd.CreateContextWithKubeconfig(contextName, clusterInfo.KubeConfigPath, clusterInfo.ClusterKubeContext) Expect(err).To(BeNil(), "context should create without any error") active, err := tf.ContextCmd.GetActiveContext(string(types.TargetK8s)) @@ -66,34 +88,35 @@ var _ = framework.CLICoreDescribe("[Tests:E2E][Feature:Plugin-sync-lifecycle]", // run plugin sync, make sure the uninstalled plugin has installed again. // f. delete current context and KIND cluster Context("Use case: Install KIND Cluster, Apply CRD, Apply specific plugin CRs, create context and validate plugin sync", func() { - var clusterInfo *framework.ClusterInfo + var clusterInfo *f.ClusterInfo var pluginCRFilePaths []string - var pluginsInfoForCRsApplied, installedPluginsList []*framework.PluginInfo + var pluginsInfoForCRsApplied, installedPluginsList []*f.PluginInfo var contextName string var err error // Test case: a. create KIND cluster, apply CRD It("create KIND cluster", func() { // Create KIND cluster, which is used in test cases to create context's - clusterInfo, err = framework.CreateKindCluster(tf, "sync-e2e-"+framework.RandomNumber(4)) + clusterInfo, err = f.CreateKindCluster(tf, f.ContextPrefixK8s+f.RandomNumber(4)) Expect(err).To(BeNil(), "should not get any error for KIND cluster creation") }) // Test case: b. apply CRD (cluster resource definition) and CR's (cluster resource) for few plugins It("apply CRD and CRs to KIND cluster", func() { - ApplyConfigOnKindCluster(tf, clusterInfo, append(make([]string, 0), CRDFilePath)) + err = f.ApplyConfigOnKindCluster(tf, clusterInfo, append(make([]string, 0), f.K8SCRDFilePath)) + Expect(err).To(BeNil(), "should not get any error for config apply") - pluginsToGenerateCRs, ok := pluginGroupToPluginListMap[framework.PluginGroupsForLifeCycleTests[0].Group] + pluginsToGenerateCRs, ok := pluginGroupToPluginListMap[usePluginsFromPluginGroup] Expect(ok).To(BeTrue(), "plugin group is not exist in the map") Expect(len(pluginsToGenerateCRs) > numberOfPluginsToInstall).To(BeTrue(), "we don't have enough plugins in local test central repo") - pluginsInfoForCRsApplied, pluginCRFilePaths, err = framework.CreateTemporaryCRsForPluginsInGivenPluginGroup(pluginsToGenerateCRs[:numberOfPluginsToInstall]) + pluginsInfoForCRsApplied, pluginCRFilePaths, err = f.CreateTemporaryCRsForPluginsInGivenPluginGroup(pluginsToGenerateCRs[:numberOfPluginsToInstall]) Expect(err).To(BeNil(), "should not get any error while generating CR files") - ApplyConfigOnKindCluster(tf, clusterInfo, pluginCRFilePaths) + err = f.ApplyConfigOnKindCluster(tf, clusterInfo, pluginCRFilePaths) + Expect(err).To(BeNil(), "should not get any error for config apply") }) // Test case: c. create context and make sure context has created It("create context with kubeconfig and context", func() { - By("create context with kubeconfig and context") - contextName = "sync-e2e-" + framework.RandomString(4) - err := tf.ContextCmd.CreateContextWithKubeconfig(contextName, clusterInfo.KubeConfigPath, clusterInfo.ClusterKubeContext) + contextName = f.ContextPrefixK8s + f.RandomString(4) + err = tf.ContextCmd.CreateContextWithKubeconfig(contextName, clusterInfo.KubeConfigPath, clusterInfo.ClusterKubeContext) Expect(err).To(BeNil(), "context should create without any error") active, err := tf.ContextCmd.GetActiveContext(string(types.TargetK8s)) Expect(err).To(BeNil(), "there should be a active context") @@ -104,7 +127,7 @@ var _ = framework.CLICoreDescribe("[Tests:E2E][Feature:Plugin-sync-lifecycle]", installedPluginsList, err = tf.PluginCmd.ListPluginsForGivenContext(contextName, true) Expect(err).To(BeNil(), "should not get any error for plugin list") Expect(len(installedPluginsList)).Should(Equal(len(pluginsInfoForCRsApplied)), "number of plugins should be same as number of plugins CRs applied") - Expect(framework.CheckAllPluginsExists(installedPluginsList, pluginsInfoForCRsApplied)).Should(BeTrue(), " plugins being installed and plugins info for which CRs applied should be same") + Expect(f.CheckAllPluginsExists(installedPluginsList, pluginsInfoForCRsApplied)).Should(BeTrue(), " plugins being installed and plugins info for which CRs applied should be same") }) // Test case: e. uninstall one of the installed plugin, make sure plugin is uninstalled, @@ -117,10 +140,10 @@ var _ = framework.CLICoreDescribe("[Tests:E2E][Feature:Plugin-sync-lifecycle]", latestPluginsInstalledList := pluginsInfoForCRsApplied[1:] allPluginsList, err := tf.PluginCmd.ListPluginsForGivenContext(contextName, false) Expect(err).To(BeNil(), "should not get any error for plugin list") - installedPluginsList = framework.GetInstalledPlugins(allPluginsList) - Expect(framework.IsPluginExists(allPluginsList, framework.GetGivenPluginFromTheGivenPluginList(allPluginsList, pluginToUninstall), framework.NotInstalled)).To(BeTrue(), "uninstalled plugin should be listed as not installed") + installedPluginsList = f.GetInstalledPlugins(allPluginsList) + Expect(f.IsPluginExists(allPluginsList, f.GetGivenPluginFromTheGivenPluginList(allPluginsList, pluginToUninstall), f.NotInstalled)).To(BeTrue(), "uninstalled plugin should be listed as not installed") Expect(len(installedPluginsList)).Should(Equal(len(latestPluginsInstalledList)), "number of plugins should be same as number of plugins CRs applied") - Expect(framework.CheckAllPluginsExists(installedPluginsList, latestPluginsInstalledList)).Should(BeTrue(), " plugins being installed and plugins info for which CRs applied should be same") + Expect(f.CheckAllPluginsExists(installedPluginsList, latestPluginsInstalledList)).Should(BeTrue(), " plugins being installed and plugins info for which CRs applied should be same") _, err = tf.PluginCmd.Sync() Expect(err).To(BeNil(), "should not get any error for plugin sync") @@ -128,7 +151,7 @@ var _ = framework.CLICoreDescribe("[Tests:E2E][Feature:Plugin-sync-lifecycle]", installedPluginsList, err = tf.PluginCmd.ListPluginsForGivenContext(contextName, true) Expect(err).To(BeNil(), "should not get any error for plugin list") Expect(len(installedPluginsList)).Should(Equal(len(pluginsInfoForCRsApplied)), "number of plugins should be same as number of plugins CRs applied") - Expect(framework.CheckAllPluginsExists(installedPluginsList, pluginsInfoForCRsApplied)).Should(BeTrue(), "plugins being installed and plugins info for which CRs applied should be same") + Expect(f.CheckAllPluginsExists(installedPluginsList, pluginsInfoForCRsApplied)).Should(BeTrue(), "plugins being installed and plugins info for which CRs applied should be same") }) // f. delete current context and the KIND cluster It("delete current context and the KIND cluster", func() { @@ -147,40 +170,43 @@ var _ = framework.CLICoreDescribe("[Tests:E2E][Feature:Plugin-sync-lifecycle]", // e. run plugin sync and validate the plugin list // f. delete the KIND cluster Context("Use case: Install KIND Cluster, Apply CRD, Apply specific plugin CRs, create context and validate plugin sync", func() { - var clusterInfo *framework.ClusterInfo + var clusterInfo *f.ClusterInfo var pluginCRFilePaths, pluginWithIncorrectVerCRFilePaths []string - var pluginsInfoForCRsApplied, pluginsWithIncorrectVer, pluginsList []*framework.PluginInfo + var pluginsInfoForCRsApplied, pluginsWithIncorrectVer, pluginsList []*f.PluginInfo var contextName string var err error // Test case: a. create KIND cluster It("create KIND cluster", func() { // Create KIND cluster, which is used in test cases to create context's - clusterInfo, err = framework.CreateKindCluster(tf, "sync-e2e-"+framework.RandomNumber(4)) + clusterInfo, err = f.CreateKindCluster(tf, f.ContextPrefixK8s+f.RandomNumber(4)) Expect(err).To(BeNil(), "should not get any error for KIND cluster creation") }) // Test case: b. apply CRD (cluster resource definition) and CR's (cluster resource) for few plugins which are available in centra repo // and CR's for plugins which are not available in central repo It("apply CRD and CRs to KIND cluster", func() { - ApplyConfigOnKindCluster(tf, clusterInfo, append(make([]string, 0), CRDFilePath)) - pluginsToGenerateCRs, ok := pluginGroupToPluginListMap[framework.PluginGroupsForLifeCycleTests[0].Group] + err = f.ApplyConfigOnKindCluster(tf, clusterInfo, append(make([]string, 0), f.K8SCRDFilePath)) + Expect(err).To(BeNil(), "should not get any error for config apply") + pluginsToGenerateCRs, ok := pluginGroupToPluginListMap[usePluginsFromPluginGroup] Expect(ok).To(BeTrue(), "plugin group is not exist in the map") Expect(len(pluginsToGenerateCRs) > numberOfPluginsToInstall).To(BeTrue(), "we don't have enough plugins in local test central repo") - pluginsInfoForCRsApplied, pluginCRFilePaths, err = framework.CreateTemporaryCRsForPluginsInGivenPluginGroup(pluginsToGenerateCRs[:numberOfPluginsToInstall]) + pluginsInfoForCRsApplied, pluginCRFilePaths, err = f.CreateTemporaryCRsForPluginsInGivenPluginGroup(pluginsToGenerateCRs[:numberOfPluginsToInstall]) Expect(err).To(BeNil(), "should not get any error while generating CR files") pluginWithIncorrectVersion := *pluginsToGenerateCRs[numberOfPluginsToInstall] - pluginWithIncorrectVersion.Version = pluginWithIncorrectVersion.Version + framework.RandomNumber(2) - pluginsWithIncorrectVer, pluginWithIncorrectVerCRFilePaths, err = framework.CreateTemporaryCRsForPluginsInGivenPluginGroup(append(make([]*framework.PluginInfo, 0), &pluginWithIncorrectVersion)) + pluginWithIncorrectVersion.Version = pluginWithIncorrectVersion.Version + f.RandomNumber(2) + pluginsWithIncorrectVer, pluginWithIncorrectVerCRFilePaths, err = f.CreateTemporaryCRsForPluginsInGivenPluginGroup(append(make([]*f.PluginInfo, 0), &pluginWithIncorrectVersion)) Expect(err).To(BeNil(), "should not get any error while generating CR files") - ApplyConfigOnKindCluster(tf, clusterInfo, pluginCRFilePaths) - ApplyConfigOnKindCluster(tf, clusterInfo, pluginWithIncorrectVerCRFilePaths) + err = f.ApplyConfigOnKindCluster(tf, clusterInfo, pluginCRFilePaths) + Expect(err).To(BeNil(), "should not get any error for config apply") + err = f.ApplyConfigOnKindCluster(tf, clusterInfo, pluginWithIncorrectVerCRFilePaths) + Expect(err).To(BeNil(), "should not get any error for config apply") }) // Test case: c. create context and make sure context has created It("create context with kubeconfig and context", func() { By("create context with kubeconfig and context") - contextName = "sync-e2e-" + framework.RandomString(4) + contextName = f.ContextPrefixK8s + f.RandomString(4) err := tf.ContextCmd.CreateContextWithKubeconfig(contextName, clusterInfo.KubeConfigPath, clusterInfo.ClusterKubeContext) Expect(err).To(BeNil(), "context should create without any error") active, err := tf.ContextCmd.GetActiveContext(string(types.TargetK8s)) @@ -192,18 +218,19 @@ var _ = framework.CLICoreDescribe("[Tests:E2E][Feature:Plugin-sync-lifecycle]", pluginsList, err = tf.PluginCmd.ListPluginsForGivenContext(contextName, true) Expect(err).To(BeNil(), "should not get any error for plugin list") Expect(len(pluginsList)).Should(Equal(len(pluginsInfoForCRsApplied)), "number of plugins should be same as number of plugins CRs applied") - Expect(framework.CheckAllPluginsExists(pluginsList, pluginsInfoForCRsApplied)).Should(BeTrue(), " plugins being installed and plugins info for which CRs applied should be same") + Expect(f.CheckAllPluginsExists(pluginsList, pluginsInfoForCRsApplied)).Should(BeTrue(), " plugins being installed and plugins info for which CRs applied should be same") }) // Test case: e. run plugin sync and validate the plugin list - It("Uninstall one of the installed plugin", func() { + It("run plugin sync and validate err response in plugin sync, validate plugin list output", func() { + // sync should fail with error as there is a plugin which does not exists in repository with the given random version _, err = tf.PluginCmd.Sync() - Expect(err.Error()).To(ContainSubstring(fmt.Sprintf(framework.UnableToFindPluginWithVersionForTarget, pluginsWithIncorrectVer[0].Name, pluginsWithIncorrectVer[0].Version, pluginsWithIncorrectVer[0].Target))) + Expect(err.Error()).To(ContainSubstring(fmt.Sprintf(f.UnableToFindPluginWithVersionForTarget, pluginsWithIncorrectVer[0].Name, pluginsWithIncorrectVer[0].Version, pluginsWithIncorrectVer[0].Target))) pluginsList, err = tf.PluginCmd.ListPluginsForGivenContext(contextName, true) Expect(err).To(BeNil(), "should not get any error for plugin list") Expect(len(pluginsList)).Should(Equal(len(pluginsInfoForCRsApplied)), "number of plugins should be same as number of plugins CRs applied") - Expect(framework.CheckAllPluginsExists(pluginsList, pluginsInfoForCRsApplied)).Should(BeTrue(), "plugins being installed and plugins info for which CRs applied should be same") + Expect(f.CheckAllPluginsExists(pluginsList, pluginsInfoForCRsApplied)).Should(BeTrue(), "plugins being installed and plugins info for which CRs applied should be same") }) // f. delete the KIND cluster It("delete the KIND cluster", func() { @@ -223,39 +250,41 @@ var _ = framework.CLICoreDescribe("[Tests:E2E][Feature:Plugin-sync-lifecycle]", // d. delete the context, make sure all context specific plugins are uninstalled // e. delete the KIND cluster Context("Use case: Install KIND Cluster, Apply CRD, Apply specific plugin CRs, create context and validate plugin sync", func() { - var clusterInfo *framework.ClusterInfo + var clusterInfo *f.ClusterInfo var pluginCRFilePaths []string - var pluginsInfoForCRsApplied, pluginsList []*framework.PluginInfo + var pluginsInfoForCRsApplied, pluginsList []*f.PluginInfo var contextName string var err error // Test case: a. create KIND cluster It("create KIND cluster", func() { // Create KIND cluster, which is used in test cases to create context's - clusterInfo, err = framework.CreateKindCluster(tf, "sync-e2e-"+framework.RandomNumber(4)) + clusterInfo, err = f.CreateKindCluster(tf, f.ContextPrefixK8s+f.RandomNumber(4)) Expect(err).To(BeNil(), "should not get any error for KIND cluster creation") }) // Test case: b. apply CRD (cluster resource definition) and CR's (cluster resource) for few plugins which are available in centra repo // and CR's for plugins which are not available in central repo It("apply CRD and CRs to KIND cluster", func() { - ApplyConfigOnKindCluster(tf, clusterInfo, append(make([]string, 0), CRDFilePath)) - pluginsToGenerateCRs, ok := pluginGroupToPluginListMap[framework.PluginGroupsForLifeCycleTests[0].Group] + err = f.ApplyConfigOnKindCluster(tf, clusterInfo, append(make([]string, 0), f.K8SCRDFilePath)) + Expect(err).To(BeNil(), "should not get any error for config apply") + pluginsToGenerateCRs, ok := pluginGroupToPluginListMap[usePluginsFromPluginGroup] Expect(ok).To(BeTrue(), "plugin group is not exist in the map") Expect(len(pluginsToGenerateCRs) > numberOfPluginsToInstall).To(BeTrue(), "we don't have enough plugins in local test central repo") - pluginsInfoForCRsApplied, pluginCRFilePaths, err = framework.CreateTemporaryCRsForPluginsInGivenPluginGroup(pluginsToGenerateCRs[:numberOfPluginsToInstall]) + pluginsInfoForCRsApplied, pluginCRFilePaths, err = f.CreateTemporaryCRsForPluginsInGivenPluginGroup(pluginsToGenerateCRs[:numberOfPluginsToInstall]) Expect(err).To(BeNil(), "should not get any error while generating CR files") - ApplyConfigOnKindCluster(tf, clusterInfo, pluginCRFilePaths) + err = f.ApplyConfigOnKindCluster(tf, clusterInfo, pluginCRFilePaths) + Expect(err).To(BeNil(), "should not get any error for config apply") }) // Test case: c. create context and make sure context has created, list plugins, make sure all plugins installed for which CR's are applied in KIND cluster It("create context and validate installed plugins list, should installed all plugins for which CRs has applied in KIND cluster", func() { By("create context with kubeconfig and context") - contextName = "sync-e2e-" + framework.RandomString(4) + contextName = f.ContextPrefixK8s + f.RandomString(4) err = tf.ContextCmd.CreateContextWithKubeconfig(contextName, clusterInfo.KubeConfigPath, clusterInfo.ClusterKubeContext) Expect(err).To(BeNil(), "context should create without any error") pluginsList, err = tf.PluginCmd.ListPluginsForGivenContext(contextName, true) Expect(err).To(BeNil(), "should not get any error for plugin list") Expect(len(pluginsList)).Should(Equal(len(pluginsInfoForCRsApplied)), "number of plugins should be same as number of plugins CRs applied") - Expect(framework.CheckAllPluginsExists(pluginsList, pluginsInfoForCRsApplied)).Should(BeTrue(), " plugins being installed and plugins info for which CRs applied should be same") + Expect(f.CheckAllPluginsExists(pluginsList, pluginsInfoForCRsApplied)).Should(BeTrue(), " plugins being installed and plugins info for which CRs applied should be same") }) // Test case: d. delete the context, make sure all context specific plugins are uninstalled It("delete context, validate installed plugins list, should uninstalled all context plugins", func() { @@ -284,60 +313,63 @@ var _ = framework.CLICoreDescribe("[Tests:E2E][Feature:Plugin-sync-lifecycle]", // e. switch context's, make sure installed plugins also updated // f. delete the KIND clusters Context("Use case: Install KIND Cluster, Apply CRD, Apply specific plugin CRs, create context and validate plugin sync", func() { - var clusterOne, clusterTwo *framework.ClusterInfo + var clusterOne, clusterTwo *f.ClusterInfo var pluginCRFilePathsClusterOne, pluginCRFilePathsClusterTwo []string - var pluginsInfoForCRsAppliedClusterOne, pluginsListClusterOne []*framework.PluginInfo - var pluginsInfoForCRsAppliedClusterTwo, pluginsListClusterTwo []*framework.PluginInfo + var pluginsInfoForCRsAppliedClusterOne, pluginsListClusterOne []*f.PluginInfo + var pluginsInfoForCRsAppliedClusterTwo, pluginsListClusterTwo []*f.PluginInfo var contextNameClusterOne, contextNameClusterTwo string var err error // Test case: a. create KIND clusters It("create KIND cluster", func() { // Create KIND cluster, which is used in test cases to create context's - clusterOne, err = framework.CreateKindCluster(tf, "sync-e2e-"+framework.RandomNumber(4)) + clusterOne, err = f.CreateKindCluster(tf, f.ContextPrefixK8s+f.RandomNumber(4)) Expect(err).To(BeNil(), "should not get any error for KIND cluster creation") - clusterTwo, err = framework.CreateKindCluster(tf, "sync-e2e-"+framework.RandomNumber(4)) + clusterTwo, err = f.CreateKindCluster(tf, f.ContextPrefixK8s+f.RandomNumber(4)) Expect(err).To(BeNil(), "should not get any error for KIND cluster creation") }) // Test case: b. for both clusters, apply CRD (cluster resource definition) and CR's (cluster resource) for few plugins It("apply CRD and CRs to KIND cluster", func() { - ApplyConfigOnKindCluster(tf, clusterOne, append(make([]string, 0), CRDFilePath)) - pluginsToGenerateCRs, ok := pluginGroupToPluginListMap[framework.PluginGroupsForLifeCycleTests[0].Group] + err = f.ApplyConfigOnKindCluster(tf, clusterOne, append(make([]string, 0), f.K8SCRDFilePath)) + pluginsToGenerateCRs, ok := pluginGroupToPluginListMap[usePluginsFromPluginGroup] Expect(ok).To(BeTrue(), "plugin group is not exist in the map") Expect(len(pluginsToGenerateCRs) > numberOfPluginsToInstall*2).To(BeTrue(), "we don't have enough plugins in local test central repo") - pluginsInfoForCRsAppliedClusterOne, pluginCRFilePathsClusterOne, err = framework.CreateTemporaryCRsForPluginsInGivenPluginGroup(pluginsToGenerateCRs[:numberOfPluginsToInstall]) + pluginsInfoForCRsAppliedClusterOne, pluginCRFilePathsClusterOne, err = f.CreateTemporaryCRsForPluginsInGivenPluginGroup(pluginsToGenerateCRs[:numberOfPluginsToInstall]) Expect(err).To(BeNil(), "should not get any error while generating CR files") - ApplyConfigOnKindCluster(tf, clusterOne, pluginCRFilePathsClusterOne) + err = f.ApplyConfigOnKindCluster(tf, clusterOne, pluginCRFilePathsClusterOne) + Expect(err).To(BeNil(), "should not get any error for config apply") - ApplyConfigOnKindCluster(tf, clusterTwo, append(make([]string, 0), CRDFilePath)) - pluginsInfoForCRsAppliedClusterTwo, pluginCRFilePathsClusterTwo, err = framework.CreateTemporaryCRsForPluginsInGivenPluginGroup(pluginsToGenerateCRs[numberOfPluginsToInstall : numberOfPluginsToInstall*2]) + err = f.ApplyConfigOnKindCluster(tf, clusterTwo, append(make([]string, 0), f.K8SCRDFilePath)) + Expect(err).To(BeNil(), "should not get any error for config apply") + pluginsInfoForCRsAppliedClusterTwo, pluginCRFilePathsClusterTwo, err = f.CreateTemporaryCRsForPluginsInGivenPluginGroup(pluginsToGenerateCRs[numberOfPluginsToInstall : numberOfPluginsToInstall*2]) Expect(err).To(BeNil(), "should not get any error while generating CR files") - ApplyConfigOnKindCluster(tf, clusterTwo, pluginCRFilePathsClusterTwo) + err = f.ApplyConfigOnKindCluster(tf, clusterTwo, pluginCRFilePathsClusterTwo) + Expect(err).To(BeNil(), "should not get any error for config apply") }) // Test case: c. for cluster one, create random context and validate the plugin list should show all plugins for which CRs are applied It("create context and validate installed plugins list, should installed all plugins for which CRs has applied in KIND cluster", func() { By("create context with kubeconfig and context") - contextNameClusterOne = "sync-e2e-" + framework.RandomString(4) + contextNameClusterOne = f.ContextPrefixK8s + f.RandomString(4) err = tf.ContextCmd.CreateContextWithKubeconfig(contextNameClusterOne, clusterOne.KubeConfigPath, clusterOne.ClusterKubeContext) Expect(err).To(BeNil(), "context should create without any error") pluginsListClusterOne, err = tf.PluginCmd.ListPluginsForGivenContext(contextNameClusterOne, true) Expect(err).To(BeNil(), "should not get any error for plugin list") Expect(len(pluginsListClusterOne)).Should(Equal(len(pluginsInfoForCRsAppliedClusterOne)), "number of plugins should be same as number of plugins CRs applied") - Expect(framework.CheckAllPluginsExists(pluginsListClusterOne, pluginsInfoForCRsAppliedClusterOne)).Should(BeTrue(), " plugins being installed and plugins info for which CRs applied should be same") + Expect(f.CheckAllPluginsExists(pluginsListClusterOne, pluginsInfoForCRsAppliedClusterOne)).Should(BeTrue(), " plugins being installed and plugins info for which CRs applied should be same") }) // Test case: d. for cluster two, create random context and validate the plugin list should show all plugins for which CRs are applied It("create context and validate installed plugins list, should installed all plugins for which CRs has applied in KIND cluster", func() { By("create context with kubeconfig and context") - contextNameClusterTwo = "sync-e2e-" + framework.RandomString(4) + contextNameClusterTwo = f.ContextPrefixK8s + f.RandomString(4) err = tf.ContextCmd.CreateContextWithKubeconfig(contextNameClusterTwo, clusterTwo.KubeConfigPath, clusterTwo.ClusterKubeContext) Expect(err).To(BeNil(), "context should create without any error") pluginsListClusterTwo, err = tf.PluginCmd.ListPluginsForGivenContext(contextNameClusterTwo, true) Expect(err).To(BeNil(), "should not get any error for plugin list") Expect(len(pluginsListClusterTwo)).Should(Equal(len(pluginsInfoForCRsAppliedClusterTwo)), "number of plugins should be same as number of plugins CRs applied") - Expect(framework.CheckAllPluginsExists(pluginsListClusterTwo, pluginsInfoForCRsAppliedClusterTwo)).Should(BeTrue(), " plugins being installed and plugins info for which CRs applied should be same") + Expect(f.CheckAllPluginsExists(pluginsListClusterTwo, pluginsInfoForCRsAppliedClusterTwo)).Should(BeTrue(), " plugins being installed and plugins info for which CRs applied should be same") }) // Test case: e. switch context's, make sure installed plugins also updated @@ -347,14 +379,14 @@ var _ = framework.CLICoreDescribe("[Tests:E2E][Feature:Plugin-sync-lifecycle]", pluginsListClusterTwo, err = tf.PluginCmd.ListPluginsForGivenContext(contextNameClusterTwo, true) Expect(err).To(BeNil(), "should not get any error for plugin list") Expect(len(pluginsListClusterTwo)).Should(Equal(len(pluginsInfoForCRsAppliedClusterTwo)), "number of plugins should be same as number of plugins CRs applied") - Expect(framework.CheckAllPluginsExists(pluginsListClusterTwo, pluginsInfoForCRsAppliedClusterTwo)).Should(BeTrue(), " plugins being installed and plugins info for which CRs applied should be same") + Expect(f.CheckAllPluginsExists(pluginsListClusterTwo, pluginsInfoForCRsAppliedClusterTwo)).Should(BeTrue(), " plugins being installed and plugins info for which CRs applied should be same") err = tf.ContextCmd.UseContext(contextNameClusterOne) Expect(err).To(BeNil(), "there should not be any error for use context") pluginsListClusterOne, err = tf.PluginCmd.ListPluginsForGivenContext(contextNameClusterOne, true) Expect(err).To(BeNil(), "should not get any error for plugin list") Expect(len(pluginsListClusterOne)).Should(Equal(len(pluginsInfoForCRsAppliedClusterOne)), "number of plugins should be same as number of plugins CRs applied") - Expect(framework.CheckAllPluginsExists(pluginsListClusterOne, pluginsInfoForCRsAppliedClusterOne)).Should(BeTrue(), " plugins being installed and plugins info for which CRs applied should be same") + Expect(f.CheckAllPluginsExists(pluginsListClusterOne, pluginsInfoForCRsAppliedClusterOne)).Should(BeTrue(), " plugins being installed and plugins info for which CRs applied should be same") }) // Test case: f. delete the KIND clusters diff --git a/test/e2e/plugin_sync/tmc/plugin_sync_tmc_lifecycle_suite_test.go b/test/e2e/plugin_sync/tmc/plugin_sync_tmc_lifecycle_suite_test.go new file mode 100644 index 000000000..defc099ef --- /dev/null +++ b/test/e2e/plugin_sync/tmc/plugin_sync_tmc_lifecycle_suite_test.go @@ -0,0 +1,118 @@ +// Copyright 2023 VMware, Inc. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// pluginsynce2etmc provides plugin sync command specific E2E test cases +package pluginsynce2etmc + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/vmware-tanzu/tanzu-cli/test/e2e/framework" + helper "github.com/vmware-tanzu/tanzu-cli/test/e2e/plugin_lifecycle" +) + +func TestPluginSyncLifecycle(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Plugin-Sync-TMC-Lifecycle E2E Test Suite") +} + +var ( + tf *framework.Framework + e2eTestLocalCentralRepoURL string + pluginsSearchList []*framework.PluginInfo + pluginGroups []*framework.PluginGroup + pluginGroupToPluginListMap map[string][]*framework.PluginInfo + usePluginsFromTmcPluginGroup string + usePluginsFromK8sPluginGroup string + K8SCRDFilePath string + tmcMappingsDir string + tmcPluginsMockFilePath string + tmcConfigFolderPath string +) + +const mockAPIEndpointForPlugins = "/pluginsInfo" +const routesFileName = "routes.json" +const tmcPluginsMockFile = "tmcPluginsMock.json" +const numberOfPluginsToInstall = 3 + +const forceCSPFlag = " --force-csp true" +const tmcConfigFolderName = "tmc" +const numberOfPluginsSameAsNoOfPluginsInfoMocked = "number of plugins should be same as number of plugins mocked in tmc endpoint response" +const pluginsInstalledAndMockedShouldBeSame = "plugins being installed and plugins being mocked in tmc endpoint response should be same" +const noErrorForMockResponseFileUpdate = "there should not be any error while updating the tmc endpoint mock response" +const noErrorForMockResponsePreparation = "there should not be any error while preparing the tmc endpoint mock response" +const deleteContextWithoutError = "context should be deleted without error" +const mockServerShouldStartWithoutError = "mock server should start without error" +const mockServerShouldStopWithoutError = "mock server should stop without error" +const noErrorWhileCreatingContext = "context should create without any error" +const activeContextShouldExists = "there should be a active context" +const pluginGroupShouldExists = "plugin group should exist in the map" +const noErrorForPluginList = "should not get any error for plugin list" +const activeContextShouldBeRecentlyAddedOne = "the active context should be recently added context" +const testRepoDoesNotHaveEnoughPlugins = "test central repo does not have enough plugins to continue e2e tests" + +// BeforeSuite initializes and set up the environment to execute the plugin sync test cases for tmc target +var _ = BeforeSuite(func() { + tf = framework.NewFramework() + + // check E2E test central repo URL (TANZU_CLI_E2E_TEST_LOCAL_CENTRAL_REPO_URL) and update the same for plugin discovery + e2eTestLocalCentralRepoURL = os.Getenv(framework.TanzuCliE2ETestLocalCentralRepositoryURL) + Expect(e2eTestLocalCentralRepoURL).NotTo(BeEmpty(), fmt.Sprintf("environment variable %s should set with local central repository URL", framework.TanzuCliE2ETestLocalCentralRepositoryURL)) + err := framework.UpdatePluginDiscoverySource(tf, e2eTestLocalCentralRepoURL) + Expect(err).To(BeNil(), "should not be any error while updating plugin discovery source") + + // Check whether the TMC token is set and whether TANZU_CLI_E2E_TEST_ENVIRONMENT is set to skip HTTPS hardcoding when mocking TMC response. + Expect(os.Getenv(framework.TanzuAPIToken)).NotTo(BeEmpty(), fmt.Sprintf("environment variable %s should set with TMC API Token", framework.TanzuAPIToken)) + Expect(os.Getenv(framework.CLIE2ETestEnvironment)).NotTo(BeEmpty(), fmt.Sprintf("environment variable %s should set as true otherwise e2e tests will fails", framework.CLIE2ETestEnvironment)) + + // create tmc/mappings config folder, in $HOME/.tanzu-cli-e2e/temp directory + tmcConfigFolderPath = filepath.Join(framework.FullPathForTempDir, tmcConfigFolderName) + tmcMappingsDir = filepath.Join(tmcConfigFolderPath, "mappings") + _ = framework.CreateDir(tmcMappingsDir) + + // create a file to update http request/response mocking for every test case + tmcPluginsMockFilePath = filepath.Join(tmcMappingsDir, tmcPluginsMockFile) + + // Search for plugin groups and ensure that there are available plugin groups. + pluginGroups, err = helper.SearchAllPluginGroups(tf) + Expect(err).To(BeNil(), framework.NoErrorForPluginGroupSearch) + + // Check that all required plugin groups for life cycle tests (listed in framework.PluginGroupsForLifeCycleTests) are available in the plugin group search output. + Expect(framework.IsAllPluginGroupsExists(pluginGroups, framework.PluginGroupsForLifeCycleTests)).Should(BeTrue(), "all required plugin groups for life cycle tests should exists in plugin group search output") + + // Retrieve the TMC specific plugin group from which plugins will be used to perform E2E tests. + usePluginsFromTmcPluginGroup = framework.GetPluginGroupWhichStartsWithGivenPrefix(framework.PluginGroupsForLifeCycleTests, framework.TMCPluginGroupPrefix) + Expect(usePluginsFromTmcPluginGroup).NotTo(BeEmpty(), "there should be a tmc specific plugin group") + // Retrieve the k8s specific plugin group from which plugins will be used to perform E2E tests. + usePluginsFromK8sPluginGroup = framework.GetPluginGroupWhichStartsWithGivenPrefix(framework.PluginGroupsForLifeCycleTests, framework.K8SPluginGroupPrefix) + Expect(usePluginsFromTmcPluginGroup).NotTo(BeEmpty(), "there should be a k8s specific plugin group") + + // search plugins and make sure there are plugins available + pluginsSearchList, err = helper.SearchAllPlugins(tf) + Expect(err).To(BeNil(), framework.NoErrorForPluginSearch) + Expect(len(pluginsSearchList)).Should(BeNumerically(">", 0)) + + // Map plugins to their respective plugin groups. + pluginGroupToPluginListMap = framework.MapPluginsToPluginGroups(pluginsSearchList, framework.PluginGroupsForLifeCycleTests) + for pluginGroupLatest := range framework.PluginGroupsLatestToOldVersions { + framework.CopyPluginsBetweenPluginGroupsAndUpdatePluginsVersion(pluginGroupToPluginListMap, pluginGroupLatest, framework.PluginGroupsLatestToOldVersions[pluginGroupLatest], strings.Split(framework.PluginGroupsLatestToOldVersions[pluginGroupLatest], "/")[1]) + } + + // Check that for every plugin group listed in framework.PluginGroupsForLifeCycleTests, there are available plugins. + for pg := range pluginGroupToPluginListMap { + Expect(len(pluginGroupToPluginListMap[pg])).Should(BeNumerically(">", 0), "there should be at least one plugin available for each plugin group in plugin group life cycle list") + } +}) + +// After the Suite, delete the temporary directory (including the TMC config directory within the temporary directory) that was created during test case execution +var _ = AfterSuite(func() { + err := os.RemoveAll(framework.FullPathForTempDir) // delete an entire directory + Expect(err).To(BeNil(), "should not get any error while deleting temp directory") +}) diff --git a/test/e2e/plugin_sync/tmc/plugin_sync_tmc_lifecycle_test.go b/test/e2e/plugin_sync/tmc/plugin_sync_tmc_lifecycle_test.go new file mode 100644 index 000000000..f3fba7c23 --- /dev/null +++ b/test/e2e/plugin_sync/tmc/plugin_sync_tmc_lifecycle_test.go @@ -0,0 +1,619 @@ +// Copyright 2023 VMware, Inc. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// pluginsynce2etmc provides plugin sync command specific E2E test cases for tmc target +package pluginsynce2etmc + +import ( + "fmt" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + f "github.com/vmware-tanzu/tanzu-cli/test/e2e/framework" + "github.com/vmware-tanzu/tanzu-plugin-runtime/config/types" +) + +// Below are the use cases executed in this suite +// Use case 1: create context when there are no plugins to sync +// Use case 2: created a context (make sure sync installs plugins), uninstall plugin, run plugins sync (should install uninstall plugins) +// Use case 3: create a context (plugin sync) when test central repo does not have all plugins mocked in tmc endpoint response +// Use case 4: create two contexts, perform switch context use case, make installed plugins should be updated as per the context +// Use Case 5: Update the TMC endpoint with additional plugins, and ensure that the plugin sync updates the latest additional plugins. +// Use case 6: create k8s context and tmc context, validate plugins list and plugin sync +var _ = f.CLICoreDescribe("[Tests:E2E][Feature:Plugin-Sync-TMC-lifecycle]", func() { + // Delete the configuration files and initialize + Context("Delete the configuration files and initialize", func() { + It("Delete the configuration files and initialize", func() { + err := tf.Config.DeleteCLIConfigurationFiles() + Expect(err).To(BeNil()) + // call init + err = tf.Config.ConfigInit() + Expect(err).To(BeNil()) + + // update plugin discovery source + err = f.UpdatePluginDiscoverySource(tf, e2eTestLocalCentralRepoURL) + Expect(err).To(BeNil(), "should not get any error for plugin source update") + }) + }) + + // Use case 1: create context when there are no plugins to sync + // a. create empty mock response, and start mock http server + // b. create context and validate current active context + // c. list plugins and make sure no plugins installed + // d. delete current context + Context("Use case: create context when there are no plugins to sync", func() { + var contextName string + var err error + // Test case: a. create empty mock response, and start mock http server + It("mock tmc endpoint with expected plugins response and start REST API mock server", func() { + pluginsToGenerateMockResponse := make([]*f.PluginInfo, 0) + mockReqResMapping, err := f.ConvertPluginsInfoToTMCEndpointMockResponse(pluginsToGenerateMockResponse) + Expect(err).To(BeNil(), noErrorForMockResponsePreparation) + err = f.WriteToFileInJSONFormat(mockReqResMapping, tmcPluginsMockFilePath) + Expect(err).To(BeNil(), noErrorForMockResponseFileUpdate) + + // start http mock server + err = f.StartMockServer(tf, tmcConfigFolderPath, f.HttpMockServerName) + Expect(err).To(BeNil(), mockServerShouldStartWithoutError) + var mockResPluginsInfo f.TMCPluginsInfo + // check the tmc mocked endpoint is working as expected + err = f.GetHTTPCall(f.TMCPluginsMockServerEndpoint, &mockResPluginsInfo) + Expect(err).To(BeNil(), "there should not be any error for GET http call on mockapi endpoint:"+f.TMCPluginsMockServerEndpoint) + Expect(len(mockResPluginsInfo.Plugins)).Should(Equal(len(pluginsToGenerateMockResponse)), "the number of plugins in endpoint response and initially mocked should be same") + }) + // Test case: b. create context and validate current active context + It("create context for TMC target with http mock server URL as endpoint", func() { + contextName = f.ContextPrefixTMC + f.RandomString(4) + _, err = tf.ContextCmd.CreateContextWithEndPointStaging(contextName, f.TMCMockServerEndpoint, f.AddAdditionalFlagAndValue(forceCSPFlag)) + Expect(err).To(BeNil(), noErrorWhileCreatingContext) + active, err := tf.ContextCmd.GetActiveContext(string(types.TargetTMC)) + Expect(err).To(BeNil(), activeContextShouldExists) + Expect(active).To(Equal(contextName), activeContextShouldBeRecentlyAddedOne) + }) + // Test case: c. list plugins and make sure no plugins installed + It("list plugins and check number plugins should be same as installed in previous test", func() { + pluginsList, err := tf.PluginCmd.ListPluginsForGivenContext(contextName, true) + Expect(err).To(BeNil(), noErrorForPluginList) + Expect(len(pluginsList)).Should(Equal(0), "there should not be any context specific plugins") + }) + // Test case: d. delete current context + It("delete current context and stop mock server", func() { + err = tf.ContextCmd.DeleteContext(contextName) + Expect(err).To(BeNil(), deleteContextWithoutError) + err = f.StopContainer(tf, f.HttpMockServerName) + Expect(err).To(BeNil(), mockServerShouldStopWithoutError) + }) + }) + + // Use case 2: Created a context (make sure sync installs plugins), uninstall plugin, run plugins sync (should install uninstall plugins) + // Steps: + // a. mock tmc endpoint with plugins info, start the mock server + // b. create context and make sure context has created + // c. list plugins and validate plugins info, make sure all plugins are installed as per mock response + // d. uninstall one of the installed plugin, make sure plugin is uninstalled, run plugin sync, make sure the uninstalled plugin has installed again. + // e. delete current context, make sure all context specific plugins are uninstalled + Context("Use case: create context, uninstall plugin, sync plugins", func() { + //var pluginCRFilePaths []string + var pluginsToGenerateMockResponse, installedPluginsList, pluginsList []*f.PluginInfo + var contextName string + var err error + var ok bool + + // Test case: a. mock tmc endpoint with plugins info, start the mock server + It("mock tmc endpoint with expected plugins response and restart REST API mock server", func() { + // get plugins from a group + pluginsToGenerateMockResponse, ok = pluginGroupToPluginListMap[usePluginsFromTmcPluginGroup] + Expect(ok).To(BeTrue(), pluginGroupShouldExists) + Expect(len(pluginsToGenerateMockResponse) > numberOfPluginsToInstall).To(BeTrue(), testRepoDoesNotHaveEnoughPlugins) + // mock tmc endpoint with only specific number of plugins info + pluginsToGenerateMockResponse = pluginsToGenerateMockResponse[:numberOfPluginsToInstall] + mockReqResMapping, err := f.ConvertPluginsInfoToTMCEndpointMockResponse(pluginsToGenerateMockResponse[:numberOfPluginsToInstall]) + Expect(err).To(BeNil(), noErrorForMockResponsePreparation) + err = f.WriteToFileInJSONFormat(mockReqResMapping, tmcPluginsMockFilePath) + Expect(err).To(BeNil(), noErrorForMockResponseFileUpdate) + + // start http mock server + err = f.StartMockServer(tf, tmcConfigFolderPath, f.HttpMockServerName) + Expect(err).To(BeNil(), mockServerShouldStartWithoutError) + var mockResPluginsInfo f.TMCPluginsInfo + // check the tmc mocked endpoint is working as expected + err = f.GetHTTPCall(f.TMCPluginsMockServerEndpoint, &mockResPluginsInfo) + Expect(err).To(BeNil(), "there should not be any error for GET http call on mockapi endpoint:"+f.TMCPluginsMockServerEndpoint) + Expect(len(mockResPluginsInfo.Plugins)).Should(Equal(len(pluginsToGenerateMockResponse)), "the number of plugins in endpoint response and initially mocked should be same") + }) + // Test case: b. create context and make sure context has created + It("create context for TMC target with http mock server URL as endpoint", func() { + contextName = f.ContextPrefixTMC + f.RandomString(4) + _, err = tf.ContextCmd.CreateContextWithEndPointStaging(contextName, f.TMCMockServerEndpoint, f.AddAdditionalFlagAndValue(forceCSPFlag)) + Expect(err).To(BeNil(), noErrorWhileCreatingContext) + active, err := tf.ContextCmd.GetActiveContext(string(types.TargetTMC)) + Expect(err).To(BeNil(), activeContextShouldExists) + Expect(active).To(Equal(contextName), activeContextShouldBeRecentlyAddedOne) + }) + + // Test case: c. list plugins and validate plugins info, make sure all plugins are installed as per mock response + It("list plugins and validate plugins being installed after context being created", func() { + installedPluginsList, err = tf.PluginCmd.ListPluginsForGivenContext(contextName, true) + Expect(err).To(BeNil(), noErrorForPluginList) + Expect(len(installedPluginsList)).Should(Equal(len(pluginsToGenerateMockResponse)), numberOfPluginsSameAsNoOfPluginsInfoMocked) + Expect(f.CheckAllPluginsExists(installedPluginsList, pluginsToGenerateMockResponse)).Should(BeTrue(), pluginsInstalledAndMockedShouldBeSame) + }) + + // Test case: d. uninstall one of the installed plugin, make sure plugin is uninstalled, + // run plugin sync, make sure the uninstalled plugin has installed again. + It("uninstall one of the installed plugin", func() { + pluginToUninstall := pluginsToGenerateMockResponse[0] + err := tf.PluginCmd.UninstallPlugin(pluginToUninstall.Name, pluginToUninstall.Target) + Expect(err).To(BeNil(), "should not get any error for plugin uninstall") + + latestPluginsInstalledList := pluginsToGenerateMockResponse[1:] + allPluginsList, err := tf.PluginCmd.ListPluginsForGivenContext(contextName, false) + Expect(err).To(BeNil(), noErrorForPluginList) + installedPluginsList = f.GetInstalledPlugins(allPluginsList) + Expect(f.IsPluginExists(allPluginsList, f.GetGivenPluginFromTheGivenPluginList(allPluginsList, pluginToUninstall), f.NotInstalled)).To(BeTrue(), "uninstalled plugin should be listed as not installed") + Expect(len(installedPluginsList)).Should(Equal(len(latestPluginsInstalledList)), numberOfPluginsSameAsNoOfPluginsInfoMocked) + Expect(f.CheckAllPluginsExists(installedPluginsList, latestPluginsInstalledList)).Should(BeTrue(), pluginsInstalledAndMockedShouldBeSame) + + _, err = tf.PluginCmd.Sync() + Expect(err).To(BeNil(), "should not get any error for plugin sync") + + installedPluginsList, err = tf.PluginCmd.ListPluginsForGivenContext(contextName, true) + Expect(err).To(BeNil(), noErrorForPluginList) + Expect(len(installedPluginsList)).Should(Equal(len(pluginsToGenerateMockResponse)), numberOfPluginsSameAsNoOfPluginsInfoMocked) + Expect(f.CheckAllPluginsExists(installedPluginsList, pluginsToGenerateMockResponse)).Should(BeTrue(), pluginsInstalledAndMockedShouldBeSame) + }) + // e. delete current context, make sure all context specific plugins are uninstalled + It("delete current context", func() { + err = tf.ContextCmd.DeleteContext(contextName) + Expect(err).To(BeNil(), deleteContextWithoutError) + + pluginsList, err = tf.PluginCmd.ListPluginsForGivenContext(contextName, true) + Expect(err).To(BeNil(), noErrorForPluginList) + Expect(len(pluginsList)).Should(Equal(0), "all context plugins should be uninstalled as context delete") + err = f.StopContainer(tf, f.HttpMockServerName) + Expect(err).To(BeNil(), mockServerShouldStopWithoutError) + }) + }) + + // Use case 3: create a context (plugin sync) when test central repo does not have all plugins mocked in tmc endpoint response + // Steps: + // a. update tmc endpoint mock response (make sure at least a plugin does not exists in test central repo) and restart REST API mock server + // b. create context and make sure context has created + // c. list plugins and validate plugins info, make sure all plugins installed for which response mocked and available in central repo, the unavailable plugin (with incorrect version) should not be installed + // d. run plugin sync and validate the plugin list + // e. delete current context + + Context("Use case: Create context should not install unavailable plugins, plugin sync also should not install unavailable plugins", func() { + var pluginsToGenerateMockResponse, pluginsGeneratedMockResponseWithCorrectInfo, pluginsList []*f.PluginInfo + var pluginWithIncorrectVersion *f.PluginInfo + var contextName string + var err error + var ok bool + // Test case: a. update tmc endpoint mock response (make sure at least a plugin does not exists in test central repo) and restart REST API mock server + It("mock tmc endpoint with expected plugins response and restart REST API mock server", func() { + // get plugins from a group + pluginsToGenerateMockResponse, ok = pluginGroupToPluginListMap[usePluginsFromTmcPluginGroup] + Expect(ok).To(BeTrue(), pluginGroupShouldExists) + Expect(len(pluginsToGenerateMockResponse) > numberOfPluginsToInstall).To(BeTrue(), testRepoDoesNotHaveEnoughPlugins) + // mock tmc endpoint with only specific number of plugins info + pluginsToGenerateMockResponse = pluginsToGenerateMockResponse[:numberOfPluginsToInstall] + // assign incorrect version to last plugin in the slice, to make sure at least one plugin is not available + // in the mock response + actualVersion := pluginsToGenerateMockResponse[numberOfPluginsToInstall-1].Version + pluginsToGenerateMockResponse[numberOfPluginsToInstall-1].Version = pluginsToGenerateMockResponse[numberOfPluginsToInstall-1].Version + f.RandomNumber(2) + pluginWithIncorrectVersion = pluginsToGenerateMockResponse[numberOfPluginsToInstall-1] + // generate mock response for all plugins (including the incorrect version plugin) + mockReqResMapping, err := f.ConvertPluginsInfoToTMCEndpointMockResponse(pluginsToGenerateMockResponse) + err = f.WriteToFileInJSONFormat(mockReqResMapping, tmcPluginsMockFilePath) + Expect(err).To(BeNil(), noErrorForMockResponseFileUpdate) + + // assign the original version back + pluginsToGenerateMockResponse[numberOfPluginsToInstall-1].Version = actualVersion + + // skip last plugin in the slice as it has incorrect version info, which is not available in the mock response + pluginsGeneratedMockResponseWithCorrectInfo = pluginsToGenerateMockResponse[:numberOfPluginsToInstall-1] + // start http mock server + err = f.StartMockServer(tf, tmcConfigFolderPath, f.HttpMockServerName) + Expect(err).To(BeNil(), mockServerShouldStartWithoutError) + var mockResPluginsInfo f.TMCPluginsInfo + // check the tmc mocked endpoint is working as expected + err = f.GetHTTPCall(f.TMCPluginsMockServerEndpoint, &mockResPluginsInfo) + Expect(err).To(BeNil(), "there should not be any error for GET http call on mockapi endpoint:"+f.TMCPluginsMockServerEndpoint) + Expect(len(mockResPluginsInfo.Plugins)).Should(Equal(len(pluginsToGenerateMockResponse)), "the number of plugins in endpoint response and initially mocked should be same") + }) + + // Test case: b. create context and make sure context has created + It("create context for TMC target with http mock server URL as endpoint", func() { + contextName = f.ContextPrefixTMC + f.RandomString(4) + _, err = tf.ContextCmd.CreateContextWithEndPointStaging(contextName, f.TMCMockServerEndpoint, f.AddAdditionalFlagAndValue(forceCSPFlag)) + Expect(err).To(BeNil(), noErrorWhileCreatingContext) + active, err := tf.ContextCmd.GetActiveContext(string(types.TargetTMC)) + Expect(err).To(BeNil(), activeContextShouldExists) + Expect(active).To(Equal(contextName), activeContextShouldBeRecentlyAddedOne) + }) + + // Test case: c. list plugins and validate plugins info, make sure all plugins installed for which response mocked and available in central repo, the unavailable plugin (with incorrect version) should not be installed + It("list plugins and validate plugins being installed after context being created", func() { + pluginsList, err = tf.PluginCmd.ListPluginsForGivenContext(contextName, true) + Expect(err).To(BeNil(), noErrorForPluginList) + Expect(len(pluginsList)).Should(Equal(len(pluginsGeneratedMockResponseWithCorrectInfo)), numberOfPluginsSameAsNoOfPluginsInfoMocked) + Expect(f.CheckAllPluginsExists(pluginsList, pluginsGeneratedMockResponseWithCorrectInfo)).Should(BeTrue(), pluginsInstalledAndMockedShouldBeSame) + }) + + // Test case: d. run plugin sync and validate the plugin list + It("run plugin sync and validate err response in plugin sync, validate plugin list output", func() { + _, err = tf.PluginCmd.Sync() + Expect(err.Error()).To(ContainSubstring(fmt.Sprintf(f.UnableToFindPluginForTarget, pluginWithIncorrectVersion.Name, pluginWithIncorrectVersion.Target))) + + pluginsList, err = tf.PluginCmd.ListPluginsForGivenContext(contextName, true) + Expect(err).To(BeNil(), noErrorForPluginList) + Expect(len(pluginsList)).Should(Equal(len(pluginsGeneratedMockResponseWithCorrectInfo)), numberOfPluginsSameAsNoOfPluginsInfoMocked) + Expect(f.CheckAllPluginsExists(pluginsList, pluginsGeneratedMockResponseWithCorrectInfo)).Should(BeTrue(), pluginsInstalledAndMockedShouldBeSame) + }) + // e. delete current context and stop mock server + It("delete current context", func() { + err = tf.ContextCmd.DeleteContext(contextName) + Expect(err).To(BeNil(), deleteContextWithoutError) + err = f.StopContainer(tf, f.HttpMockServerName) + Expect(err).To(BeNil(), mockServerShouldStopWithoutError) + }) + }) + + // Use case 4: Create two contexts, perform switch context use case, make installed plugins should be updated as per the context + // Steps: + // a. check the plugins availability + // b. update mock response for first context, restart mock server, create context, and validate plugin list + // c. update mock response for second context, restart mock server, create context, and validate plugin list + // e. delete both contexts + Context("Use case: create two contexts, and validate plugin list ", func() { + var pluginsFromPluginGroup, pluginsToGenerateMockResponseOne, pluginsToGenerateMockResponseTwo, pluginsListOne, pluginsListTwo []*f.PluginInfo + var contextNameOne, contextNameTwo string + var TMCEndpointResponseOne, TMCEndpointResponseTwo *f.TMCPluginsMockRequestResponseMapping + var err error + var ok bool + + // Test case: a. check the plugins availability + It("check the plugins availability", func() { + // get plugins from a group + pluginsFromPluginGroup, ok = pluginGroupToPluginListMap[usePluginsFromTmcPluginGroup] + Expect(ok).To(BeTrue(), pluginGroupShouldExists) + Expect(len(pluginsFromPluginGroup) > numberOfPluginsToInstall*2).To(BeTrue(), testRepoDoesNotHaveEnoughPlugins) + // mock tmc endpoint with only specific number of plugins info + pluginsFromPluginGroup = pluginsFromPluginGroup[:numberOfPluginsToInstall*2] + }) + + // Test case: b. update mock response for first context, restart mock server, create context, and validate plugin list + It("update mock response for first context, restart mock server, create context, and validate plugin list", func() { + + pluginsToGenerateMockResponseOne = pluginsFromPluginGroup[:numberOfPluginsToInstall] + // generate mock response for all plugins (including the incorrect version plugin) + TMCEndpointResponseOne, err = f.ConvertPluginsInfoToTMCEndpointMockResponse(pluginsToGenerateMockResponseOne) + err = f.WriteToFileInJSONFormat(TMCEndpointResponseOne, tmcPluginsMockFilePath) + Expect(err).To(BeNil(), noErrorForMockResponseFileUpdate) + + // start http mock server + err = f.StartMockServer(tf, tmcConfigFolderPath, f.HttpMockServerName) + Expect(err).To(BeNil(), mockServerShouldStartWithoutError) + + var mockResPluginsInfo f.TMCPluginsInfo + // check the tmc mocked endpoint is working as expected + err = f.GetHTTPCall(f.TMCPluginsMockServerEndpoint, &mockResPluginsInfo) + Expect(err).To(BeNil(), "there should not be any error for GET http call on mockapi endpoint:"+f.TMCPluginsMockServerEndpoint) + Expect(len(mockResPluginsInfo.Plugins)).Should(Equal(len(pluginsToGenerateMockResponseOne)), "the number of plugins in endpoint response and initially mocked should be same") + + contextNameOne = f.ContextPrefixTMC + f.RandomString(4) + _, err = tf.ContextCmd.CreateContextWithEndPointStaging(contextNameOne, f.TMCMockServerEndpoint, f.AddAdditionalFlagAndValue(forceCSPFlag)) + Expect(err).To(BeNil(), noErrorWhileCreatingContext) + active, err := tf.ContextCmd.GetActiveContext(string(types.TargetTMC)) + Expect(err).To(BeNil(), activeContextShouldExists) + Expect(active).To(Equal(contextNameOne), activeContextShouldBeRecentlyAddedOne) + + pluginsListOne, err = tf.PluginCmd.ListPluginsForGivenContext(contextNameOne, true) + Expect(err).To(BeNil(), noErrorForPluginList) + Expect(len(pluginsListOne)).Should(Equal(len(pluginsToGenerateMockResponseOne)), numberOfPluginsSameAsNoOfPluginsInfoMocked) + Expect(f.CheckAllPluginsExists(pluginsListOne, pluginsToGenerateMockResponseOne)).Should(BeTrue(), pluginsInstalledAndMockedShouldBeSame) + }) + // Test case: c. update mock response for second context, restart mock server, create context, and validate plugin list + It("update mock response for second context, restart mock server, create second context, and validate plugin list", func() { + + pluginsToGenerateMockResponseTwo = pluginsFromPluginGroup[numberOfPluginsToInstall:] + TMCEndpointResponseTwo, err = f.ConvertPluginsInfoToTMCEndpointMockResponse(pluginsToGenerateMockResponseTwo) + + err = f.WriteToFileInJSONFormat(TMCEndpointResponseTwo, tmcPluginsMockFilePath) + Expect(err).To(BeNil(), noErrorForMockResponseFileUpdate) + + // start http mock server + err = f.StartMockServer(tf, tmcConfigFolderPath, f.HttpMockServerName) + Expect(err).To(BeNil(), mockServerShouldStartWithoutError) + + // check the tmc mocked endpoint is working as expected + var mockResPluginsInfo f.TMCPluginsInfo + err = f.GetHTTPCall(f.TMCPluginsMockServerEndpoint, &mockResPluginsInfo) + Expect(err).To(BeNil(), "there should not be any error for GET http call on mockapi endpoint:"+f.TMCPluginsMockServerEndpoint) + Expect(len(mockResPluginsInfo.Plugins)).Should(Equal(len(pluginsToGenerateMockResponseTwo)), "the number of plugins in endpoint response and initially mocked should be same") + + contextNameTwo = f.ContextPrefixTMC + f.RandomString(4) + _, err = tf.ContextCmd.CreateContextWithEndPointStaging(contextNameTwo, f.TMCMockServerEndpoint, f.AddAdditionalFlagAndValue(forceCSPFlag)) + Expect(err).To(BeNil(), noErrorWhileCreatingContext) + active, err := tf.ContextCmd.GetActiveContext(string(types.TargetTMC)) + Expect(err).To(BeNil(), activeContextShouldExists) + Expect(active).To(Equal(contextNameTwo), activeContextShouldBeRecentlyAddedOne) + + pluginsListTwo, err = tf.PluginCmd.ListPluginsForGivenContext(contextNameTwo, true) + Expect(err).To(BeNil(), noErrorForPluginList) + Expect(len(pluginsListTwo)).Should(Equal(len(pluginsToGenerateMockResponseTwo)), numberOfPluginsSameAsNoOfPluginsInfoMocked) + Expect(f.CheckAllPluginsExists(pluginsListTwo, pluginsToGenerateMockResponseTwo)).Should(BeTrue(), pluginsInstalledAndMockedShouldBeSame) + }) + + // d. delete both contexts + It("delete current context", func() { + err = tf.ContextCmd.DeleteContext(contextNameOne) + Expect(err).To(BeNil(), deleteContextWithoutError) + err = tf.ContextCmd.DeleteContext(contextNameTwo) + Expect(err).To(BeNil(), deleteContextWithoutError) + err = f.StopContainer(tf, f.HttpMockServerName) + Expect(err).To(BeNil(), mockServerShouldStopWithoutError) + }) + }) + + // Use Case 5: Update the TMC endpoint with additional plugins, and ensure that the plugin sync updates the latest additional plugins. + // Steps: + // a. check the plugins availability + // b. update mock response, restart mock server, create context, and validate plugin list + // c. update mock response with additional plugins, restart mock server, run plugin sync, and validate plugin list + // d. delete context + Context("Use case: create two contexts, and validate plugin list ", func() { + var pluginsFromPluginGroup, pluginsToGenerateMockResponseOne, pluginsToGenerateMockResponseTwo, pluginsListOne []*f.PluginInfo + var contextNameOne string + var TMCEndpointResponseOne, TMCEndpointResponseTwo *f.TMCPluginsMockRequestResponseMapping + var err error + var ok bool + + // Test case: a. check the plugins availability + It("check the plugins availability", func() { + // get plugins from a group + pluginsFromPluginGroup, ok = pluginGroupToPluginListMap[usePluginsFromTmcPluginGroup] + Expect(ok).To(BeTrue(), pluginGroupShouldExists) + Expect(len(pluginsFromPluginGroup) > numberOfPluginsToInstall*2).To(BeTrue(), testRepoDoesNotHaveEnoughPlugins) + // mock tmc endpoint with only specific number of plugins info + pluginsFromPluginGroup = pluginsFromPluginGroup[:numberOfPluginsToInstall*2] + }) + + // Test case: b. update mock response for first context, restart mock server, create context, and validate plugin list + It("update mock response for first context, restart mock server, create context, and validate plugin list", func() { + + pluginsToGenerateMockResponseOne = pluginsFromPluginGroup[:numberOfPluginsToInstall] + // generate mock response for all plugins (including the incorrect version plugin) + TMCEndpointResponseOne, err = f.ConvertPluginsInfoToTMCEndpointMockResponse(pluginsToGenerateMockResponseOne) + err = f.WriteToFileInJSONFormat(TMCEndpointResponseOne, tmcPluginsMockFilePath) + Expect(err).To(BeNil(), noErrorForMockResponseFileUpdate) + + // start http mock server + err = f.StartMockServer(tf, tmcConfigFolderPath, f.HttpMockServerName) + Expect(err).To(BeNil(), mockServerShouldStartWithoutError) + + var mockResPluginsInfo f.TMCPluginsInfo + // check the tmc mocked endpoint is working as expected + err = f.GetHTTPCall(f.TMCPluginsMockServerEndpoint, &mockResPluginsInfo) + Expect(err).To(BeNil(), "there should not be any error for GET http call on mockapi endpoint:"+f.TMCPluginsMockServerEndpoint) + Expect(len(mockResPluginsInfo.Plugins)).Should(Equal(len(pluginsToGenerateMockResponseOne)), "the number of plugins in endpoint response and initially mocked should be same") + + contextNameOne = f.ContextPrefixTMC + f.RandomString(4) + _, err = tf.ContextCmd.CreateContextWithEndPointStaging(contextNameOne, f.TMCMockServerEndpoint, f.AddAdditionalFlagAndValue(forceCSPFlag)) + Expect(err).To(BeNil(), noErrorWhileCreatingContext) + active, err := tf.ContextCmd.GetActiveContext(string(types.TargetTMC)) + Expect(err).To(BeNil(), activeContextShouldExists) + Expect(active).To(Equal(contextNameOne), activeContextShouldBeRecentlyAddedOne) + + pluginsListOne, err = tf.PluginCmd.ListPluginsForGivenContext(contextNameOne, true) + Expect(err).To(BeNil(), noErrorForPluginList) + Expect(len(pluginsListOne)).Should(Equal(len(pluginsToGenerateMockResponseOne)), numberOfPluginsSameAsNoOfPluginsInfoMocked) + Expect(f.CheckAllPluginsExists(pluginsListOne, pluginsToGenerateMockResponseOne)).Should(BeTrue(), pluginsInstalledAndMockedShouldBeSame) + }) + // Test case: c. update mock response with additional plugins, restart mock server, run plugin sync, and validate plugin list + It("update mock response for second context, restart mock server, create second context, and validate plugin list", func() { + + pluginsToGenerateMockResponseTwo = pluginsFromPluginGroup[:numberOfPluginsToInstall*2] + TMCEndpointResponseTwo, err = f.ConvertPluginsInfoToTMCEndpointMockResponse(pluginsToGenerateMockResponseTwo) + + err = f.WriteToFileInJSONFormat(TMCEndpointResponseTwo, tmcPluginsMockFilePath) + Expect(err).To(BeNil(), noErrorForMockResponseFileUpdate) + + // start http mock server + err = f.StartMockServer(tf, tmcConfigFolderPath, f.HttpMockServerName) + Expect(err).To(BeNil(), mockServerShouldStartWithoutError) + + // check the tmc mocked endpoint is working as expected + var mockResPluginsInfo f.TMCPluginsInfo + err = f.GetHTTPCall(f.TMCPluginsMockServerEndpoint, &mockResPluginsInfo) + Expect(err).To(BeNil(), "there should not be any error for GET http call on mockapi endpoint:"+f.TMCPluginsMockServerEndpoint) + Expect(len(mockResPluginsInfo.Plugins)).Should(Equal(len(pluginsToGenerateMockResponseTwo)), "the number of plugins in endpoint response and initially mocked should be same") + + _, err = tf.PluginCmd.Sync() + Expect(err).To(BeNil(), "should not get any error for plugin sync") + + pluginsListOne, err = tf.PluginCmd.ListPluginsForGivenContext(contextNameOne, true) + Expect(err).To(BeNil(), noErrorForPluginList) + Expect(len(pluginsListOne)).Should(Equal(len(pluginsToGenerateMockResponseTwo)), numberOfPluginsSameAsNoOfPluginsInfoMocked) + Expect(f.CheckAllPluginsExists(pluginsListOne, pluginsToGenerateMockResponseTwo)).Should(BeTrue(), pluginsInstalledAndMockedShouldBeSame) + }) + + // d. delete both contexts + It("delete current context", func() { + err = tf.ContextCmd.DeleteContext(contextNameOne) + Expect(err).To(BeNil(), deleteContextWithoutError) + err = f.StopContainer(tf, f.HttpMockServerName) + Expect(err).To(BeNil(), mockServerShouldStopWithoutError) + }) + }) + + // Use case 6: k8s and tmc contexts coexistnce, create k8s context and tmc context, validate plugins list and plugin sync + // test cases: + // Test case: a. k8s: create KIND cluster, apply CRD + // Test case: b. k8s: apply CRD (cluster resource definition) and CR's (cluster resource) for few plugins + // Test case: c. k8s: create context and make sure context has created + // Test case: d. k8s: list plugins and validate plugins info, make sure all plugins are installed for which CRs were present on the cluster + // Test case: e. TMC: mock tmc endpoint with plugins info, start the mock server + // Test case: f. TMC: create context and make sure context has created + // Test case: g. TMC: list plugins and validate plugins info, make sure all plugins are installed as per mock response + // Test case: h. k8s: use first k8s context and check plugin list + // Test case: i. k8s: uninstall one of the installed plugin, make sure plugin is uninstalled, run plugin sync, make sure the uninstalled plugin has installed again. + // Test case: j. tmc: use tmc context and check plugin list + // Test case: k. delete tmc/k8s contexts and the KIND cluster + // f. delete current context and KIND cluster + Context("Use case: create k8s and tmc specific contexts, validate plugins list and perform pluin sync, and perform context switch", func() { + var clusterInfo *f.ClusterInfo + var pluginCRFilePaths []string + var pluginsInfoForCRsApplied, installedPluginsListK8s []*f.PluginInfo + var contextNameK8s string + var err error + // Test case: a. k8s: create KIND cluster, apply CRD + It("create KIND cluster", func() { + // Create KIND cluster, which is used in test cases to create context's + clusterInfo, err = f.CreateKindCluster(tf, f.ContextPrefixK8s+f.RandomNumber(4)) + Expect(err).To(BeNil(), "should not get any error for KIND cluster creation") + }) + // Test case: b. k8s: apply CRD (cluster resource definition) and CR's (cluster resource) for few plugins + It("apply CRD and CRs to KIND cluster", func() { + err = f.ApplyConfigOnKindCluster(tf, clusterInfo, append(make([]string, 0), f.K8SCRDFilePath)) + Expect(err).To(BeNil(), "should not get any error for config apply") + + pluginsToGenerateCRs, ok := pluginGroupToPluginListMap[usePluginsFromK8sPluginGroup] + Expect(ok).To(BeTrue(), "plugin group is not exist in the map") + Expect(len(pluginsToGenerateCRs) > numberOfPluginsToInstall).To(BeTrue(), "we don't have enough plugins in local test central repo") + pluginsInfoForCRsApplied, pluginCRFilePaths, err = f.CreateTemporaryCRsForPluginsInGivenPluginGroup(pluginsToGenerateCRs[:numberOfPluginsToInstall]) + Expect(err).To(BeNil(), "should not get any error while generating CR files") + err = f.ApplyConfigOnKindCluster(tf, clusterInfo, pluginCRFilePaths) + Expect(err).To(BeNil(), "should not get any error for config apply") + }) + + // Test case: c. k8s: create context and make sure context has created + It("create context with kubeconfig and context", func() { + By("create context with kubeconfig and context") + contextNameK8s = f.ContextPrefixK8s + f.RandomString(4) + err := tf.ContextCmd.CreateContextWithKubeconfig(contextNameK8s, clusterInfo.KubeConfigPath, clusterInfo.ClusterKubeContext) + Expect(err).To(BeNil(), "context should create without any error") + active, err := tf.ContextCmd.GetActiveContext(string(types.TargetK8s)) + Expect(err).To(BeNil(), "there should be a active context") + Expect(active).To(Equal(contextNameK8s), "the active context should be recently added context") + }) + // Test case: d. k8s: list plugins and validate plugins info, make sure all plugins are installed for which CRs were present on the cluster + It("list plugins and validate plugins being installed after context being created", func() { + installedPluginsListK8s, err = tf.PluginCmd.ListPluginsForGivenContext(contextNameK8s, true) + Expect(err).To(BeNil(), "should not get any error for plugin list") + Expect(len(installedPluginsListK8s)).Should(Equal(len(pluginsInfoForCRsApplied)), "number of plugins should be same as number of plugins CRs applied") + Expect(f.CheckAllPluginsExists(installedPluginsListK8s, pluginsInfoForCRsApplied)).Should(BeTrue(), " plugins being installed and plugins info for which CRs applied should be same") + }) + + var pluginsToGenerateMockResponse, installedPluginsListTMC []*f.PluginInfo + var contextNameTMC string + var ok bool + + // Test case: e. TMC: mock tmc endpoint with plugins info, start the mock server + It("mock tmc endpoint with expected plugins response and restart REST API mock server", func() { + // get plugins from a group + pluginsToGenerateMockResponse, ok = pluginGroupToPluginListMap[usePluginsFromTmcPluginGroup] + Expect(ok).To(BeTrue(), pluginGroupShouldExists) + Expect(len(pluginsToGenerateMockResponse) > numberOfPluginsToInstall).To(BeTrue(), testRepoDoesNotHaveEnoughPlugins) + // mock tmc endpoint with only specific number of plugins info + pluginsToGenerateMockResponse = pluginsToGenerateMockResponse[:numberOfPluginsToInstall] + mockReqResMapping, err := f.ConvertPluginsInfoToTMCEndpointMockResponse(pluginsToGenerateMockResponse[:numberOfPluginsToInstall]) + Expect(err).To(BeNil(), noErrorForMockResponsePreparation) + err = f.WriteToFileInJSONFormat(mockReqResMapping, tmcPluginsMockFilePath) + Expect(err).To(BeNil(), noErrorForMockResponseFileUpdate) + + // start http mock server + err = f.StartMockServer(tf, tmcConfigFolderPath, f.HttpMockServerName) + Expect(err).To(BeNil(), mockServerShouldStartWithoutError) + var mockResPluginsInfo f.TMCPluginsInfo + // check the tmc mocked endpoint is working as expected + err = f.GetHTTPCall(f.TMCPluginsMockServerEndpoint, &mockResPluginsInfo) + Expect(err).To(BeNil(), "there should not be any error for GET http call on mockapi endpoint:"+f.TMCPluginsMockServerEndpoint) + Expect(len(mockResPluginsInfo.Plugins)).Should(Equal(len(pluginsToGenerateMockResponse)), "the number of plugins in endpoint response and initially mocked should be same") + }) + // Test case: f. TMC: create context and make sure context has created + It("create context for TMC target with http mock server URL as endpoint", func() { + contextNameTMC = f.ContextPrefixTMC + f.RandomString(4) + _, err = tf.ContextCmd.CreateContextWithEndPointStaging(contextNameTMC, f.TMCMockServerEndpoint, f.AddAdditionalFlagAndValue(forceCSPFlag)) + Expect(err).To(BeNil(), noErrorWhileCreatingContext) + active, err := tf.ContextCmd.GetActiveContext(string(types.TargetTMC)) + Expect(err).To(BeNil(), activeContextShouldExists) + Expect(active).To(Equal(contextNameTMC), activeContextShouldBeRecentlyAddedOne) + }) + + // Test case: g. TMC: list plugins and validate plugins info, make sure all plugins are installed as per mock response + It("list plugins and validate plugins being installed after context being created", func() { + installedPluginsListTMC, err = tf.PluginCmd.ListPluginsForGivenContext(contextNameTMC, true) + Expect(err).To(BeNil(), noErrorForPluginList) + Expect(len(installedPluginsListTMC)).Should(Equal(len(pluginsToGenerateMockResponse)), numberOfPluginsSameAsNoOfPluginsInfoMocked) + Expect(f.CheckAllPluginsExists(installedPluginsListTMC, pluginsToGenerateMockResponse)).Should(BeTrue(), pluginsInstalledAndMockedShouldBeSame) + }) + + // Test case: h. k8s: use first k8s context and check plugin list + It("use first context, check plugin list", func() { + err = tf.ContextCmd.UseContext(contextNameK8s) + Expect(err).To(BeNil(), "use context should not return any error") + active, err := tf.ContextCmd.GetActiveContext(string(types.TargetK8s)) + Expect(err).To(BeNil(), activeContextShouldExists) + Expect(active).To(Equal(contextNameK8s), active, "the active context should be the recently switched one") + + installedPluginsListK8s, err = tf.PluginCmd.ListPluginsForGivenContext(contextNameK8s, true) + Expect(err).To(BeNil(), "should not get any error for plugin list") + Expect(len(installedPluginsListK8s)).Should(Equal(len(pluginsInfoForCRsApplied)), "number of plugins should be same as number of plugins CRs applied") + Expect(f.CheckAllPluginsExists(installedPluginsListK8s, pluginsInfoForCRsApplied)).Should(BeTrue(), " plugins being installed and plugins info for which CRs applied should be same") + }) + + // Test case: i. k8s: uninstall one of the installed plugin, make sure plugin is uninstalled, run plugin sync, make sure the uninstalled plugin has installed again. + It("Uninstall one of the installed plugin", func() { + pluginToUninstall := pluginsInfoForCRsApplied[0] + err := tf.PluginCmd.UninstallPlugin(pluginToUninstall.Name, pluginToUninstall.Target) + Expect(err).To(BeNil(), "should not get any error for plugin uninstall") + + latestPluginsInstalledList := pluginsInfoForCRsApplied[1:] + allPluginsList, err := tf.PluginCmd.ListPluginsForGivenContext(contextNameK8s, false) + Expect(err).To(BeNil(), "should not get any error for plugin list") + installedPluginsListK8s = f.GetInstalledPlugins(allPluginsList) + Expect(f.IsPluginExists(allPluginsList, f.GetGivenPluginFromTheGivenPluginList(allPluginsList, pluginToUninstall), f.NotInstalled)).To(BeTrue(), "uninstalled plugin should be listed as not installed") + Expect(len(installedPluginsListK8s)).Should(Equal(len(latestPluginsInstalledList)), "number of plugins should be same as number of plugins CRs applied") + Expect(f.CheckAllPluginsExists(installedPluginsListK8s, latestPluginsInstalledList)).Should(BeTrue(), " plugins being installed and plugins info for which CRs applied should be same") + + _, err = tf.PluginCmd.Sync() + Expect(err).To(BeNil(), "should not get any error for plugin sync") + + installedPluginsListK8s, err = tf.PluginCmd.ListPluginsForGivenContext(contextNameK8s, true) + Expect(err).To(BeNil(), "should not get any error for plugin list") + Expect(len(installedPluginsListK8s)).Should(Equal(len(pluginsInfoForCRsApplied)), "number of plugins should be same as number of plugins CRs applied") + Expect(f.CheckAllPluginsExists(installedPluginsListK8s, pluginsInfoForCRsApplied)).Should(BeTrue(), "plugins being installed and plugins info for which CRs applied should be same") + }) + + // Test case: j. tmc: use tmc context and check plugin list + It("use second context again, check plugin list", func() { + + err = tf.ContextCmd.UseContext(contextNameTMC) + Expect(err).To(BeNil(), "use context should not return any error") + active, err := tf.ContextCmd.GetActiveContext(string(types.TargetTMC)) + Expect(err).To(BeNil(), activeContextShouldExists) + Expect(active).To(Equal(contextNameTMC), activeContextShouldBeRecentlyAddedOne) + + // use context will install the plugins from the tmc endpoint, which is second set of plugins list + installedPluginsListTMC, err = tf.PluginCmd.ListPluginsForGivenContext(contextNameTMC, true) + Expect(err).To(BeNil(), noErrorForPluginList) + Expect(len(installedPluginsListTMC)).Should(Equal(len(pluginsToGenerateMockResponse)), numberOfPluginsSameAsNoOfPluginsInfoMocked) + Expect(f.CheckAllPluginsExists(installedPluginsListTMC, pluginsToGenerateMockResponse)).Should(BeTrue(), pluginsInstalledAndMockedShouldBeSame) + }) + + // Test case: k. delete tmc/k8s contexts and the KIND cluster + It("delete tmc/k8s contexts and the KIND cluster", func() { + err = tf.ContextCmd.DeleteContext(contextNameTMC) + Expect(err).To(BeNil(), "context should be deleted without error") + err = f.StopContainer(tf, f.HttpMockServerName) + Expect(err).To(BeNil(), mockServerShouldStopWithoutError) + + err = tf.ContextCmd.DeleteContext(contextNameK8s) + Expect(err).To(BeNil(), "context should be deleted without error") + _, err := tf.KindCluster.DeleteCluster(clusterInfo.Name) + Expect(err).To(BeNil(), "kind cluster should be deleted without any error") + }) + }) +})