From 92fa8ed97a39c7fdb346eccc86881b32bbab77ef Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Wed, 27 Nov 2024 20:36:50 -0500 Subject: [PATCH] Avoid uselessly trying to migrate plugins The only time we need to migrate context-scope plugins is after moving from a CLI < 1.3 to a newer one. This commit makes use of the existing global initializer to do this migration once, for all contexts. This avoids an unnecessary slow down for every CLI commands, especially shell completion which should be as fast as possible. Signed-off-by: Marc Khouzam --- pkg/catalog/cleanup.go | 20 +- pkg/catalog/cleanup_test.go | 382 +++++++++++++++++++++ pkg/globalinit/catalog_cache_init.go | 22 +- pkg/pluginsupplier/plugin_supplier.go | 5 - pkg/pluginsupplier/plugin_supplier_test.go | 227 ------------ 5 files changed, 408 insertions(+), 248 deletions(-) create mode 100644 pkg/catalog/cleanup_test.go diff --git a/pkg/catalog/cleanup.go b/pkg/catalog/cleanup.go index 16f013cb8..f4d5943ef 100644 --- a/pkg/catalog/cleanup.go +++ b/pkg/catalog/cleanup.go @@ -4,7 +4,6 @@ package catalog import ( - configlib "github.com/vmware-tanzu/tanzu-plugin-runtime/config" configtypes "github.com/vmware-tanzu/tanzu-plugin-runtime/config/types" ) @@ -47,27 +46,24 @@ func DeleteIncorrectPluginEntriesFromCatalog() { } // MigrateContextPluginsAsStandaloneIfNeeded updates the catalog cache to move all the -// context-scoped plugins associated with the active context as standalone plugins +// context-scoped plugins as standalone plugins // and removes the context-scoped plugin mapping from the catalog cache. -// This is to ensure backwards compatibility when user migrates from pre v1.3 version of -// the CLI, the context-scoped plugins are still gets shown as installed +// This is to ensure backwards compatibility when the user migrates from pre v1.3 version of +// the CLI, the context-scoped plugins are still shown as installed func MigrateContextPluginsAsStandaloneIfNeeded() { - activeContexts, err := configlib.GetAllActiveContextsList() - if err != nil || len(activeContexts) == 0 { - return - } - c, lockedFile, err := getCatalogCache(true) if err != nil { return } defer lockedFile.Close() - for _, ac := range activeContexts { - for pluginKey, installPath := range c.ServerPlugins[ac] { + for _, association := range c.ServerPlugins { + for pluginKey, installPath := range association { c.StandAlonePlugins.Add(pluginKey, installPath) } - delete(c.ServerPlugins, ac) } + // Delete all entries by reassigning to a new empty map + c.ServerPlugins = make(map[string]PluginAssociation) + _ = saveCatalogCache(c, lockedFile) } diff --git a/pkg/catalog/cleanup_test.go b/pkg/catalog/cleanup_test.go new file mode 100644 index 000000000..59e395dc3 --- /dev/null +++ b/pkg/catalog/cleanup_test.go @@ -0,0 +1,382 @@ +// Copyright 2024 VMware, Inc. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package catalog + +import ( + "os" + "path/filepath" + "sort" + "testing" + + "github.com/otiai10/copy" + + "github.com/vmware-tanzu/tanzu-cli/pkg/cli" + "github.com/vmware-tanzu/tanzu-cli/pkg/common" + "github.com/vmware-tanzu/tanzu-cli/pkg/constants" + "github.com/vmware-tanzu/tanzu-plugin-runtime/config/types" + "github.com/vmware-tanzu/tanzu-plugin-runtime/plugin" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestCatalogSuite(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "plugin catalog test suite") +} + +var _ = Describe("GetInstalledPlugins (standalone plugins)", func() { + var ( + cdir string + err error + configFile *os.File + configFileNG *os.File + pd1 *cli.PluginInfo + pd2 *cli.PluginInfo + pd3 *cli.PluginInfo + pd4 *cli.PluginInfo + pd5 *cli.PluginInfo + pd6 *cli.PluginInfo + originalVarValue string + ) + const ( + tmcContextName = "test-tmc-context" + k8sContextName = "test-mc" + ) + BeforeEach(func() { + cdir, err = os.MkdirTemp("", "test-catalog-cache") + Expect(err).ToNot(HaveOccurred()) + common.DefaultCacheDir = cdir + + configFile, err = os.CreateTemp("", "config") + Expect(err).To(BeNil()) + err = copy.Copy(filepath.Join("..", "fakes", "config", "tanzu_config.yaml"), configFile.Name()) + Expect(err).To(BeNil(), "Error while copying tanzu config file for testing") + os.Setenv("TANZU_CONFIG", configFile.Name()) + + configFileNG, err = os.CreateTemp("", "config_ng") + Expect(err).To(BeNil()) + os.Setenv("TANZU_CONFIG_NEXT_GEN", configFileNG.Name()) + err = copy.Copy(filepath.Join("..", "fakes", "config", "tanzu_config_ng.yaml"), configFileNG.Name()) + Expect(err).To(BeNil(), "Error while coping tanzu config-ng file for testing") + + originalVarValue = os.Getenv(constants.ConfigVariableStandaloneOverContextPlugins) + }) + AfterEach(func() { + os.RemoveAll(cdir) + os.Unsetenv("TANZU_CONFIG") + os.Unsetenv("TANZU_CONFIG_NEXT_GEN") + os.RemoveAll(configFile.Name()) + os.RemoveAll(configFileNG.Name()) + + os.Setenv(constants.ConfigVariableStandaloneOverContextPlugins, originalVarValue) + }) + + Context("when no standalone or server plugins installed", func() { + + It("should return empty plugin list", func() { + MigrateContextPluginsAsStandaloneIfNeeded() + + installedPlugins, err := getInstalledPlugins() + Expect(err).ToNot(HaveOccurred()) + Expect(len(installedPlugins)).To(Equal(0)) + }) + }) + Context("when a standalone plugins installed", func() { + BeforeEach(func() { + pd1, err = fakeInstallPlugin("", "fake-server-plugin1", types.TargetK8s, "v1.0.0") + Expect(err).ToNot(HaveOccurred()) + }) + + It("should return installed standalone plugin ", func() { + MigrateContextPluginsAsStandaloneIfNeeded() + + installedPlugins, err := getInstalledPlugins() + Expect(err).ToNot(HaveOccurred()) + Expect(len(installedPlugins)).To(Equal(1)) + Expect(installedPlugins).Should(ContainElement(*pd1)) + }) + + It("should return correct result for IsPluginInstalled", func() { + MigrateContextPluginsAsStandaloneIfNeeded() + + isInstalled := isPluginInstalled("fake-server-plugin1", types.TargetK8s, "v1.0.0") + Expect(isInstalled).To(BeTrue()) + isInstalled = isPluginInstalled("random-plugin", types.TargetK8s, "v1.0.0") + Expect(isInstalled).To(BeFalse()) + }) + }) + + Context("when a standalone and server plugin for k8s target installed", func() { + BeforeEach(func() { + pd1, err = fakeInstallPlugin("", "fake-server-plugin1", types.TargetK8s, "v1.0.0") + Expect(err).ToNot(HaveOccurred()) + + contextNameFromConfig := k8sContextName + pd2, err = fakeInstallPlugin(contextNameFromConfig, "fake-server-plugin2", types.TargetK8s, "v1.0.0") + Expect(err).ToNot(HaveOccurred()) + }) + + It("should return installed plugins ", func() { + MigrateContextPluginsAsStandaloneIfNeeded() + + installedPlugins, err := getInstalledPlugins() + Expect(err).ToNot(HaveOccurred()) + Expect(len(installedPlugins)).To(Equal(2)) // server plugins will be migrated as standalone + Expect(installedPlugins).Should(ContainElement(*pd1)) + Expect(installedPlugins).Should(ContainElement(*pd2)) + }) + }) + + Context("when a standalone plugin and server plugin for both tmc and k8s targets installed", func() { + BeforeEach(func() { + pd1, err = fakeInstallPlugin("", "fake-server-plugin1", types.TargetK8s, "v1.0.0") + Expect(err).ToNot(HaveOccurred()) + + contextNameFromConfig := k8sContextName + pd2, err = fakeInstallPlugin(contextNameFromConfig, "fake-server-plugin2", types.TargetK8s, "v1.0.0") + Expect(err).ToNot(HaveOccurred()) + + contextNameFromConfig = tmcContextName + pd3, err = fakeInstallPlugin(contextNameFromConfig, "fake-server-plugin3", types.TargetTMC, "v1.0.0") + Expect(err).ToNot(HaveOccurred()) + }) + + It("should return installed plugins ", func() { + MigrateContextPluginsAsStandaloneIfNeeded() + + installedPlugins, err := getInstalledPlugins() + Expect(err).ToNot(HaveOccurred()) + Expect(len(installedPlugins)).To(Equal(3)) // server plugins will be migrated as standalone + Expect(installedPlugins).Should(ContainElement(*pd1)) + Expect(installedPlugins).Should(ContainElement(*pd2)) + Expect(installedPlugins).Should(ContainElement(*pd3)) + }) + }) + + Context("when a standalone plugin and server plugin of the same name and target are installed", func() { + BeforeEach(func() { + sharedPluginName := "fake-plugin" + sharedPluginTarget := types.TargetK8s + pd1, err = fakeInstallPlugin("", sharedPluginName, sharedPluginTarget, "v1.0.0") + Expect(err).ToNot(HaveOccurred()) + + contextNameFromConfig := k8sContextName + pd2, err = fakeInstallPlugin(contextNameFromConfig, sharedPluginName, sharedPluginTarget, "v2.0.0") + Expect(err).ToNot(HaveOccurred()) + }) + + It("should return the server plugin only", func() { + MigrateContextPluginsAsStandaloneIfNeeded() + + installedPlugins, err := getInstalledPlugins() + Expect(err).ToNot(HaveOccurred()) + Expect(len(installedPlugins)).To(Equal(1)) + Expect(installedPlugins).Should(ContainElement(*pd2)) + }) + }) + + Context("when multiple standalone plugins and server plugins are installed with some overlap", func() { + BeforeEach(func() { + pd1, err = fakeInstallPlugin("", "fake-server-plugin1", types.TargetK8s, "v1.0.0") + Expect(err).ToNot(HaveOccurred()) + + contextNameFromConfig := k8sContextName + pd2, err = fakeInstallPlugin(contextNameFromConfig, "fake-server-plugin2", types.TargetK8s, "v1.0.0") + Expect(err).ToNot(HaveOccurred()) + + sharedPluginName := "fake-plugin1" + sharedPluginTarget := types.TargetK8s + pd3, err = fakeInstallPlugin("", sharedPluginName, sharedPluginTarget, "v1.0.0") + Expect(err).ToNot(HaveOccurred()) + pd4, err = fakeInstallPlugin(contextNameFromConfig, sharedPluginName, sharedPluginTarget, "v2.0.0") + Expect(err).ToNot(HaveOccurred()) + + sharedPluginName = "fake-plugin2" + sharedPluginTarget = types.TargetTMC + pd5, err = fakeInstallPlugin("", sharedPluginName, sharedPluginTarget, "v1.0.0") + Expect(err).ToNot(HaveOccurred()) + pd6, err = fakeInstallPlugin(contextNameFromConfig, sharedPluginName, sharedPluginTarget, "v2.0.0") + Expect(err).ToNot(HaveOccurred()) + }) + + It("should not return any server plugins and only standalone plugins should be listed", func() { + MigrateContextPluginsAsStandaloneIfNeeded() + + installedPlugins, err := getInstalledPlugins() + Expect(err).ToNot(HaveOccurred()) + Expect(len(installedPlugins)).To(Equal(4)) + Expect(installedPlugins).Should(ContainElement(*pd1)) + Expect(installedPlugins).Should(ContainElement(*pd2)) + Expect(installedPlugins).ShouldNot(ContainElement(*pd3)) + Expect(installedPlugins).Should(ContainElement(*pd4)) + Expect(installedPlugins).ShouldNot(ContainElement(*pd5)) + Expect(installedPlugins).Should(ContainElement(*pd6)) + }) + }) + + Context("with a catalog cache from an older CLI version", func() { + BeforeEach(func() { + cdir, err = os.MkdirTemp("", "test-catalog-cache") + Expect(err).ToNot(HaveOccurred()) + common.DefaultCacheDir = cdir + + err = copy.Copy( + filepath.Join("..", "fakes", "cache", "catalog_v0.29.yaml"), + // filepath.Join("..", "fakes", "cache", "catalog.yaml"), + filepath.Join(common.DefaultCacheDir, "catalog.yaml")) + Expect(err).To(BeNil(), "Error while copying tanzu catalog file for testing") + + configFile, err = os.CreateTemp("", "config") + Expect(err).To(BeNil()) + err = copy.Copy(filepath.Join("..", "fakes", "config", "tanzu_config.yaml"), configFile.Name()) + Expect(err).To(BeNil(), "Error while copying tanzu config file for testing") + os.Setenv("TANZU_CONFIG", configFile.Name()) + + configFileNG, err = os.CreateTemp("", "config_ng") + Expect(err).To(BeNil()) + os.Setenv("TANZU_CONFIG_NEXT_GEN", configFileNG.Name()) + err = copy.Copy(filepath.Join("..", "fakes", "config", "tanzu_config_ng.yaml"), configFileNG.Name()) + Expect(err).To(BeNil(), "Error while coping tanzu config-ng file for testing") + }) + AfterEach(func() { + os.RemoveAll(cdir) + os.Unsetenv("TANZU_CONFIG") + os.Unsetenv("TANZU_CONFIG_NEXT_GEN") + os.RemoveAll(configFile.Name()) + os.RemoveAll(configFileNG.Name()) + }) + + It("should find the installed standalone plugin along with server plugins with server plugins migrated in catalog", func() { + MigrateContextPluginsAsStandaloneIfNeeded() + + installedStandalonePlugins, err := getInstalledPlugins() + Expect(err).ToNot(HaveOccurred()) + sort.Sort(cli.PluginInfoSorter(installedStandalonePlugins)) + Expect(len(installedStandalonePlugins)).To(Equal(3)) + Expect(installedStandalonePlugins[0]).Should(Equal( + cli.PluginInfo{ + Name: "cluster", + Description: "cluster functionality", + Version: "v0.0.1", + BuildSHA: "01234567", + Digest: "", + Group: plugin.SystemCmdGroup, + DocURL: "", + Hidden: false, + CompletionType: 0, + CompletionArgs: nil, + CompletionCommand: "", + Aliases: nil, + InstallationPath: "/Users/test/Library/Application Support/tanzu-cli/cluster/v0.0.1_2ddee7c0a8ecbef610a651bc8d83657fd3438f1038e817b4a7d44f2d0b3bac72_kubernetes", + Discovery: "test-mc", + Scope: "", + Status: "", + DiscoveredRecommendedVersion: "v0.0.1", + Target: types.TargetK8s, + PostInstallHook: nil, + DefaultFeatureFlags: map[string]bool{}, + InvokedAs: nil, + SupportedContextType: nil, + }, + )) + Expect(installedStandalonePlugins[1]).Should(Equal( + cli.PluginInfo{ + Name: "iam", + Description: "IAM Policies for tmc resources", + Version: "v0.0.1", + BuildSHA: "01234567", + Digest: "", + Group: plugin.ManageCmdGroup, + DocURL: "", + Hidden: false, + CompletionType: 0, + CompletionArgs: nil, + CompletionCommand: "", + Aliases: nil, + InstallationPath: "/Users/test/Library/Application Support/tanzu-cli/iam/v0.0.1_2de17ef20dfb00dd8bcf5cb61cbce3cbddcd0a71fba858817343188c093cef7c_mission-control", + Discovery: "test-tmc-context", + Scope: "", + Status: "", + DiscoveredRecommendedVersion: "v0.0.1", + Target: types.TargetTMC, + PostInstallHook: nil, + DefaultFeatureFlags: map[string]bool{}, + InvokedAs: nil, + SupportedContextType: nil, + }, + )) + Expect(installedStandalonePlugins[2]).Should(Equal( + cli.PluginInfo{ + Name: "isolated-cluster", + Description: "Prepopulating images/bundle for internet-restricted environments", + Version: "v0.29.0", + BuildSHA: "e403941f7", + Digest: "", + Group: plugin.RunCmdGroup, + DocURL: "", + Hidden: false, + CompletionType: 0, + CompletionArgs: nil, + CompletionCommand: "", + Aliases: nil, + InstallationPath: "/Users/test/Library/Application Support/tanzu-cli/isolated-cluster/v0.29.0_78d8b432ca369a161fca39e777aeb81fe63c2ba8b8dd25b1b8270eeab485a2ca_", + Discovery: "", + Scope: "", + Status: "", + DiscoveredRecommendedVersion: "v0.29.0", + Target: types.TargetUnknown, + DefaultFeatureFlags: map[string]bool{}, + PostInstallHook: nil, + }, + )) + }) + }) +}) + +func fakeInstallPlugin(contextName, pluginName string, target types.Target, version string) (*cli.PluginInfo, error) { + cc, err := NewContextCatalogUpdater(contextName) + if err != nil { + return nil, err + } + defer cc.Unlock() + pi := &cli.PluginInfo{ + Name: pluginName, + InstallationPath: "/path/to/plugin/" + pluginName + "/" + version, + Version: version, + Hidden: true, + Target: target, + DefaultFeatureFlags: map[string]bool{ + "test-feature": true, + }, + } + err = cc.Upsert(pi) + if err != nil { + return nil, err + } + return pi, nil +} + +func getInstalledPlugins() ([]cli.PluginInfo, error) { + // Get all the standalone plugins found in the catalog + standAloneCatalog, err := NewContextCatalog("") + if err != nil { + return nil, err + } + return standAloneCatalog.List(), nil +} + +func isPluginInstalled(name string, target types.Target, version string) bool { + // Check if the plugin is already installed, if installed skip the installation of the plugin + installedPlugins, err := getInstalledPlugins() + if err == nil { + for i := range installedPlugins { + if installedPlugins[i].Name == name && + installedPlugins[i].Target == target && + installedPlugins[i].Version == version { + return true + } + } + } + return false +} diff --git a/pkg/globalinit/catalog_cache_init.go b/pkg/globalinit/catalog_cache_init.go index 5e4a9e45a..124ba1778 100644 --- a/pkg/globalinit/catalog_cache_init.go +++ b/pkg/globalinit/catalog_cache_init.go @@ -20,10 +20,12 @@ import ( ) // This global initializer checks if the last executed CLI version is < 1.3.0. -// If so it refreshes the plugin catalog to make sure any remapping data is in the catalog. +// If so it refreshes the plugin catalog to: +// 1- migrate context-scoped plugins as standalone plugins +// 2- make sure any remapping data is in the catalog func init() { - RegisterInitializer("Plugin Info Catalog Initializer", triggerForPreCommandRemapping, refreshPluginCatalog) + RegisterInitializer("Plugin Info Catalog Initializer", triggerForPreCommandRemapping, updatePluginCatalog) } func triggerForPreCommandRemapping() bool { @@ -31,10 +33,22 @@ func triggerForPreCommandRemapping() bool { return lastversion.GetLastExecutedCLIVersion() == lastversion.OlderThan1_3_0 } -// refreshPluginCatalog reads the info from each installed plugin +// updatePluginCatalog does the following catalog udpates: +// 1- Migrate context-scoped plugins as standalone plugins +// 2- Reads the info from each installed plugin and updates the plugin catalog +// +// with the latest info. We need to do this to make sure the command re-mapping +// data is in the cache. +func updatePluginCatalog(outStream io.Writer) error { + catalog.MigrateContextPluginsAsStandaloneIfNeeded() + + return refreshPluginsForRemapping(outStream) +} + +// refreshPluginsForRemapping reads the info from each installed plugin // and updates the plugin catalog with the latest info. // We need to do this to make sure the command re-mapping data is in the cache. -func refreshPluginCatalog(outStream io.Writer) error { +func refreshPluginsForRemapping(outStream io.Writer) error { plugins, err := pluginsupplier.GetInstalledPlugins() if err != nil { return err diff --git a/pkg/pluginsupplier/plugin_supplier.go b/pkg/pluginsupplier/plugin_supplier.go index ac5881115..10b6e3427 100644 --- a/pkg/pluginsupplier/plugin_supplier.go +++ b/pkg/pluginsupplier/plugin_supplier.go @@ -15,11 +15,6 @@ import ( // GetInstalledPlugins return the installed plugins func GetInstalledPlugins() ([]cli.PluginInfo, error) { - // Migrate context-scoped plugins as standalone plugin if required - // TODO(anujc): Think on how to invoke this function just once after the newer version - // of the CLI gets installed as we just need to do this migration once - catalog.MigrateContextPluginsAsStandaloneIfNeeded() - // Get all the standalone plugins found in the catalog standAloneCatalog, err := catalog.NewContextCatalog("") if err != nil { diff --git a/pkg/pluginsupplier/plugin_supplier_test.go b/pkg/pluginsupplier/plugin_supplier_test.go index cc10c4507..9575e658b 100644 --- a/pkg/pluginsupplier/plugin_supplier_test.go +++ b/pkg/pluginsupplier/plugin_supplier_test.go @@ -5,7 +5,6 @@ package pluginsupplier import ( "os" "path/filepath" - "sort" "testing" "github.com/otiai10/copy" @@ -15,7 +14,6 @@ import ( "github.com/vmware-tanzu/tanzu-cli/pkg/common" "github.com/vmware-tanzu/tanzu-cli/pkg/constants" "github.com/vmware-tanzu/tanzu-plugin-runtime/config/types" - "github.com/vmware-tanzu/tanzu-plugin-runtime/plugin" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -73,17 +71,8 @@ var _ = Describe("GetInstalledPlugins (standalone plugins)", func() { configFile *os.File configFileNG *os.File pd1 *cli.PluginInfo - pd2 *cli.PluginInfo - pd3 *cli.PluginInfo - pd4 *cli.PluginInfo - pd5 *cli.PluginInfo - pd6 *cli.PluginInfo originalVarValue string ) - const ( - tmcContextName = "test-tmc-context" - k8sContextName = "test-mc" - ) BeforeEach(func() { cdir, err = os.MkdirTemp("", "test-catalog-cache") Expect(err).ToNot(HaveOccurred()) @@ -141,222 +130,6 @@ var _ = Describe("GetInstalledPlugins (standalone plugins)", func() { Expect(isInstalled).To(BeFalse()) }) }) - - Context("when a standalone and server plugin for k8s target installed", func() { - BeforeEach(func() { - pd1, err = fakeInstallPlugin("", "fake-server-plugin1", types.TargetK8s, "v1.0.0") - Expect(err).ToNot(HaveOccurred()) - - contextNameFromConfig := k8sContextName - pd2, err = fakeInstallPlugin(contextNameFromConfig, "fake-server-plugin2", types.TargetK8s, "v1.0.0") - Expect(err).ToNot(HaveOccurred()) - }) - - It("should return installed plugins ", func() { - installedPlugins, err := GetInstalledPlugins() - Expect(err).ToNot(HaveOccurred()) - Expect(len(installedPlugins)).To(Equal(2)) // server plugins will be migrated as standalone - Expect(installedPlugins).Should(ContainElement(*pd1)) - Expect(installedPlugins).Should(ContainElement(*pd2)) - }) - }) - - Context("when a standalone plugin and server plugin for both tmc and k8s targets installed", func() { - BeforeEach(func() { - pd1, err = fakeInstallPlugin("", "fake-server-plugin1", types.TargetK8s, "v1.0.0") - Expect(err).ToNot(HaveOccurred()) - - contextNameFromConfig := k8sContextName - pd2, err = fakeInstallPlugin(contextNameFromConfig, "fake-server-plugin2", types.TargetK8s, "v1.0.0") - Expect(err).ToNot(HaveOccurred()) - - contextNameFromConfig = tmcContextName - pd3, err = fakeInstallPlugin(contextNameFromConfig, "fake-server-plugin3", types.TargetTMC, "v1.0.0") - Expect(err).ToNot(HaveOccurred()) - }) - - It("should return installed plugins ", func() { - installedPlugins, err := GetInstalledPlugins() - Expect(err).ToNot(HaveOccurred()) - Expect(len(installedPlugins)).To(Equal(3)) // server plugins will be migrated as standalone - Expect(installedPlugins).Should(ContainElement(*pd1)) - Expect(installedPlugins).Should(ContainElement(*pd2)) - Expect(installedPlugins).Should(ContainElement(*pd3)) - }) - }) - - Context("when a standalone plugin and server plugin of the same name and target are installed", func() { - BeforeEach(func() { - sharedPluginName := "fake-plugin" - sharedPluginTarget := types.TargetK8s - pd1, err = fakeInstallPlugin("", sharedPluginName, sharedPluginTarget, "v1.0.0") - Expect(err).ToNot(HaveOccurred()) - - contextNameFromConfig := k8sContextName - pd2, err = fakeInstallPlugin(contextNameFromConfig, sharedPluginName, sharedPluginTarget, "v2.0.0") - Expect(err).ToNot(HaveOccurred()) - }) - - It("should return the server plugin only", func() { - installedPlugins, err := GetInstalledPlugins() - Expect(err).ToNot(HaveOccurred()) - Expect(len(installedPlugins)).To(Equal(1)) - Expect(installedPlugins).Should(ContainElement(*pd2)) - }) - }) - - Context("when multiple standalone plugins and server plugins are installed with some overlap", func() { - BeforeEach(func() { - pd1, err = fakeInstallPlugin("", "fake-server-plugin1", types.TargetK8s, "v1.0.0") - Expect(err).ToNot(HaveOccurred()) - - contextNameFromConfig := k8sContextName - pd2, err = fakeInstallPlugin(contextNameFromConfig, "fake-server-plugin2", types.TargetK8s, "v1.0.0") - Expect(err).ToNot(HaveOccurred()) - - sharedPluginName := "fake-plugin1" - sharedPluginTarget := types.TargetK8s - pd3, err = fakeInstallPlugin("", sharedPluginName, sharedPluginTarget, "v1.0.0") - Expect(err).ToNot(HaveOccurred()) - pd4, err = fakeInstallPlugin(contextNameFromConfig, sharedPluginName, sharedPluginTarget, "v2.0.0") - Expect(err).ToNot(HaveOccurred()) - - sharedPluginName = "fake-plugin2" - sharedPluginTarget = types.TargetTMC - pd5, err = fakeInstallPlugin("", sharedPluginName, sharedPluginTarget, "v1.0.0") - Expect(err).ToNot(HaveOccurred()) - pd6, err = fakeInstallPlugin(contextNameFromConfig, sharedPluginName, sharedPluginTarget, "v2.0.0") - Expect(err).ToNot(HaveOccurred()) - }) - - It("should not return any server plugins and only standalone plugins should be listed", func() { - installedPlugins, err := GetInstalledPlugins() - Expect(err).ToNot(HaveOccurred()) - Expect(len(installedPlugins)).To(Equal(4)) - Expect(installedPlugins).Should(ContainElement(*pd1)) - Expect(installedPlugins).Should(ContainElement(*pd2)) - Expect(installedPlugins).ShouldNot(ContainElement(*pd3)) - Expect(installedPlugins).Should(ContainElement(*pd4)) - Expect(installedPlugins).ShouldNot(ContainElement(*pd5)) - Expect(installedPlugins).Should(ContainElement(*pd6)) - }) - }) - - Context("with a catalog cache from an older CLI version", func() { - BeforeEach(func() { - cdir, err = os.MkdirTemp("", "test-catalog-cache") - Expect(err).ToNot(HaveOccurred()) - common.DefaultCacheDir = cdir - - err = copy.Copy( - filepath.Join("..", "fakes", "cache", "catalog_v0.29.yaml"), - // filepath.Join("..", "fakes", "cache", "catalog.yaml"), - filepath.Join(common.DefaultCacheDir, "catalog.yaml")) - Expect(err).To(BeNil(), "Error while copying tanzu catalog file for testing") - - configFile, err = os.CreateTemp("", "config") - Expect(err).To(BeNil()) - err = copy.Copy(filepath.Join("..", "fakes", "config", "tanzu_config.yaml"), configFile.Name()) - Expect(err).To(BeNil(), "Error while copying tanzu config file for testing") - os.Setenv("TANZU_CONFIG", configFile.Name()) - - configFileNG, err = os.CreateTemp("", "config_ng") - Expect(err).To(BeNil()) - os.Setenv("TANZU_CONFIG_NEXT_GEN", configFileNG.Name()) - err = copy.Copy(filepath.Join("..", "fakes", "config", "tanzu_config_ng.yaml"), configFileNG.Name()) - Expect(err).To(BeNil(), "Error while coping tanzu config-ng file for testing") - }) - AfterEach(func() { - os.RemoveAll(cdir) - os.Unsetenv("TANZU_CONFIG") - os.Unsetenv("TANZU_CONFIG_NEXT_GEN") - os.RemoveAll(configFile.Name()) - os.RemoveAll(configFileNG.Name()) - }) - - It("should find the installed standalone plugin along with server plugins with server plugins migrated in catalog", func() { - installedStandalonePlugins, err := GetInstalledPlugins() - Expect(err).ToNot(HaveOccurred()) - sort.Sort(cli.PluginInfoSorter(installedStandalonePlugins)) - Expect(len(installedStandalonePlugins)).To(Equal(3)) - Expect(installedStandalonePlugins[0]).Should(Equal( - cli.PluginInfo{ - Name: "cluster", - Description: "cluster functionality", - Version: "v0.0.1", - BuildSHA: "01234567", - Digest: "", - Group: plugin.SystemCmdGroup, - DocURL: "", - Hidden: false, - CompletionType: 0, - CompletionArgs: nil, - CompletionCommand: "", - Aliases: nil, - InstallationPath: "/Users/test/Library/Application Support/tanzu-cli/cluster/v0.0.1_2ddee7c0a8ecbef610a651bc8d83657fd3438f1038e817b4a7d44f2d0b3bac72_kubernetes", - Discovery: "test-mc", - Scope: "", - Status: "", - DiscoveredRecommendedVersion: "v0.0.1", - Target: types.TargetK8s, - PostInstallHook: nil, - DefaultFeatureFlags: map[string]bool{}, - InvokedAs: nil, - SupportedContextType: nil, - }, - )) - Expect(installedStandalonePlugins[1]).Should(Equal( - cli.PluginInfo{ - Name: "iam", - Description: "IAM Policies for tmc resources", - Version: "v0.0.1", - BuildSHA: "01234567", - Digest: "", - Group: plugin.ManageCmdGroup, - DocURL: "", - Hidden: false, - CompletionType: 0, - CompletionArgs: nil, - CompletionCommand: "", - Aliases: nil, - InstallationPath: "/Users/test/Library/Application Support/tanzu-cli/iam/v0.0.1_2de17ef20dfb00dd8bcf5cb61cbce3cbddcd0a71fba858817343188c093cef7c_mission-control", - Discovery: "test-tmc-context", - Scope: "", - Status: "", - DiscoveredRecommendedVersion: "v0.0.1", - Target: types.TargetTMC, - PostInstallHook: nil, - DefaultFeatureFlags: map[string]bool{}, - InvokedAs: nil, - SupportedContextType: nil, - }, - )) - Expect(installedStandalonePlugins[2]).Should(Equal( - cli.PluginInfo{ - Name: "isolated-cluster", - Description: "Prepopulating images/bundle for internet-restricted environments", - Version: "v0.29.0", - BuildSHA: "e403941f7", - Digest: "", - Group: plugin.RunCmdGroup, - DocURL: "", - Hidden: false, - CompletionType: 0, - CompletionArgs: nil, - CompletionCommand: "", - Aliases: nil, - InstallationPath: "/Users/test/Library/Application Support/tanzu-cli/isolated-cluster/v0.29.0_78d8b432ca369a161fca39e777aeb81fe63c2ba8b8dd25b1b8270eeab485a2ca_", - Discovery: "", - Scope: "", - Status: "", - DiscoveredRecommendedVersion: "v0.29.0", - Target: types.TargetUnknown, - DefaultFeatureFlags: map[string]bool{}, - PostInstallHook: nil, - }, - )) - }) - }) }) func fakeInstallPlugin(contextName, pluginName string, target types.Target, version string) (*cli.PluginInfo, error) {