From c0e6210b8f01563808b3c72ff95dfd18c0d533a9 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Tue, 19 Sep 2023 21:48:10 -0400 Subject: [PATCH] Cleanup pluginmanager of unused feature flag code (#484) This commit removes all the unused code around feature flags from the `manager.go` file. Those changes are: 1. Remove `ValidatePlugin()` which was never used. There is a separate copy of this function in the tanzu-plugin-runtime repository. 2. Remove `discoverPlugins()` and directly use `discoverSpecificPlugins()` 3. Remove `discoverServerPluginsBasedOnCurrentServer` which is no longer used. 4. Include the logic of `discoverServerPluginsBasedOnAllCurrentContexts()` directly into `DiscoverServerPlugins()` which had become a one-liner. 5. Remove the following functions that were no longer used: - `AvailablePlugins()` - `AvailablePluginsFromLocalSource()` - `availablePlugins()` - `combineDuplicatePlugins()` - `getInstalledButNotDiscoveredStandalonePlugins()` - `DiscoveredFromPlugininfo()` - `availablePluginsFromStandaloneAndServerPlugins()` - `pluginIndexForName()` - `legacyPluginInstall()` - `GetRecommendedVersionOfPlugin()` Completely remove the feature flag constant: FeatureDisableCentralRepositoryForTesting * Update the unit tests for the plugin manager. This commit improves our ability to test the pluginmanager. It does this using two techniques: 1. Creating a test plugin inventory DB in the cache and requesting the inventory code to always use the cache (without checking the digest). To force the use of the cache the commit introduces a test variable `TEST_TANZU_CLI_USE_DB_CACHE_ONLY`. This approach allows to discover plugins and groups without needing a real OCI registry. 2. Creating "fake" plugin binaries in the plugin binary cache. This allows installation of plugins to find the binaries in the cache and therefore never have to go to an OCI registry to download them. With these two techniques, the unit tests can discovery and install plugins, and can also do so using groups. The plugin sync can also be tested with this. Note: the unit tests would still benefit from some tests moving to this approach of testing, but this will need to come in a future effort. Signed-off-by: Marc Khouzam --- go.mod | 2 +- pkg/constants/featureflags.go | 2 - pkg/discovery/oci.go | 10 + pkg/pluginmanager/manager.go | 387 +---------- pkg/pluginmanager/manager_helper_test.go | 245 +++++++ pkg/pluginmanager/manager_test.go | 848 ++++++----------------- pkg/pluginmanager/test/config-ng.yaml | 2 + pkg/pluginmanager/test/config-ng2.yaml | 28 + pkg/pluginmanager/test/config.yaml | 36 +- 9 files changed, 516 insertions(+), 1044 deletions(-) create mode 100644 pkg/pluginmanager/test/config-ng2.yaml diff --git a/go.mod b/go.mod index ddc06ab86..e4799e902 100644 --- a/go.mod +++ b/go.mod @@ -43,7 +43,6 @@ require ( github.com/vmware-tanzu/tanzu-framework/capabilities/client v0.0.0-20230523145612-1c6fbba34686 github.com/vmware-tanzu/tanzu-plugin-runtime v1.0.0 go.pinniped.dev v0.20.0 - go.uber.org/multierr v1.11.0 golang.org/x/mod v0.10.0 golang.org/x/oauth2 v0.8.0 golang.org/x/sync v0.2.0 @@ -212,6 +211,7 @@ require ( go.opentelemetry.io/otel v1.15.0 // indirect go.opentelemetry.io/otel/trace v1.15.0 // indirect go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.24.0 // indirect golang.org/x/crypto v0.9.0 // indirect golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect diff --git a/pkg/constants/featureflags.go b/pkg/constants/featureflags.go index b7a9951fb..d1fa7d5b5 100644 --- a/pkg/constants/featureflags.go +++ b/pkg/constants/featureflags.go @@ -7,8 +7,6 @@ package constants const ( // FeatureContextCommand determines whether to surface the context command. This is disabled by default. FeatureContextCommand = "features.global.context-target-v2" - // FeatureDisableCentralRepositoryForTesting determines if the CLI uses the Central Repository of plugins. - FeatureDisableCentralRepositoryForTesting = "features.global.no-central-repo-test-only" ) // DefaultCliFeatureFlags is used to populate an initially empty config file with default values for feature flags. diff --git a/pkg/discovery/oci.go b/pkg/discovery/oci.go index 286f20b21..7eabd600c 100644 --- a/pkg/discovery/oci.go +++ b/pkg/discovery/oci.go @@ -4,8 +4,10 @@ package discovery import ( + "os" "path" "path/filepath" + "strconv" "github.com/vmware-tanzu/tanzu-cli/pkg/common" "github.com/vmware-tanzu/tanzu-cli/pkg/plugininventory" @@ -22,6 +24,10 @@ func NewOCIDiscovery(name, image string, options ...DiscoveryOptions) Discovery discovery := newDBBackedOCIDiscovery(name, image) discovery.pluginCriteria = opts.PluginDiscoveryCriteria discovery.useLocalCacheOnly = opts.UseLocalCacheOnly + // NOTE: the use of TEST_TANZU_CLI_USE_DB_CACHE_ONLY is for testing only + if useCacheOnlyForTesting, _ := strconv.ParseBool(os.Getenv("TEST_TANZU_CLI_USE_DB_CACHE_ONLY")); useCacheOnlyForTesting { + discovery.useLocalCacheOnly = true + } return discovery } @@ -36,6 +42,10 @@ func NewOCIGroupDiscovery(name, image string, options ...DiscoveryOptions) Group discovery := newDBBackedOCIDiscovery(name, image) discovery.groupCriteria = opts.GroupDiscoveryCriteria discovery.useLocalCacheOnly = opts.UseLocalCacheOnly + // NOTE: the use of TEST_TANZU_CLI_USE_DB_CACHE_ONLY is for testing only + if useCacheOnlyForTesting, _ := strconv.ParseBool(os.Getenv("TEST_TANZU_CLI_USE_DB_CACHE_ONLY")); useCacheOnlyForTesting { + discovery.useLocalCacheOnly = true + } return discovery } diff --git a/pkg/pluginmanager/manager.go b/pkg/pluginmanager/manager.go index e7da64ace..319d08b43 100644 --- a/pkg/pluginmanager/manager.go +++ b/pkg/pluginmanager/manager.go @@ -15,8 +15,6 @@ import ( "strings" "github.com/pkg/errors" - "go.uber.org/multierr" - "golang.org/x/mod/semver" "gopkg.in/yaml.v2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kerrors "k8s.io/apimachinery/pkg/util/errors" @@ -31,7 +29,6 @@ import ( "github.com/vmware-tanzu/tanzu-cli/pkg/cli" "github.com/vmware-tanzu/tanzu-cli/pkg/common" "github.com/vmware-tanzu/tanzu-cli/pkg/config" - "github.com/vmware-tanzu/tanzu-cli/pkg/constants" "github.com/vmware-tanzu/tanzu-cli/pkg/discovery" "github.com/vmware-tanzu/tanzu-cli/pkg/distribution" "github.com/vmware-tanzu/tanzu-cli/pkg/plugincmdtree" @@ -65,34 +62,6 @@ type DeletePluginOptions struct { ForceDelete bool } -// ValidatePlugin validates the plugin info. -func ValidatePlugin(p *cli.PluginInfo) (err error) { - // skip builder plugin for bootstrapping - if p.Name == "builder" { - return nil - } - if p.Name == "" { - err = multierr.Append(err, errors.New("plugin name cannot be empty")) - } - if p.Version == "" { - err = multierr.Append(err, fmt.Errorf("plugin %q version cannot be empty", p.Name)) - } - if !semver.IsValid(p.Version) && p.Version != "dev" { - err = multierr.Append(err, fmt.Errorf("version %q %q is not a valid semantic version", p.Name, p.Version)) - } - if p.Description == "" { - err = multierr.Append(err, fmt.Errorf("plugin %q description cannot be empty", p.Name)) - } - if p.Group == "" { - err = multierr.Append(err, fmt.Errorf("plugin %q group cannot be empty", p.Name)) - } - return -} - -func discoverPlugins(pd []configtypes.PluginDiscovery) ([]discovery.Discovered, error) { - return discoverSpecificPlugins(pd, nil) -} - // discoverSpecificPlugins returns all plugins that match the specified criteria from all PluginDiscovery sources, // along with an aggregated error (if any) that occurred while creating the plugin discovery source or fetching plugins. func discoverSpecificPlugins(pd []configtypes.PluginDiscovery, options ...discovery.DiscoveryOptions) ([]discovery.Discovered, error) { @@ -194,15 +163,6 @@ func GetAdditionalTestPluginDiscoveries() []configtypes.PluginDiscovery { // DiscoverServerPlugins returns the available plugins associated all the active contexts func DiscoverServerPlugins() ([]discovery.Discovered, error) { - // If the context and target feature is enabled, discover plugins from all currentContexts - // Else discover plugin based on current Server - if configlib.IsFeatureActivated(constants.FeatureContextCommand) { - return discoverServerPluginsBasedOnAllCurrentContexts() - } - return discoverServerPluginsBasedOnCurrentServer() -} - -func discoverServerPluginsBasedOnAllCurrentContexts() ([]discovery.Discovered, error) { var plugins []discovery.Discovered var errList []error @@ -218,7 +178,7 @@ func discoverServerPluginsBasedOnAllCurrentContexts() ([]discovery.Discovered, e var discoverySources []configtypes.PluginDiscovery discoverySources = append(discoverySources, context.DiscoverySources...) discoverySources = append(discoverySources, defaultDiscoverySourceBasedOnContext(context)...) - discoveredPlugins, err := discoverPlugins(discoverySources) + discoveredPlugins, err := discoverSpecificPlugins(discoverySources) // If there is an error while discovering plugins from all of the given plugin sources, // append the error to the error list and continue processing the discoveredPlugins, @@ -244,144 +204,6 @@ func discoverServerPluginsBasedOnAllCurrentContexts() ([]discovery.Discovered, e return plugins, kerrors.NewAggregate(errList) } -// discoverServerPluginsBasedOnCurrentServer returns the available plugins associated with the given server -func discoverServerPluginsBasedOnCurrentServer() ([]discovery.Discovered, error) { - var plugins []discovery.Discovered - - server, err := configlib.GetCurrentServer() // nolint:staticcheck // Deprecated - if err != nil || server == nil { - // If servername is not specified than returning empty list - // as there are no server plugins that can be discovered - return plugins, nil - } - var discoverySources []configtypes.PluginDiscovery - discoverySources = append(discoverySources, server.DiscoverySources...) - discoverySources = append(discoverySources, defaultDiscoverySourceBasedOnServer(server)...) - - plugins, err = discoverPlugins(discoverySources) - if err != nil { - log.Warningf(errorWhileDiscoveringPlugins, err.Error()) - } - for i := range plugins { - plugins[i].Scope = common.PluginScopeContext - plugins[i].Status = common.PluginStatusNotInstalled - } - return plugins, nil -} - -// DiscoverPlugins returns all the discovered plugins including standalone and context-scoped plugins -// Context scoped plugin discovery happens for all active contexts -func DiscoverPlugins() ([]discovery.Discovered, []discovery.Discovered) { - serverPlugins, err := DiscoverServerPlugins() - if err != nil { - log.Warningf("unable to discover server plugins, %v", err.Error()) - } - - standalonePlugins, err := DiscoverStandalonePlugins(nil) - if err != nil { - log.Warningf("unable to discover standalone plugins, %v", err.Error()) - } - - // TODO(anuj): Remove duplicate plugins with server plugins getting higher priority - return serverPlugins, standalonePlugins -} - -// AvailablePlugins returns the list of available plugins including discovered and installed plugins. -// Plugin discovery happens for all active contexts -func AvailablePlugins() ([]discovery.Discovered, error) { - discoveredServerPlugins, discoveredStandalonePlugins := DiscoverPlugins() - return availablePlugins(discoveredServerPlugins, discoveredStandalonePlugins) -} - -// AvailablePluginsFromLocalSource returns the list of available plugins from local source -func AvailablePluginsFromLocalSource(localPath string) ([]discovery.Discovered, error) { - localStandalonePlugins, err := DiscoverPluginsFromLocalSource(localPath) - if err != nil { - log.Warningf("Unable to discover standalone plugins from local source, %v", err.Error()) - } - return availablePlugins([]discovery.Discovered{}, localStandalonePlugins) -} - -func availablePlugins(discoveredServerPlugins, discoveredStandalonePlugins []discovery.Discovered) ([]discovery.Discovered, error) { - installedPlugins, err := pluginsupplier.GetInstalledPlugins() - if err != nil { - return nil, err - } - - availablePlugins := availablePluginsFromStandaloneAndServerPlugins(discoveredServerPlugins, discoveredStandalonePlugins) - setAvailablePluginsStatus(availablePlugins, installedPlugins) - - installedStandalonePlugins, err := pluginsupplier.GetInstalledStandalonePlugins() - if err != nil { - return nil, err - } - installedButNotDiscoveredPlugins := getInstalledButNotDiscoveredStandalonePlugins(availablePlugins, installedStandalonePlugins) - availablePlugins = append(availablePlugins, installedButNotDiscoveredPlugins...) - - availablePlugins = combineDuplicatePlugins(availablePlugins) - - return availablePlugins, nil -} - -// combineDuplicatePlugins combines same plugins to eliminate duplicates -// When there is a plugin name conflicts and target of both the plugins are same, remove duplicate one. -// In addition to above, plugin with same name having `k8s` and `none` target are also considered same for -// backward compatibility reasons. Considering, we are adding `k8s` targeted plugins as root level commands. -// -// E.g. A plugin 'foo' getting discovered/installed with `` target and a plugin `foo` getting discovered -// with `k8s` discovery (having `k8s` target) should be treated as same plugin. -// This function takes this case into consideration and removes `` targeted plugin for above the mentioned scenario. -func combineDuplicatePlugins(availablePlugins []discovery.Discovered) []discovery.Discovered { - mapOfSelectedPlugins := make(map[string]discovery.Discovered) - - // TODO: If there are multiple discovered (but not installed) plugins of the same name then we will - // always end up keeping one deterministically (due to the sequence of sources/plugins that we process), - // but we should merge the result and show the combined result for duplicate plugins - combinePluginInstallationStatus := func(plugin1, plugin2 discovery.Discovered) discovery.Discovered { - // Combine the installation status and installedVersion result when combining plugins - if plugin2.Status == common.PluginStatusInstalled { - plugin1.Status = common.PluginStatusInstalled - } - if plugin2.InstalledVersion != "" { - plugin1.InstalledVersion = plugin2.InstalledVersion - } - return plugin1 - } - - for i := range availablePlugins { - if availablePlugins[i].Target == configtypes.TargetUnknown { - // As we are considering None targeted and k8s target plugin to be treated as same plugins - // in the case of plugin name conflicts, using `k8s` target to determine the plugin already - // exists or not. - // If plugin already exists in the map then combining the installation status for both the plugins - key := fmt.Sprintf("%s_%s", availablePlugins[i].Name, configtypes.TargetK8s) - dp, exists := mapOfSelectedPlugins[key] - if !exists { - mapOfSelectedPlugins[key] = availablePlugins[i] - } else { - mapOfSelectedPlugins[key] = combinePluginInstallationStatus(dp, availablePlugins[i]) - } - } else { - // If plugin doesn't exist in the map then add the plugin to the map - // else combine the installation status for both the plugins - key := fmt.Sprintf("%s_%s", availablePlugins[i].Name, availablePlugins[i].Target) - dp, exists := mapOfSelectedPlugins[key] - if !exists { - mapOfSelectedPlugins[key] = availablePlugins[i] - } else if availablePlugins[i].Target == configtypes.TargetK8s || availablePlugins[i].Scope == common.PluginScopeContext { - mapOfSelectedPlugins[key] = combinePluginInstallationStatus(availablePlugins[i], dp) - } - } - } - - var selectedPlugins []discovery.Discovered - for key := range mapOfSelectedPlugins { - selectedPlugins = append(selectedPlugins, mapOfSelectedPlugins[key]) - } - - return selectedPlugins -} - func mergePluginEntries(plugin1, plugin2 *discovery.Discovered) *discovery.Discovered { // Plugins with the same name having `k8s` and `none` targets are also considered the same for // backward compatibility reasons, considering, we are adding `k8s` targeted plugins as root level commands. @@ -540,46 +362,6 @@ func mergeDuplicateGroups(groups []*plugininventory.PluginGroup) []*plugininvent return mergedGroups } -func getInstalledButNotDiscoveredStandalonePlugins(availablePlugins []discovery.Discovered, installedPlugins []cli.PluginInfo) []discovery.Discovered { - var newPlugins []discovery.Discovered - for i := range installedPlugins { - found := false - for j := range availablePlugins { - if installedPlugins[i].Name == availablePlugins[j].Name && installedPlugins[i].Target == availablePlugins[j].Target { - found = true - // If plugin is installed but marked as not installed as part of availablePlugins list - // mark the plugin as installed - // This is possible if user has used --local mode to install the plugin which is also - // getting discovered from the configured discovery sources - if availablePlugins[j].Status == common.PluginStatusNotInstalled { - availablePlugins[j].Status = common.PluginStatusInstalled - } - } - } - if !found { - p := DiscoveredFromPlugininfo(&installedPlugins[i]) - p.Scope = common.PluginScopeStandalone - p.Status = common.PluginStatusInstalled - p.InstalledVersion = installedPlugins[i].Version - newPlugins = append(newPlugins, p) - } - } - return newPlugins -} - -// DiscoveredFromPlugininfo returns discovered plugin object from k8sV1alpha1 -func DiscoveredFromPlugininfo(p *cli.PluginInfo) discovery.Discovered { - dp := discovery.Discovered{ - Name: p.Name, - Description: p.Description, - RecommendedVersion: p.Version, - Source: p.Discovery, - SupportedVersions: []string{p.Version}, - Target: p.Target, - } - return dp -} - func setAvailablePluginsStatus(availablePlugins []discovery.Discovered, installedPlugins []cli.PluginInfo) { for i := range installedPlugins { for j := range availablePlugins { @@ -596,41 +378,6 @@ func setAvailablePluginsStatus(availablePlugins []discovery.Discovered, installe } } -func availablePluginsFromStandaloneAndServerPlugins(discoveredServerPlugins, discoveredStandalonePlugins []discovery.Discovered) []discovery.Discovered { - availablePlugins := discoveredServerPlugins - - // Check whether the default standalone discovery type is local or not - isLocalStandaloneDiscovery := config.GetDefaultStandaloneDiscoveryType() == common.DiscoveryTypeLocal - - for i := range discoveredStandalonePlugins { - matchIndex := pluginIndexForName(availablePlugins, &discoveredStandalonePlugins[i]) - - // Add the standalone plugin to available plugins if it doesn't exist in the serverPlugins list - // OR - // Current standalone discovery or plugin discovered is of type 'local' - // We are overriding the discovered plugins that we got from server in case of 'local' discovery type - // to allow developers to use the plugins that are built locally and not returned from the server - // This local discovery is only used for development purpose and should not be used for production - if matchIndex < 0 { - availablePlugins = append(availablePlugins, discoveredStandalonePlugins[i]) - continue - } - if isLocalStandaloneDiscovery || discoveredStandalonePlugins[i].DiscoveryType == common.DiscoveryTypeLocal { // matchIndex >= 0 is guaranteed here - availablePlugins[matchIndex] = discoveredStandalonePlugins[i] - } - } - return availablePlugins -} - -func pluginIndexForName(availablePlugins []discovery.Discovered, p *discovery.Discovered) int { - for j := range availablePlugins { - if p != nil && p.Name == availablePlugins[j].Name && p.Target == availablePlugins[j].Target { - return j - } - } - return -1 // haven't found a match -} - // DescribePlugin describes a plugin. func DescribePlugin(pluginName string, target configtypes.Target) (info *cli.PluginInfo, err error) { plugins, err := pluginsupplier.GetInstalledPlugins() @@ -701,12 +448,6 @@ func InstallPluginFromContext(pluginName, version string, target configtypes.Tar // we are installing a standalone plugin. // nolint: gocyclo func installPlugin(pluginName, version string, target configtypes.Target, contextName string) error { - if configlib.IsFeatureActivated(constants.FeatureDisableCentralRepositoryForTesting) { - // The legacy installation can figure out if the plugin is from a context - // because it searches all contexts for plugins. So, we don't need to pass on that parameter. - return legacyPluginInstall(pluginName, version, target) - } - discoveries, err := getPluginDiscoveries() if err != nil { return err @@ -781,41 +522,6 @@ func installPlugin(pluginName, version string, target configtypes.Target, contex return kerrors.NewAggregate(errorList) } -// legacyInstallPlugin installs a plugin by name, version and target. -// This function is only used without the Central Repository feature. -func legacyPluginInstall(pluginName, version string, target configtypes.Target) error { - availablePlugins, err := AvailablePlugins() - if err != nil { - return err - } - - var matchedPlugins []discovery.Discovered - for i := range availablePlugins { - if availablePlugins[i].Name == pluginName && - (target == configtypes.TargetUnknown || target == availablePlugins[i].Target) { - matchedPlugins = append(matchedPlugins, availablePlugins[i]) - } - } - if len(matchedPlugins) == 0 { - if target != configtypes.TargetUnknown { - return errors.Errorf("unable to find plugin '%v' with version '%v' for target '%s'", pluginName, version, string(target)) - } - return errors.Errorf("unable to find plugin '%v' with version '%v'", pluginName, version) - } - - if len(matchedPlugins) == 1 { - return installOrUpgradePlugin(&matchedPlugins[0], version, false) - } - - for i := range matchedPlugins { - if matchedPlugins[i].Target == target { - return installOrUpgradePlugin(&matchedPlugins[i], version, false) - } - } - - return errors.Errorf(missingTargetStr, pluginName) -} - // UpgradePlugin upgrades a plugin from the given repository. func UpgradePlugin(pluginName, version string, target configtypes.Target) error { // Upgrade is only triggered from a manual user operation. @@ -901,60 +607,27 @@ func InstallPluginsFromGroup(pluginName, groupIDAndVersion string, options ...Pl } if !pluginExist { - return "", fmt.Errorf("plugin '%s' is not part of the group '%s'", pluginName, groupIDAndVersion) + return groupIDAndVersion, fmt.Errorf("plugin '%s' is not part of the group '%s'", pluginName, groupIDAndVersion) } if !mandatoryPluginsExist { if pluginName == cli.AllPlugins { - return "", fmt.Errorf("plugin group '%s' has no mandatory plugins to install", groupIDAndVersion) + return groupIDAndVersion, fmt.Errorf("plugin group '%s' has no mandatory plugins to install", groupIDAndVersion) } - return "", fmt.Errorf("plugin '%s' from group '%s' is not mandatory to install", pluginName, groupIDAndVersion) + return groupIDAndVersion, fmt.Errorf("plugin '%s' from group '%s' is not mandatory to install", pluginName, groupIDAndVersion) } if numErrors > 0 { - return "", fmt.Errorf("could not install %d plugin(s) from group '%s'", numErrors, groupIDAndVersion) + return groupIDAndVersion, fmt.Errorf("could not install %d plugin(s) from group '%s'", numErrors, groupIDAndVersion) } if numInstalled == 0 { - return "", fmt.Errorf("plugin '%s' is not part of the group '%s'", pluginName, groupIDAndVersion) + return groupIDAndVersion, fmt.Errorf("plugin '%s' is not part of the group '%s'", pluginName, groupIDAndVersion) } return groupIDAndVersion, nil } -// GetRecommendedVersionOfPlugin returns recommended version of the plugin -func GetRecommendedVersionOfPlugin(pluginName string, target configtypes.Target) (string, error) { - availablePlugins, err := AvailablePlugins() - if err != nil { - return "", err - } - - var matchedPlugins []discovery.Discovered - for i := range availablePlugins { - if availablePlugins[i].Name == pluginName && - (target == configtypes.TargetUnknown || target == availablePlugins[i].Target) { - matchedPlugins = append(matchedPlugins, availablePlugins[i]) - } - } - if len(matchedPlugins) == 0 { - if target != configtypes.TargetUnknown { - return "", errors.Errorf("unable to find plugin '%v' for target '%s'", pluginName, string(target)) - } - return "", errors.Errorf("unable to find plugin '%v'", pluginName) - } - - if len(matchedPlugins) == 1 { - return matchedPlugins[0].RecommendedVersion, nil - } - - for i := range matchedPlugins { - if matchedPlugins[i].Target == target { - return matchedPlugins[i].RecommendedVersion, nil - } - } - return "", errors.Errorf(missingTargetStr, pluginName) -} - func installOrUpgradePlugin(p *discovery.Discovered, version string, installTestPlugin bool) error { if p.Target == configtypes.TargetUnknown { log.Infof("Installing plugin '%v:%v'", p.Name, version) @@ -1246,31 +919,22 @@ func doDeletePluginFromCatalog(pluginName string, target configtypes.Target, cat // If the central-repo is disabled, all discovered plugins will be installed. func SyncPlugins() error { log.Info("Checking for required plugins...") - var plugins []discovery.Discovered - var err error errList := make([]error, 0) - if !configlib.IsFeatureActivated(constants.FeatureDisableCentralRepositoryForTesting) { - // We no longer sync standalone plugins. - // With a centralized approach to discovering plugins, synchronizing - // standalone plugins would install ALL plugins available for ALL - // products. - // Instead, we only synchronize any plugins that are specifically specified - // by the contexts. - // - // Note: to install all plugins for a specific product, plugin groups will - // need to be used. - plugins, err = DiscoverServerPlugins() - if err != nil { - errList = append(errList, err) - } - if installedPlugins, err := pluginsupplier.GetInstalledServerPlugins(); err == nil { - setAvailablePluginsStatus(plugins, installedPlugins) - } - } else { - plugins, err = AvailablePlugins() - if err != nil { - errList = append(errList, err) - } + // We no longer sync standalone plugins. + // With a centralized approach to discovering plugins, synchronizing + // standalone plugins would install ALL plugins available for ALL + // products. + // Instead, we only synchronize any plugins that are specifically specified + // by the contexts. + // + // Note: to install all plugins for a specific product, plugin groups will + // need to be used. + plugins, err := DiscoverServerPlugins() + if err != nil { + errList = append(errList, err) + } + if installedPlugins, err := pluginsupplier.GetInstalledServerPlugins(); err == nil { + setAvailablePluginsStatus(plugins, installedPlugins) } installed := false @@ -1402,7 +1066,7 @@ func discoverPluginsFromLocalSource(localPath string) ([]discovery.Discovered, e } } - plugins, err := discoverPlugins(pds) + plugins, err := discoverSpecificPlugins(pds) if err != nil { log.Warningf(errorWhileDiscoveringPlugins, err.Error()) } @@ -1663,11 +1327,8 @@ func FindVersion(recommendedPluginVersion, requestedVersion string) string { // getPluginDiscoveries returns the plugin discoveries found in the configuration file. func getPluginDiscoveries() ([]configtypes.PluginDiscovery, error) { - var testDiscoveries []configtypes.PluginDiscovery - if !configlib.IsFeatureActivated(constants.FeatureDisableCentralRepositoryForTesting) { - // Look for testing discoveries. Those should be stored and searched AFTER the central repo. - testDiscoveries = GetAdditionalTestPluginDiscoveries() - } + // Look for testing discoveries. Those should be stored and searched AFTER the central repo. + testDiscoveries := GetAdditionalTestPluginDiscoveries() // The configured discoveries should be searched BEFORE the test discoveries. // For example, if the staging central repo is added as a test discovery, it diff --git a/pkg/pluginmanager/manager_helper_test.go b/pkg/pluginmanager/manager_helper_test.go index 85a4aa3bf..bdecae440 100644 --- a/pkg/pluginmanager/manager_helper_test.go +++ b/pkg/pluginmanager/manager_helper_test.go @@ -4,10 +4,15 @@ package pluginmanager import ( + "database/sql" + "fmt" "os" "os/exec" "path/filepath" + // Import the sqlite driver + _ "modernc.org/sqlite" + "github.com/otiai10/copy" "github.com/stretchr/testify/assert" @@ -18,9 +23,97 @@ import ( "github.com/vmware-tanzu/tanzu-cli/pkg/common" "github.com/vmware-tanzu/tanzu-cli/pkg/config" "github.com/vmware-tanzu/tanzu-cli/pkg/discovery" + "github.com/vmware-tanzu/tanzu-cli/pkg/plugininventory" "github.com/vmware-tanzu/tanzu-plugin-runtime/log" ) +var testPlugins = []plugininventory.PluginIdentifier{ + {Name: "management-cluster", Target: configtypes.TargetK8s, Version: "v1.6.0"}, + {Name: "cluster", Target: configtypes.TargetK8s, Version: "v1.6.0"}, + {Name: "myplugin", Target: configtypes.TargetK8s, Version: "v1.6.0"}, + {Name: "feature", Target: configtypes.TargetK8s, Version: "v0.2.0"}, + + {Name: "isolated-cluster", Target: configtypes.TargetGlobal, Version: "v1.2.3"}, + {Name: "isolated-cluster", Target: configtypes.TargetGlobal, Version: "v1.3.0"}, + {Name: "login", Target: configtypes.TargetGlobal, Version: "v0.2.0"}, + + {Name: "management-cluster", Target: configtypes.TargetTMC, Version: "v0.0.1"}, + {Name: "management-cluster", Target: configtypes.TargetTMC, Version: "v0.0.2"}, + {Name: "management-cluster", Target: configtypes.TargetTMC, Version: "v0.0.3"}, + {Name: "management-cluster", Target: configtypes.TargetTMC, Version: "v0.2.0"}, + {Name: "cluster", Target: configtypes.TargetTMC, Version: "v0.2.0"}, + {Name: "myplugin", Target: configtypes.TargetTMC, Version: "v0.2.0"}, +} + +const createGroupsStmt = ` +INSERT INTO PluginGroups VALUES( + 'vmware', + 'test', + 'default', + 'v1.6.0', + 'Description for vmware-test/default:v1.6.0', + 'management-cluster', + 'kubernetes', + 'v1.6.0', + 'true', + 'false'); +INSERT INTO PluginGroups VALUES( + 'vmware', + 'test', + 'default', + 'v1.6.0', + 'Description for vmware-test/default:v1.6.0', + 'feature', + 'kubernetes', + 'v0.2.0', + 'true', + 'false'); +INSERT INTO PluginGroups VALUES( + 'vmware', + 'test', + 'default', + 'v1.6.0', + 'Description for vmware-test/default:v1.6.0', + 'myplugin', + 'kubernetes', + 'v1.6.0', + 'true', + 'false'); +INSERT INTO PluginGroups VALUES( + 'vmware', + 'test', + 'default', + 'v1.6.0', + 'Description for vmware-test/default:v1.6.0', + 'isolated-cluster', + 'global', + 'v1.2.3', + 'true', + 'false'); +INSERT INTO PluginGroups VALUES( + 'vmware', + 'test', + 'default', + 'v1.6.0', + 'Description for vmware-test/default:v1.6.0', + 'cluster', + 'kubernetes', + 'v1.6.0', + 'false', + 'false'); +INSERT INTO PluginGroups VALUES( + 'vmware', + 'test', + 'default', + 'v2.1.0', + 'Description for vmware-test/default:v2.1.0', + 'isolated-cluster', + 'global', + 'v1.3.0', + 'true', + 'false'); +` + func findDiscoveredPlugin(discovered []discovery.Discovered, pluginName string, target configtypes.Target) *discovery.Discovered { for i := range discovered { if pluginName == discovered[i].Name && target == discovered[i].Target { @@ -39,6 +132,158 @@ func findPluginInfo(pd []cli.PluginInfo, pluginName string, target configtypes.T return nil } +func findGroupVersion(allGroups []*plugininventory.PluginGroup, id string) bool { + groupID := plugininventory.PluginGroupIdentifierFromID(id) + for _, g := range allGroups { + if g.Publisher == groupID.Publisher && + g.Vendor == groupID.Vendor && + g.Name == groupID.Name { + for v := range g.Versions { + if v == groupID.Version { + return true + } + } + } + } + return false +} + +func setupPluginBinaryInCache(name, version string, target configtypes.Target, digest string) { + dir := filepath.Join(common.DefaultPluginRoot, name) + err := os.MkdirAll(dir, 0755) + if err != nil { + log.Fatal(err, "unable to create temporary directory for plugin binary") + } + + pluginBinary := filepath.Join(dir, fmt.Sprintf("%s_%s_%s", version, digest, target)) + f, err := os.OpenFile(pluginBinary, os.O_CREATE|os.O_WRONLY, 0755) + if err != nil { + log.Fatal(err, "unable to create temporary plugin binary") + } + defer f.Close() + + _, err = fmt.Fprintf(f, + `{"name":"%s", +"description":"Test plugin", +"target":"%s", +"version": "%s", +"buildSHA":"c2dbd15", +"digest":"%s", +"group":"Run", +"docURL":"", +"completionType":0, +"aliases":["test"], +"installationPath":"", +"discovery":"", +"scope":"", +"status":""}`, name, target, version, digest) + + if err != nil { + log.Fatal(err, fmt.Sprintf("Error while generating plugin binary %s", pluginBinary)) + } +} + +func setupPluginEntriesAndBinaries(db *sql.DB) { + digest := "0000000000" + for _, plugin := range testPlugins { + desc := fmt.Sprintf("Plugin %s description", plugin.Name) + for _, osArch := range cli.AllOSArch { + uri := fmt.Sprintf("vmware/test/%s/%s/%s/%s:%s", osArch.OS(), osArch.Arch(), plugin.Target, plugin.Name, plugin.Version) + + _, err := db.Exec("INSERT INTO PluginBinaries (PluginName,Target,RecommendedVersion,Version,Hidden,Description,Publisher,Vendor,OS,Architecture,Digest,URI) VALUES(?,?,'',?,'false',?,'test','vmware',?,?,?,?);", plugin.Name, plugin.Target, plugin.Version, desc, osArch.OS(), osArch.Arch(), digest, uri) + + if err != nil { + log.Fatal(err, fmt.Sprintf("failed to create %s:%s for target %s for testing", plugin.Name, plugin.Version, plugin.Target)) + } + } + setupPluginBinaryInCache(plugin.Name, plugin.Version, plugin.Target, digest) + } +} + +func setupTestPluginInventory() { + // Create a temporary directory for the plugin inventory DB + inventoryDir := filepath.Join( + common.DefaultCacheDir, + common.PluginInventoryDirName, + config.DefaultStandaloneDiscoveryName) + err := os.MkdirAll(inventoryDir, 0755) + if err != nil { + log.Fatal(err, "unable to create temporary directory for plugin inventory") + } + + // Generate a test plugin inventory DB + dbFile, err := os.Create(filepath.Join(inventoryDir, plugininventory.SQliteDBFileName)) + if err != nil { + log.Fatal(err, "unable to create temporary file for plugin inventory") + } + // Open DB with the sqlite driver + db, err := sql.Open("sqlite", dbFile.Name()) + if err != nil { + log.Fatal(err, "unable to open create temporary plugin inventory DB") + } + defer db.Close() + + // Create the table + _, err = db.Exec(plugininventory.CreateTablesSchema) + if err != nil { + log.Fatal(err, "failed to create DB table for testing") + } + + // Add plugin entries to the DB and create the corresponding binaries + setupPluginEntriesAndBinaries(db) + + // Add plugin group entries to the DB + _, err = db.Exec(createGroupsStmt) + if err != nil { + log.Fatal(err, "failed to create plugin groups for testing") + } +} + +func setupPluginSourceForTesting() func() { + tmpDir, err := os.MkdirTemp(os.TempDir(), "") + if err != nil { + log.Fatal(err, "unable to create temporary directory") + } + + common.DefaultPluginRoot = filepath.Join(tmpDir, "plugin-root") + + // Setup the two temporary configuration files + configFile := filepath.Join(tmpDir, "tanzu_config.yaml") + configNextGenFile := filepath.Join(tmpDir, "tanzu_config_ng.yaml") + os.Setenv("TANZU_CONFIG", configFile) + os.Setenv("TANZU_CONFIG_NEXT_GEN", configNextGenFile) + + // Setup both test configuration files + err = copy.Copy(filepath.Join("test", "config.yaml"), configFile) + if err != nil { + log.Fatal(err, "Error while coping tanzu config file for testing") + } + + err = copy.Copy(filepath.Join("test", "config-ng2.yaml"), configNextGenFile) + if err != nil { + log.Fatal(err, "Error while coping tanzu config next gen file for testing") + } + + // Setup a temporary cache directory + common.DefaultCacheDir = filepath.Join(tmpDir, "cache") + + common.DefaultLocalPluginDistroDir = filepath.Join(tmpDir, "distro") + err = copy.Copy(filepath.Join("test", "local"), common.DefaultLocalPluginDistroDir) + if err != nil { + log.Fatal(err, "Error while setting local distro for testing") + } + + setupTestPluginInventory() + os.Setenv("TEST_TANZU_CLI_USE_DB_CACHE_ONLY", "1") + + return func() { + os.RemoveAll(tmpDir) + os.Unsetenv("TANZU_CONFIG") + os.Unsetenv("TANZU_CONFIG_NEXT_GEN") + os.Unsetenv("TEST_TANZU_CLI_USE_DB_CACHE_ONLY") + } +} + func setupLocalDistroForTesting() func() { tmpDir, err := os.MkdirTemp(os.TempDir(), "") if err != nil { diff --git a/pkg/pluginmanager/manager_test.go b/pkg/pluginmanager/manager_test.go index 980480d97..1468b0915 100644 --- a/pkg/pluginmanager/manager_test.go +++ b/pkg/pluginmanager/manager_test.go @@ -8,13 +8,11 @@ import ( "os" "os/exec" "path/filepath" - "strings" "testing" "github.com/pkg/errors" "github.com/stretchr/testify/assert" - configlib "github.com/vmware-tanzu/tanzu-plugin-runtime/config" configtypes "github.com/vmware-tanzu/tanzu-plugin-runtime/config/types" "github.com/vmware-tanzu/tanzu-cli/pkg/cli" @@ -25,7 +23,6 @@ import ( "github.com/vmware-tanzu/tanzu-cli/pkg/distribution" "github.com/vmware-tanzu/tanzu-cli/pkg/plugininventory" "github.com/vmware-tanzu/tanzu-cli/pkg/pluginsupplier" - "github.com/vmware-tanzu/tanzu-plugin-runtime/log" ) var expectedDiscoveredContextPlugins = []discovery.Discovered{ @@ -53,181 +50,152 @@ var expectedDiscoveredContextPlugins = []discovery.Discovered{ } var expectedDiscoveredStandalonePlugins = []discovery.Discovered{ { - Name: "login", - RecommendedVersion: "v0.2.0", + Name: "management-cluster", + Description: "Plugin management-cluster description", + RecommendedVersion: "v1.6.0", + SupportedVersions: []string{"v1.6.0"}, Scope: common.PluginScopeStandalone, ContextName: "", - Target: configtypes.TargetUnknown, + Target: configtypes.TargetK8s, }, { - Name: "feature", - RecommendedVersion: "v0.2.0", + Name: "cluster", + Description: "Plugin cluster description", + RecommendedVersion: "v1.6.0", + SupportedVersions: []string{"v1.6.0"}, Scope: common.PluginScopeStandalone, ContextName: "", Target: configtypes.TargetK8s, }, { - Name: "management-cluster", + Name: "myplugin", + Description: "Plugin myplugin description", RecommendedVersion: "v1.6.0", + SupportedVersions: []string{"v1.6.0"}, Scope: common.PluginScopeStandalone, ContextName: "", Target: configtypes.TargetK8s, }, { - Name: "myplugin", - RecommendedVersion: "v1.6.0", + Name: "feature", + Description: "Plugin feature description", + RecommendedVersion: "v0.2.0", + SupportedVersions: []string{"v0.2.0"}, Scope: common.PluginScopeStandalone, ContextName: "", Target: configtypes.TargetK8s, }, + { + Name: "isolated-cluster", + Description: "Plugin isolated-cluster description", + RecommendedVersion: "v1.3.0", + SupportedVersions: []string{"v1.2.3", "v1.3.0"}, + Scope: common.PluginScopeStandalone, + ContextName: "", + Target: configtypes.TargetGlobal, + }, + { + Name: "login", + Description: "Plugin login description", + RecommendedVersion: "v0.2.0", + SupportedVersions: []string{"v0.2.0"}, + Scope: common.PluginScopeStandalone, + ContextName: "", + Target: configtypes.TargetGlobal, + }, + { + Name: "management-cluster", + Description: "Plugin management-cluster description", + RecommendedVersion: "v0.2.0", + SupportedVersions: []string{"v0.0.1", "v0.0.2", "v0.0.3", "v0.2.0"}, + Scope: common.PluginScopeStandalone, + ContextName: "", + Target: configtypes.TargetTMC, + }, + { + Name: "cluster", + Description: "Plugin cluster description", + RecommendedVersion: "v0.2.0", + SupportedVersions: []string{"v0.2.0"}, + Scope: common.PluginScopeStandalone, + ContextName: "", + Target: configtypes.TargetTMC, + }, { Name: "myplugin", + Description: "Plugin myplugin description", RecommendedVersion: "v0.2.0", + SupportedVersions: []string{"v0.2.0"}, Scope: common.PluginScopeStandalone, ContextName: "", Target: configtypes.TargetTMC, }, } -func Test_DiscoverPlugins(t *testing.T) { +var expectedDiscoveredGroups = []string{"vmware-test/default:v1.6.0", "vmware-test/default:v2.1.0"} + +const ( + testGroupName = "vmware-test/default" + testGroupVersion = "v1.6.0" +) + +func Test_DiscoverStandalonePlugins(t *testing.T) { assertions := assert.New(t) - defer setupLocalDistroForTesting()() + defer setupPluginSourceForTesting()() - serverPlugins, standalonePlugins := DiscoverPlugins() - assertions.Equal(len(expectedDiscoveredContextPlugins), len(serverPlugins)) + standalonePlugins, err := DiscoverStandalonePlugins() + assertions.Nil(err) assertions.Equal(len(expectedDiscoveredStandalonePlugins), len(standalonePlugins)) - discoveredPlugins := append(serverPlugins, standalonePlugins...) - expectedDiscoveredPlugins := append(expectedDiscoveredContextPlugins, expectedDiscoveredStandalonePlugins...) - - for i := 0; i < len(expectedDiscoveredPlugins); i++ { - p := findDiscoveredPlugin(discoveredPlugins, expectedDiscoveredPlugins[i].Name, expectedDiscoveredPlugins[i].Target) + for i := range expectedDiscoveredStandalonePlugins { + p := findDiscoveredPlugin(standalonePlugins, expectedDiscoveredStandalonePlugins[i].Name, expectedDiscoveredStandalonePlugins[i].Target) assertions.NotNil(p) - assertions.Equal(expectedDiscoveredPlugins[i].Name, p.Name) - assertions.Equal(expectedDiscoveredPlugins[i].RecommendedVersion, p.RecommendedVersion) - assertions.Equal(expectedDiscoveredPlugins[i].Target, p.Target) + assertions.Equal(expectedDiscoveredStandalonePlugins[i].Description, p.Description) + assertions.Equal(expectedDiscoveredStandalonePlugins[i].RecommendedVersion, p.RecommendedVersion) + assertions.Equal(expectedDiscoveredStandalonePlugins[i].SupportedVersions, p.SupportedVersions) + assertions.Equal(expectedDiscoveredStandalonePlugins[i].Scope, p.Scope) + assertions.Equal(expectedDiscoveredStandalonePlugins[i].ContextName, p.ContextName) } - - err := configlib.SetFeature("global", "context-target-v2", "false") - assertions.Nil(err) - - serverPlugins, standalonePlugins = DiscoverPlugins() - assertions.Equal(1, len(serverPlugins)) - assertions.Equal(len(expectedDiscoveredStandalonePlugins), len(standalonePlugins)) } -func Test_InstallPlugin_InstalledPlugins_No_Central_Repo(t *testing.T) { +func Test_DiscoverServerPlugins(t *testing.T) { assertions := assert.New(t) - defer setupLocalDistroForTesting()() - execCommand = fakeInfoExecCommand - defer func() { execCommand = exec.Command }() - - // Turn off central repo feature - featureArray := strings.Split(constants.FeatureDisableCentralRepositoryForTesting, ".") - err := configlib.SetFeature(featureArray[1], featureArray[2], "true") - assertions.Nil(err) - - // Try installing nonexistent plugin - err = InstallStandalonePlugin("not-exists", "v0.2.0", configtypes.TargetUnknown) - assertions.NotNil(err) - assertions.Contains(err.Error(), "unable to find plugin 'not-exists'") - - // Install login (standalone) plugin - err = InstallStandalonePlugin("login", "v0.2.0", configtypes.TargetUnknown) - assertions.Nil(err) - // Verify installed plugin - installedPlugins, err := pluginsupplier.GetInstalledPlugins() - assertions.Nil(err) - assertions.Equal(1, len(installedPlugins)) - assertions.Equal("login", installedPlugins[0].Name) + defer setupPluginSourceForTesting()() - // Try installing cluster plugin with no context-type - err = InstallStandalonePlugin("cluster", "v0.2.0", configtypes.TargetUnknown) + serverPlugins, err := DiscoverServerPlugins() assertions.NotNil(err) - assertions.Contains(err.Error(), fmt.Sprintf(missingTargetStr, "cluster")) - - // Try installing cluster plugin with context-type=tmc - err = InstallStandalonePlugin("cluster", "v0.2.0", configtypes.TargetTMC) - assertions.Nil(err) - - // Try installing cluster plugin through context-type=k8s with incorrect version - err = InstallStandalonePlugin("cluster", "v1.0.0", configtypes.TargetK8s) - assertions.NotNil(err) - assertions.Contains(err.Error(), "plugin pre-download verification failed") + assertions.Contains(err.Error(), "unable to list plugins from discovery source 'default-mgmt': Failed to load Kubeconfig file") + assertions.Equal(len(expectedDiscoveredContextPlugins), len(serverPlugins)) - // Try installing cluster plugin through context-type=k8s - err = InstallStandalonePlugin("cluster", "v1.6.0", configtypes.TargetK8s) - assertions.Nil(err) + for i := range expectedDiscoveredContextPlugins { + p := findDiscoveredPlugin(serverPlugins, expectedDiscoveredContextPlugins[i].Name, expectedDiscoveredContextPlugins[i].Target) + assertions.NotNil(p) + assertions.Equal(expectedDiscoveredContextPlugins[i].RecommendedVersion, p.RecommendedVersion) + assertions.Equal(expectedDiscoveredContextPlugins[i].Scope, p.Scope) + assertions.Equal(expectedDiscoveredContextPlugins[i].ContextName, p.ContextName) + } +} - // Try installing management-cluster plugin from standalone discovery without context-type - err = InstallStandalonePlugin("management-cluster", "v1.6.0", configtypes.TargetUnknown) - assertions.NotNil(err) - assertions.Contains(err.Error(), fmt.Sprintf(missingTargetStr, "management-cluster")) +func Test_DiscoverPluginGroups(t *testing.T) { + assertions := assert.New(t) - // Try installing management-cluster plugin from standalone discovery - err = InstallStandalonePlugin("management-cluster", "v1.6.0", configtypes.TargetK8s) - assertions.Nil(err) + defer setupPluginSourceForTesting()() - // Try installing the feature plugin which is targeted for k8s but requesting the TMC target - err = InstallStandalonePlugin("feature", "v0.2.0", configtypes.TargetTMC) - assertions.NotNil(err) - assertions.Contains(err.Error(), "unable to find plugin 'feature' with version 'v0.2.0' for target 'mission-control'") - - // Verify installed plugins - installedStandalonePlugins, err := pluginsupplier.GetInstalledStandalonePlugins() + groups, err := DiscoverPluginGroups() assertions.Nil(err) - assertions.Equal(2, len(installedStandalonePlugins)) - installedServerPlugins, err := pluginsupplier.GetInstalledServerPlugins() - assertions.Nil(err) - assertions.Equal(2, len(installedServerPlugins)) - expectedInstalledServerPlugins := []cli.PluginInfo{ - { - Name: "cluster", - Version: "v1.6.0", - Scope: common.PluginScopeContext, - Target: configtypes.TargetK8s, - }, - { - Name: "cluster", - Version: "v0.2.0", - Scope: common.PluginScopeContext, - Target: configtypes.TargetTMC, - }, - } - expectedInstalledStandalonePlugins := []cli.PluginInfo{ - { - Name: "login", - Version: "v0.2.0", - Scope: common.PluginScopeStandalone, - Target: configtypes.TargetUnknown, - }, - { - Name: "management-cluster", - Version: "v1.6.0", - Scope: common.PluginScopeStandalone, - Target: configtypes.TargetK8s, - }, - } - - for i := 0; i < len(expectedInstalledServerPlugins); i++ { - pd := findPluginInfo(installedServerPlugins, expectedInstalledServerPlugins[i].Name, expectedInstalledServerPlugins[i].Target) - assertions.NotNil(pd) - assertions.Equal(expectedInstalledServerPlugins[i].Version, pd.Version) - } - for i := 0; i < len(expectedInstalledStandalonePlugins); i++ { - pd := findPluginInfo(installedStandalonePlugins, expectedInstalledStandalonePlugins[i].Name, expectedInstalledStandalonePlugins[i].Target) - assertions.NotNil(pd) - assertions.Equal(expectedInstalledStandalonePlugins[i].Version, pd.Version) + for _, id := range expectedDiscoveredGroups { + found := findGroupVersion(groups, id) + assertions.True(found, fmt.Sprintf("unable to find group %s", id)) } } -func Test_InstallPlugin_InstalledPlugins_Central_Repo(t *testing.T) { +func Test_InstallStandalonePlugin(t *testing.T) { assertions := assert.New(t) - defer setupLocalDistroForTesting()() + defer setupPluginSourceForTesting()() execCommand = fakeInfoExecCommand defer func() { execCommand = exec.Command }() @@ -245,25 +213,25 @@ func Test_InstallPlugin_InstalledPlugins_Central_Repo(t *testing.T) { assertions.Equal(1, len(installedPlugins)) assertions.Equal("login", installedPlugins[0].Name) - // Try installing myplugin plugin with no context-type - err = InstallStandalonePlugin("myplugin", "v0.2.0", configtypes.TargetUnknown) + // Try installing myplugin plugin with no context-type and no specific version + err = InstallStandalonePlugin("myplugin", cli.VersionLatest, configtypes.TargetUnknown) assertions.NotNil(err) assertions.Contains(err.Error(), fmt.Sprintf(missingTargetStr, "myplugin")) - // Try installing myplugin plugin with context-type=tmc - err = InstallStandalonePlugin("myplugin", "v0.2.0", configtypes.TargetTMC) + // Try installing myplugin plugin with context-type=tmc with no specific version + err = InstallStandalonePlugin("myplugin", cli.VersionLatest, configtypes.TargetTMC) assertions.Nil(err) // Try installing myplugin plugin through context-type=k8s with incorrect version err = InstallStandalonePlugin("myplugin", "v1.0.0", configtypes.TargetK8s) assertions.NotNil(err) - assertions.Contains(err.Error(), "plugin pre-download verification failed") + assertions.Contains(err.Error(), "unable to find plugin 'myplugin' with version 'v1.0.0'") - // Try installing myplugin plugin through context-type=k8s + // Try installing myplugin plugin through context-type=k8s with the correct version err = InstallStandalonePlugin("myplugin", "v1.6.0", configtypes.TargetK8s) assertions.Nil(err) - // Try installing management-cluster plugin from standalone discovery + // Try installing management-cluster plugin err = InstallStandalonePlugin("management-cluster", "v1.6.0", configtypes.TargetK8s) assertions.Nil(err) @@ -285,7 +253,7 @@ func Test_InstallPlugin_InstalledPlugins_Central_Repo(t *testing.T) { Name: "login", Version: "v0.2.0", Scope: common.PluginScopeStandalone, - Target: configtypes.TargetUnknown, + Target: configtypes.TargetGlobal, }, { Name: "management-cluster", @@ -314,23 +282,76 @@ func Test_InstallPlugin_InstalledPlugins_Central_Repo(t *testing.T) { } } -func Test_InstallPluginFromGroup(t *testing.T) { +func Test_InstallPluginsFromGroup(t *testing.T) { assertions := assert.New(t) - defer setupLocalDistroForTesting()() + defer setupPluginSourceForTesting()() execCommand = fakeInfoExecCommand defer func() { execCommand = exec.Command }() - // A local discovery currently does not support groups, but we can - // at least do negative testing - groupID := "vmware-tkg/default:v2.1.0" - _, err := InstallPluginsFromGroup("cluster", groupID) - assertions.NotNil(err) - assertions.Contains(err.Error(), "unable to create group discovery: unknown group discovery source") + // Install the management-cluster plugin from a group:version + groupID := testGroupName + ":" + testGroupVersion + fullGroupID, err := InstallPluginsFromGroup("management-cluster", groupID) + assertions.Nil(err) + assertions.Equal(groupID, fullGroupID) + + installedStandalonePlugins, err := pluginsupplier.GetInstalledStandalonePlugins() + assertions.Nil(err) + assertions.Equal(1, len(installedStandalonePlugins)) + pd := findPluginInfo(installedStandalonePlugins, "management-cluster", configtypes.TargetK8s) + assertions.NotNil(pd) + assertions.Equal("v1.6.0", pd.Version) + + // Install the isolated-cluster from the latest group + groupID = testGroupName + fullGroupID, err = InstallPluginsFromGroup("isolated-cluster", groupID) + assertions.Nil(err) + assertions.Equal(groupID+":v2.1.0", fullGroupID) + + installedStandalonePlugins, err = pluginsupplier.GetInstalledStandalonePlugins() + assertions.Nil(err) + assertions.Equal(2, len(installedStandalonePlugins)) + pd = findPluginInfo(installedStandalonePlugins, "management-cluster", configtypes.TargetK8s) + assertions.NotNil(pd) + assertions.Equal("v1.6.0", pd.Version) + pd = findPluginInfo(installedStandalonePlugins, "isolated-cluster", configtypes.TargetGlobal) + assertions.NotNil(pd) + assertions.Equal("v1.3.0", pd.Version) + + // Install all plugins from a group:version + // Note that this should replace isolated-cluster:v1.3.0 with its v1.2.3 version + groupID = testGroupName + ":" + testGroupVersion + fullGroupID, err = InstallPluginsFromGroup(cli.AllPlugins, groupID) + assertions.Nil(err) + assertions.Equal(groupID, fullGroupID) + + installedStandalonePlugins, err = pluginsupplier.GetInstalledStandalonePlugins() + assertions.Nil(err) + assertions.Equal(4, len(installedStandalonePlugins)) + pd = findPluginInfo(installedStandalonePlugins, "management-cluster", configtypes.TargetK8s) + assertions.NotNil(pd) + assertions.Equal("v1.6.0", pd.Version) + pd = findPluginInfo(installedStandalonePlugins, "isolated-cluster", configtypes.TargetGlobal) + assertions.NotNil(pd) + assertions.Equal("v1.2.3", pd.Version) + pd = findPluginInfo(installedStandalonePlugins, "myplugin", configtypes.TargetK8s) + assertions.NotNil(pd) + assertions.Equal("v1.6.0", pd.Version) + pd = findPluginInfo(installedStandalonePlugins, "feature", configtypes.TargetK8s) + assertions.NotNil(pd) + assertions.Equal("v0.2.0", pd.Version) +} + +func Test_InstallPluginsFromGroupErrors(t *testing.T) { + assertions := assert.New(t) + + defer setupPluginSourceForTesting()() + execCommand = fakeInfoExecCommand + defer func() { execCommand = exec.Command }() // make sure a poorly formatted group is properly handled - groupID = "invalid" - _, err = InstallPluginsFromGroup("cluster", groupID) + groupID := "invalid" + _, err := InstallPluginsFromGroup("cluster", groupID) assertions.NotNil(err) assertions.Contains(err.Error(), "could not find group") @@ -353,295 +374,18 @@ func Test_InstallPluginFromGroup(t *testing.T) { _, err = InstallPluginsFromGroup("cluster", groupID) assertions.NotNil(err) assertions.Contains(err.Error(), "could not find group") -} -func Test_DiscoverPluginGroups(t *testing.T) { - assertions := assert.New(t) - - defer setupLocalDistroForTesting()() - execCommand = fakeInfoExecCommand - defer func() { execCommand = exec.Command }() - - // A local discovery currently does not support groups, but we can - // at least do negative testing - _, err := DiscoverPluginGroups(nil) + groupID = testGroupName + fullGroupID, err := InstallPluginsFromGroup("cluster", groupID) assertions.NotNil(err) - assertions.Contains(err.Error(), "unable to create group discovery: unknown group discovery source") -} - -func Test_AvailablePlugins(t *testing.T) { - assertions := assert.New(t) - - defer setupLocalDistroForTesting()() - - expectedDiscoveredPlugins := append(expectedDiscoveredContextPlugins, expectedDiscoveredStandalonePlugins...) - discoveredPlugins, err := AvailablePlugins() - assertions.Nil(err) - assertions.Equal(len(expectedDiscoveredPlugins), len(discoveredPlugins)) - - for i := 0; i < len(expectedDiscoveredPlugins); i++ { - pd := findDiscoveredPlugin(discoveredPlugins, expectedDiscoveredPlugins[i].Name, expectedDiscoveredPlugins[i].Target) - assertions.NotNil(pd) - assertions.Equal(expectedDiscoveredPlugins[i].Name, pd.Name) - assertions.Equal(expectedDiscoveredPlugins[i].RecommendedVersion, pd.RecommendedVersion) - assertions.Equal(expectedDiscoveredPlugins[i].Target, pd.Target) - assertions.Equal(expectedDiscoveredPlugins[i].Scope, pd.Scope) - assertions.Equal(common.PluginStatusNotInstalled, pd.Status) - } - - // Install login, myplugin plugins - mockInstallPlugin(assertions, "login", "v0.2.0", configtypes.TargetUnknown) - mockInstallPlugin(assertions, "myplugin", "v0.2.0", configtypes.TargetTMC) - - expectedInstallationStatusOfPlugins := []discovery.Discovered{ - { - Name: "myplugin", - Target: configtypes.TargetTMC, - InstalledVersion: "v0.2.0", - Status: common.PluginStatusInstalled, - }, - { - Name: "myplugin", - Target: configtypes.TargetK8s, - InstalledVersion: "", - Status: common.PluginStatusNotInstalled, - }, - { - Name: "login", - Target: configtypes.TargetUnknown, - InstalledVersion: "v0.2.0", - Status: common.PluginStatusInstalled, - }, - } - - // Get available plugin after install and verify installation status - discoveredPlugins, err = AvailablePlugins() - assertions.Nil(err) - assertions.Equal(len(expectedDiscoveredPlugins), len(discoveredPlugins)) - - for _, eisp := range expectedInstallationStatusOfPlugins { - p := findDiscoveredPlugin(discoveredPlugins, eisp.Name, eisp.Target) - assertions.NotNil(p) - assertions.Equal(eisp.Status, p.Status) - assertions.Equal(eisp.InstalledVersion, p.InstalledVersion) - } - - // Install management-cluster, feature plugins - mockInstallPlugin(assertions, "management-cluster", "v1.6.0", configtypes.TargetK8s) - mockInstallPlugin(assertions, "feature", "v0.2.0", configtypes.TargetK8s) + assertions.Equal(groupID+":v2.1.0", fullGroupID) + assertions.Contains(err.Error(), fmt.Sprintf("plugin 'cluster' is not part of the group '%s'", fullGroupID)) - expectedInstallationStatusOfPlugins = []discovery.Discovered{ - { - Name: "management-cluster", - Target: configtypes.TargetK8s, - InstalledVersion: "v1.6.0", - Status: common.PluginStatusInstalled, - }, - { - Name: "feature", - Target: configtypes.TargetK8s, - InstalledVersion: "v0.2.0", - Status: common.PluginStatusInstalled, - }, - { - Name: "management-cluster", - Target: configtypes.TargetTMC, - InstalledVersion: "", - Status: common.PluginStatusNotInstalled, - }, - { - Name: "login", - Target: configtypes.TargetUnknown, - InstalledVersion: "v0.2.0", - Status: common.PluginStatusInstalled, - }, - } - - // Get available plugin after install and verify installation status - discoveredPlugins, err = AvailablePlugins() - assertions.Nil(err) - assertions.Equal(len(expectedDiscoveredPlugins), len(discoveredPlugins)) - - for _, eisp := range expectedInstallationStatusOfPlugins { - p := findDiscoveredPlugin(discoveredPlugins, eisp.Name, eisp.Target) - assertions.NotNil(p) - assertions.Equal(eisp.Status, p.Status, eisp.Name) - assertions.Equal(eisp.InstalledVersion, p.InstalledVersion, eisp.Name) - } -} - -func Test_AvailablePlugins_With_K8s_None_Target_Plugin_Name_Conflict_With_One_Installed_Getting_Discovered(t *testing.T) { - assertions := assert.New(t) - - defer setupLocalDistroForTesting()() - - expectedDiscoveredPlugins := append(expectedDiscoveredContextPlugins, expectedDiscoveredStandalonePlugins...) - discoveredPlugins, err := AvailablePlugins() - assertions.Nil(err) - assertions.Equal(len(expectedDiscoveredPlugins), len(discoveredPlugins)) - - // Install login, cluster plugins - mockInstallPlugin(assertions, "login", "v0.2.0", configtypes.TargetUnknown) - - // Considering `login` plugin with `` target is already installed and - // getting discovered through some discoveries source - // - // if the same `login` plugin is now getting discovered with `k8s` target - // verify the result of AvailablePlugins - - discoverySource := configtypes.PluginDiscovery{ - Local: &configtypes.LocalDiscovery{ - Name: "fake-with-k8s-target", - Path: "standalone-k8s-target", - }, - } - err = configlib.SetCLIDiscoverySource(discoverySource) - assertions.Nil(err) - - discoveredPlugins, err = AvailablePlugins() - assertions.Nil(err) - assertions.Equal(len(expectedDiscoveredPlugins), len(discoveredPlugins)) - - expectedInstallationStatusOfPlugins := []discovery.Discovered{ - { - Name: "login", - Target: configtypes.TargetK8s, - InstalledVersion: "v0.2.0", - Status: common.PluginStatusInstalled, - }, - } - - for i := range discoveredPlugins { - log.Infof("Discovered: %v, %v, %v, %v", discoveredPlugins[i].Name, discoveredPlugins[i].Target, discoveredPlugins[i].Status, discoveredPlugins[i].InstalledVersion) - } - - for _, eisp := range expectedInstallationStatusOfPlugins { - p := findDiscoveredPlugin(discoveredPlugins, eisp.Name, eisp.Target) - assertions.NotNil(p) - assertions.Equal(eisp.Status, p.Status, eisp.Name) - assertions.Equal(eisp.InstalledVersion, p.InstalledVersion, eisp.Name) - } -} - -func Test_AvailablePlugins_With_K8s_None_Target_Plugin_Name_Conflict_With_Plugin_Installed_But_Not_Getting_Discovered(t *testing.T) { - assertions := assert.New(t) - - defer setupLocalDistroForTesting()() - - expectedDiscoveredPlugins := append(expectedDiscoveredContextPlugins, expectedDiscoveredStandalonePlugins...) - discoveredPlugins, err := AvailablePlugins() - assertions.Nil(err) - assertions.Equal(len(expectedDiscoveredPlugins), len(discoveredPlugins)) - - // Install login, cluster plugins - mockInstallPlugin(assertions, "login", "v0.2.0", configtypes.TargetUnknown) - - // Considering `login` plugin with `` target is already installed and - // getting discovered through some discoveries source - // - // if the same `login` plugin is now getting discovered with `k8s` target - // verify the result of AvailablePlugins - - // Replace old discovery source to point to new standalone discovery where the same plugin is getting - // discovered through k8s target - discoverySource := configtypes.PluginDiscovery{ - Local: &configtypes.LocalDiscovery{ - Name: "fake", - Path: "standalone-k8s-target", - }, - } - err = configlib.SetCLIDiscoverySource(discoverySource) - assertions.Nil(err) - - discoveredPlugins, err = AvailablePlugins() - assertions.Nil(err) - assertions.Equal(len(expectedDiscoveredPlugins), len(discoveredPlugins)) - - expectedInstallationStatusOfPlugins := []discovery.Discovered{ - { - Name: "login", - Target: configtypes.TargetK8s, - InstalledVersion: "v0.2.0", - Status: common.PluginStatusInstalled, - }, - } - - for _, eisp := range expectedInstallationStatusOfPlugins { - p := findDiscoveredPlugin(discoveredPlugins, eisp.Name, eisp.Target) - assertions.NotNil(p) - assertions.Equal(eisp.Status, p.Status, eisp.Name) - assertions.Equal(eisp.InstalledVersion, p.InstalledVersion, eisp.Name) - } -} - -func Test_AvailablePlugins_From_LocalSource(t *testing.T) { - assertions := assert.New(t) - - defer setupLocalDistroForTesting()() - - currentDirAbsPath, _ := filepath.Abs(".") - discoveredPlugins, err := AvailablePluginsFromLocalSource(filepath.Join(currentDirAbsPath, "test", "local")) - assertions.Nil(err) - - expectedInstallationStatusOfPlugins := []discovery.Discovered{ - { - Name: "cluster", - Scope: common.PluginScopeStandalone, - Target: configtypes.TargetK8s, - Status: common.PluginStatusNotInstalled, - }, - { - Name: "management-cluster", - Scope: common.PluginScopeStandalone, - Target: configtypes.TargetK8s, - Status: common.PluginStatusNotInstalled, - }, - { - Name: "management-cluster", - Scope: common.PluginScopeStandalone, - Target: configtypes.TargetTMC, - Status: common.PluginStatusNotInstalled, - }, - { - Name: "login", - Scope: common.PluginScopeStandalone, - Target: configtypes.TargetK8s, - Status: common.PluginStatusNotInstalled, - }, - { - Name: "feature", - Scope: common.PluginScopeStandalone, - Target: configtypes.TargetK8s, - Status: common.PluginStatusNotInstalled, - }, - { - Name: "cluster", - Scope: common.PluginScopeStandalone, - Target: configtypes.TargetTMC, - Status: common.PluginStatusNotInstalled, - }, - { - Name: "myplugin", - Scope: common.PluginScopeStandalone, - Target: configtypes.TargetK8s, - Status: common.PluginStatusNotInstalled, - }, - { - Name: "myplugin", - Scope: common.PluginScopeStandalone, - Target: configtypes.TargetTMC, - Status: common.PluginStatusNotInstalled, - }, - } - - assertions.Equal(len(expectedInstallationStatusOfPlugins), len(discoveredPlugins)) - - for _, eisp := range expectedInstallationStatusOfPlugins { - p := findDiscoveredPlugin(discoveredPlugins, eisp.Name, eisp.Target) - assertions.NotNil(p, "plugin %q with target %q not found", eisp.Name, eisp.Target) - assertions.Equal(eisp.Status, p.Status, "status mismatch for plugin %q with target %q", eisp.Name, eisp.Target) - assertions.Equal(eisp.Scope, p.Scope, "scope mismatch for plugin %q with target %q", eisp.Name, eisp.Target) - } + groupID = testGroupName + ":" + testGroupVersion + fullGroupID, err = InstallPluginsFromGroup("cluster", groupID) + assertions.NotNil(err) + assertions.Equal(groupID, fullGroupID) + assertions.Contains(err.Error(), fmt.Sprintf("plugin 'cluster' from group '%s' is not mandatory to install", fullGroupID)) } func Test_InstallPlugin_InstalledPlugins_From_LocalSource(t *testing.T) { @@ -771,104 +515,44 @@ func Test_DeletePlugin(t *testing.T) { assertions.Contains(err.Error(), fmt.Sprintf(missingTargetStr, "myplugin")) } -func Test_ValidatePlugin(t *testing.T) { - assertions := assert.New(t) - - pd := cli.PluginInfo{} - err := ValidatePlugin(&pd) - assertions.Contains(err.Error(), "plugin name cannot be empty") - - pd.Name = "fake-plugin" - err = ValidatePlugin(&pd) - assertions.NotContains(err.Error(), "plugin name cannot be empty") - assertions.Contains(err.Error(), "plugin \"fake-plugin\" version cannot be empty") - assertions.Contains(err.Error(), "plugin \"fake-plugin\" group cannot be empty") -} - -func Test_SyncPlugins_All_Plugins_No_Central_Repo(t *testing.T) { +func Test_SyncPlugins(t *testing.T) { assertions := assert.New(t) - defer setupLocalDistroForTesting()() + defer setupPluginSourceForTesting()() execCommand = fakeInfoExecCommand defer func() { execCommand = exec.Command }() - // Turn off central repo feature - featureArray := strings.Split(constants.FeatureDisableCentralRepositoryForTesting, ".") - err := configlib.SetFeature(featureArray[1], featureArray[2], "true") - assertions.Nil(err) - - expectedDiscoveredPlugins := append(expectedDiscoveredContextPlugins, expectedDiscoveredStandalonePlugins...) - - // Get all available plugins(standalone+context-aware) and verify the status is `not installed` - discovered, err := AvailablePlugins() - assertions.Nil(err) - assertions.Equal(len(expectedDiscoveredPlugins), len(discovered)) + // Get the server plugins (they are not installed yet) + serverPlugins, err := DiscoverServerPlugins() + assertions.NotNil(err) + // There is an error for the kubernetes discovery since we don't have a cluster + // but other server plugins will be found, so we use those + assertions.Contains(err.Error(), `Failed to load Kubeconfig file from "config"`) + assertions.Equal(len(expectedDiscoveredContextPlugins), len(serverPlugins)) - for _, edp := range expectedDiscoveredPlugins { - p := findDiscoveredPlugin(discovered, edp.Name, edp.Target) + for _, edp := range expectedDiscoveredContextPlugins { + p := findDiscoveredPlugin(serverPlugins, edp.Name, edp.Target) assertions.NotNil(p) assertions.Equal(common.PluginStatusNotInstalled, p.Status) } // Sync all available plugins err = SyncPlugins() - assertions.Nil(err) + assertions.NotNil(err) + // There is an error for the kubernetes discovery since we don't have a cluster + // but other server plugins will be found, so we use those + assertions.Contains(err.Error(), `Failed to load Kubeconfig file from "config"`) - // Get all available plugins(standalone+context-aware) and verify the status is updated to `installed` - discovered, err = AvailablePlugins() + installedServerPlugins, err := pluginsupplier.GetInstalledServerPlugins() assertions.Nil(err) - assertions.Equal(len(expectedDiscoveredPlugins), len(discovered)) + assertions.Equal(len(installedServerPlugins), len(serverPlugins)) - for _, edp := range expectedDiscoveredPlugins { - p := findDiscoveredPlugin(discovered, edp.Name, edp.Target) + for _, isp := range installedServerPlugins { + p := findDiscoveredPlugin(serverPlugins, isp.Name, isp.Target) assertions.NotNil(p) - assertions.Equal(common.PluginStatusInstalled, p.Status) - assertions.Equal(edp.RecommendedVersion, p.InstalledVersion) } } -func Test_getInstalledButNotDiscoveredStandalonePlugins(t *testing.T) { - assertions := assert.New(t) - - availablePlugins := []discovery.Discovered{{Name: "fake1", DiscoveryType: "oci", RecommendedVersion: "v1.0.0", Status: common.PluginStatusInstalled}} - installedPlugin := []cli.PluginInfo{{Name: "fake2", Version: "v2.0.0", Discovery: "local"}} - - // If installed plugin is not part of available(discovered) plugins - plugins := getInstalledButNotDiscoveredStandalonePlugins(availablePlugins, installedPlugin) - assertions.Equal(len(plugins), 1) - assertions.Equal("fake2", plugins[0].Name) - assertions.Equal("v2.0.0", plugins[0].RecommendedVersion) - assertions.Equal(common.PluginStatusInstalled, plugins[0].Status) - - // If installed plugin is part of available(discovered) plugins and provided available plugin is already marked as `installed` - installedPlugin = append(installedPlugin, cli.PluginInfo{Name: "fake1", Version: "v1.0.0", Discovery: "local"}) - plugins = getInstalledButNotDiscoveredStandalonePlugins(availablePlugins, installedPlugin) - assertions.Equal(len(plugins), 1) - assertions.Equal("fake2", plugins[0].Name) - assertions.Equal("v2.0.0", plugins[0].RecommendedVersion) - assertions.Equal(common.PluginStatusInstalled, plugins[0].Status) - - // If installed plugin is part of available(discovered) plugins and provided available plugin is already marked as `not installed` - // then test the availablePlugin status gets updated to `installed` - availablePlugins[0].Status = common.PluginStatusNotInstalled - plugins = getInstalledButNotDiscoveredStandalonePlugins(availablePlugins, installedPlugin) - assertions.Equal(len(plugins), 1) - assertions.Equal("fake2", plugins[0].Name) - assertions.Equal("v2.0.0", plugins[0].RecommendedVersion) - assertions.Equal(common.PluginStatusInstalled, plugins[0].Status) - assertions.Equal(common.PluginStatusInstalled, availablePlugins[0].Status) - - // If installed plugin is part of available(discovered) plugins and versions installed is different than discovered version - availablePlugins[0].Status = common.PluginStatusNotInstalled - availablePlugins[0].RecommendedVersion = "v4.0.0" - plugins = getInstalledButNotDiscoveredStandalonePlugins(availablePlugins, installedPlugin) - assertions.Equal(len(plugins), 1) - assertions.Equal("fake2", plugins[0].Name) - assertions.Equal("v2.0.0", plugins[0].RecommendedVersion) - assertions.Equal(common.PluginStatusInstalled, plugins[0].Status) - assertions.Equal(common.PluginStatusInstalled, availablePlugins[0].Status) -} - func Test_setAvailablePluginsStatus(t *testing.T) { assertions := assert.New(t) @@ -1153,141 +837,6 @@ func TestVerifyPluginPostDownload(t *testing.T) { } } -func Test_removeDuplicates(t *testing.T) { - assertions := assert.New(t) - - tcs := []struct { - name string - inputPlugins []discovery.Discovered - expectedResult []discovery.Discovered - }{ - { - name: "when plugin name-target conflict happens with '' and 'k8s' targeted plugins ", - inputPlugins: []discovery.Discovered{ - { - Name: "foo", - Target: configtypes.TargetUnknown, - Scope: common.PluginScopeStandalone, - }, - { - Name: "foo", - Target: configtypes.TargetK8s, - Scope: common.PluginScopeStandalone, - }, - { - Name: "bar", - Target: configtypes.TargetK8s, - Scope: common.PluginScopeStandalone, - }, - }, - expectedResult: []discovery.Discovered{ - { - Name: "foo", - Target: configtypes.TargetK8s, - Scope: common.PluginScopeStandalone, - }, - { - Name: "bar", - Target: configtypes.TargetK8s, - Scope: common.PluginScopeStandalone, - }, - }, - }, - { - name: "when same plugin exists for '', 'k8s' and 'tmc' target as standalone plugin", - inputPlugins: []discovery.Discovered{ - { - Name: "foo", - Target: configtypes.TargetUnknown, - Scope: common.PluginScopeStandalone, - }, - { - Name: "foo", - Target: configtypes.TargetK8s, - Scope: common.PluginScopeStandalone, - }, - { - Name: "foo", - Target: configtypes.TargetTMC, - Scope: common.PluginScopeStandalone, - }, - }, - expectedResult: []discovery.Discovered{ - { - Name: "foo", - Target: configtypes.TargetK8s, - Scope: common.PluginScopeStandalone, - }, - { - Name: "foo", - Target: configtypes.TargetTMC, - Scope: common.PluginScopeStandalone, - }, - }, - }, - { - name: "when foo standalone plugin is available with `k8s` and `` target and also available as context-scoped plugin with `k8s` target", - inputPlugins: []discovery.Discovered{ - { - Name: "foo", - Target: configtypes.TargetUnknown, - Scope: common.PluginScopeStandalone, - }, - { - Name: "foo", - Target: configtypes.TargetK8s, - Scope: common.PluginScopeStandalone, - }, - { - Name: "foo", - Target: configtypes.TargetK8s, - Scope: common.PluginScopeContext, - }, - }, - expectedResult: []discovery.Discovered{ - { - Name: "foo", - Target: configtypes.TargetK8s, - Scope: common.PluginScopeContext, - }, - }, - }, - { - name: "when tmc targeted plugin exists as standalone as well as context-scope", - inputPlugins: []discovery.Discovered{ - { - Name: "foo", - Target: configtypes.TargetTMC, - Scope: common.PluginScopeStandalone, - }, - { - Name: "foo", - Target: configtypes.TargetTMC, - Scope: common.PluginScopeContext, - }, - }, - expectedResult: []discovery.Discovered{ - { - Name: "foo", - Target: configtypes.TargetTMC, - Scope: common.PluginScopeContext, - }, - }, - }, - } - - for _, tc := range tcs { - t.Run(tc.name, func(t *testing.T) { - result := combineDuplicatePlugins(tc.inputPlugins) - assertions.Equal(len(result), len(tc.expectedResult)) - for i := range tc.expectedResult { - p := findDiscoveredPlugin(result, tc.expectedResult[i].Name, tc.expectedResult[i].Target) - assertions.Equal(p.Scope, tc.expectedResult[i].Scope) - } - }) - } -} - func TestHelperProcess(t *testing.T) { if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { return @@ -1409,17 +958,6 @@ func TestGetPluginDiscoveries(t *testing.T) { assertions.Equal(expectedTestDiscoveries[2], discoveries[4].OCI.Image) assertions.Equal(expectedTestDiscoveries[3], discoveries[5].OCI.Image) - // Test with the Central Repo feature disabled - featureArray := strings.Split(constants.FeatureDisableCentralRepositoryForTesting, ".") - err = configlib.SetFeature(featureArray[1], featureArray[2], "true") - assertions.Nil(err) - - discoveries, err = getPluginDiscoveries() - assertions.Nil(err) - assertions.Equal(2, len(discoveries)) - assertions.Equal("default-local", discoveries[0].Local.Name) - assertions.Equal("fake", discoveries[1].Local.Name) - os.Unsetenv(constants.ConfigVariableAdditionalDiscoveryForTesting) } diff --git a/pkg/pluginmanager/test/config-ng.yaml b/pkg/pluginmanager/test/config-ng.yaml index 9f597f2cf..91ed17a5e 100644 --- a/pkg/pluginmanager/test/config-ng.yaml +++ b/pkg/pluginmanager/test/config-ng.yaml @@ -6,6 +6,8 @@ cli: - local: name: fake path: standalone + eulaStatus: accepted + ceipOptIn: "false" currentContext: kubernetes: mgmt mission-control: tmc-fake diff --git a/pkg/pluginmanager/test/config-ng2.yaml b/pkg/pluginmanager/test/config-ng2.yaml new file mode 100644 index 000000000..5e0f9c519 --- /dev/null +++ b/pkg/pluginmanager/test/config-ng2.yaml @@ -0,0 +1,28 @@ +cli: + discoverySources: + - oci: + name: default + image: example.com/plugin-inventory:latest + eulaStatus: accepted + ceipOptIn: "false" +currentContext: + kubernetes: mgmt + mission-control: tmc-fake +contexts: + - clusterOpts: + context: mgmt-admin@mgmt + path: config + isManagementCluster: true + name: mgmt + target: kubernetes + discoverySources: + - local: + name: fake-mgmt + path: context-mgmt + - globalOpts: + name: tmc-fake + target: mission-control + discoverySources: + - local: + name: fake-tmc + path: context-tmc diff --git a/pkg/pluginmanager/test/config.yaml b/pkg/pluginmanager/test/config.yaml index c2ecbd624..37b007731 100644 --- a/pkg/pluginmanager/test/config.yaml +++ b/pkg/pluginmanager/test/config.yaml @@ -1,36 +1,26 @@ -apiVersion: config.tanzu.vmware.com/v1alpha1 -current: mgmt -currentContext: - kubernetes: mgmt - mission-control: tmc-fake -kind: ClientConfig -metadata: - creationTimestamp: null +clientOptions: + features: + global: + context-target-v2: "true" + cli: + edition: tkg + bomRepo: projects.registry.vmware.com/tkg + compatibilityFilePath: tkg-compatibility servers: - managementClusterOpts: context: mgmt-admin@mgmt path: config name: mgmt - type: global + type: managementcluster discoverySources: - local: name: fake-mgmt path: context-mgmt -contexts: - - clusterOpts: - context: mgmt-admin@mgmt - path: config - isManagementCluster: true - name: mgmt - target: kubernetes - discoverySources: - - local: - name: fake-mgmt - path: context-mgmt - - globalOpts: - name: tmc-fake - target: mission-control + - name: tmc-fake + type: global + globalOpts: discoverySources: - local: name: fake-tmc path: context-tmc +current: mgmt