Skip to content

Commit

Permalink
Provide a smarter "plugin install/upgrade"
Browse files Browse the repository at this point in the history
This new version of "plugin install" only obtains the required amount
of information it needs to install a plugin.  This avoids getting the
entire list of plugins, which, with a Central Repository, can be large.

A similar change was made for "plugin upgrade".

Signed-off-by: Marc Khouzam <kmarc@vmware.com>
  • Loading branch information
marckhouzam committed Feb 10, 2023
1 parent 76e677c commit cf7c395
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 19 deletions.
22 changes: 22 additions & 0 deletions pkg/command/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,19 @@ func newInstallPluginCmd() *cobra.Command {
return nil
}

if config.IsFeatureActivated(constants.FeatureCentralRepository) {
if pluginName == cli.AllPlugins {
return fmt.Errorf("the '%s' argument is not longer supported. You must install each plugin individually",
cli.AllPlugins)
}
version, err = pluginmanager.InstallPluginV2(pluginName, version, getTarget())
if err != nil {
return err
}
log.Successf("successfully installed '%s' plugin", pluginName)
return nil
}

// Invoke plugin sync if install all plugins is mentioned
if pluginName == cli.AllPlugins {
err = pluginmanager.SyncPlugins()
Expand Down Expand Up @@ -259,6 +272,15 @@ func newUpgradePluginCmd() *cobra.Command {
return errors.New("invalid target specified. Please specify correct value of `--target` or `-t` flag from 'kubernetes/k8s/mission-control/tmc'")
}

if config.IsFeatureActivated(constants.FeatureCentralRepository) {
pluginVersion, err := pluginmanager.UpgradePluginV2(pluginName, version, getTarget())
if err != nil {
return err
}
log.Successf("successfully upgraded plugin '%s' to version '%s'", pluginName, pluginVersion)
return nil
}

pluginVersion, err := pluginmanager.GetRecommendedVersionOfPlugin(pluginName, getTarget())
if err != nil {
return err
Expand Down
24 changes: 24 additions & 0 deletions pkg/plugininventory/sqlite_inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,30 @@ func (b *SQLiteInventory) GetAllPlugins() ([]*PluginInventoryEntry, error) {

// GetPlugins returns the plugin found in the inventory that matches the provided parameters.
func (b *SQLiteInventory) GetPlugins(filter *PluginInventoryFilter) ([]*PluginInventoryEntry, error) {
// TODO(khouzam): Since the Central Repo does not have its RecommendedVersion field set yet,
// we first search for it by looking for the latest version amongst all versions.
if filter.Version == cli.VersionLatest {
if filter.Name == "" {
return nil, fmt.Errorf("cannot get the recommended version of a plugin without a plugin name")
}
// Ask for all versions
filter.Version = ""
plugins, err := b.getPluginsFromDB(filter)
if err != nil {
return nil, err
}
// We could end up with two plugins if we didn't filter on target.
// We know this will cause an error as it trickles back up so we just return what
// we found without further processing. This is NOT generic, but a temporary workaround.
// Also, if we have no plugins found, we can return immediately.
if len(plugins) != 1 {
return plugins, nil
}

// We can now use the RecommendedVersion field which was filled when parsing the DB.
filter.Version = plugins[0].RecommendedVersion
}

return b.getPluginsFromDB(filter)
}

Expand Down
111 changes: 94 additions & 17 deletions pkg/pluginmanager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -482,20 +482,7 @@ func InitializePlugin(plugin *cli.PluginInfo) error {

// InstallPlugin installs a plugin by name, version and target.
func InstallPlugin(pluginName, version string, target configtypes.Target) error {
var availablePlugins []discovery.Discovered
var err error
if configlib.IsFeatureActivated(constants.FeatureCentralRepository) {
availablePlugins, err = DiscoverStandalonePlugins()
if err != nil {
return err
}
if installedPlugins, err := pluginsupplier.GetInstalledPlugins(); err == nil {
setAvailablePluginsStatus(availablePlugins, installedPlugins)
}
} else {
availablePlugins, err = AvailablePlugins()
}

availablePlugins, err := AvailablePlugins()
if err != nil {
return err
}
Expand Down Expand Up @@ -585,7 +572,7 @@ func installOrUpgradePlugin(p *discovery.Discovered, version string, installTest

func fetchAndVerifyPlugin(p *discovery.Discovered, version string) ([]byte, error) {
// verify plugin before download
err := verifyPluginPreDownload(p)
err := verifyPluginPreDownload(p, version)
if err != nil {
return nil, errors.Wrapf(err, "%q plugin pre-download verification failed", p.Name)
}
Expand Down Expand Up @@ -1016,8 +1003,8 @@ func getPluginInfoResource(pluginFilePath string) (*cli.PluginInfo, error) {

// verifyPluginPreDownload verifies that the plugin distribution repo is trusted
// and returns error if the verification fails.
func verifyPluginPreDownload(p *discovery.Discovered) error {
artifactInfo, err := p.Distribution.DescribeArtifact(p.RecommendedVersion, runtime.GOOS, runtime.GOARCH)
func verifyPluginPreDownload(p *discovery.Discovered, version string) error {
artifactInfo, err := p.Distribution.DescribeArtifact(version, runtime.GOOS, runtime.GOARCH)
if err != nil {
return err
}
Expand Down Expand Up @@ -1092,3 +1079,93 @@ func FindVersion(recommendedPluginVersion, requestedVersion string) string {
}
return requestedVersion
}

/////////////////////////////////////////////////////////////////////////////////////
// Below are new versions of some functions targeting the Central Repository feature.
/////////////////////////////////////////////////////////////////////////////////////

// InstallPluginV2 installs a plugin by name, version and target.
// It returns the version of the plugin what was installed.
func InstallPluginV2(pluginName, version string, target configtypes.Target) (string, error) {
matchedPlugins, err := discoverSpecificPlugins(&discovery.PluginDiscoveryCriteria{
Name: pluginName,
Target: target,
Version: version,
OS: runtime.GOOS,
Arch: runtime.GOARCH,
})
if err != nil {
return "", err
}

if len(matchedPlugins) == 0 {
return "", errors.Errorf("unable to find matching plugin '%v', version '%s'", pluginName, version)
}

// If the version requested was the RecommendedVersion, we should set it explicitly
if version == cli.VersionLatest {
version = matchedPlugins[0].RecommendedVersion
}

// Set the status of the matched plugins
if installedPlugins, err := pluginsupplier.GetInstalledPlugins(); err == nil {
setAvailablePluginsStatus(matchedPlugins, installedPlugins)
}

if len(matchedPlugins) == 1 {
return version, installOrUpgradePlugin(&matchedPlugins[0], version, false)
}

for i := range matchedPlugins {
if matchedPlugins[i].Target == target {
return version, installOrUpgradePlugin(&matchedPlugins[i], version, false)
}
}

return "", errors.Errorf("unable to uniquely identify plugin '%v'. Please specify correct Target(kubernetes[k8s]/mission-control[tmc]) of the plugin with `--target` flag", pluginName)
}

// UpgradePluginV2 upgrades a plugin to the recommended version.
// it returns the version to which the plugin was upgraded.
func UpgradePluginV2(pluginName, version string, target configtypes.Target) (string, error) {
return InstallPluginV2(pluginName, version, target)
}

// getPluginDiscoveries returns the plugin discoveries found in the configuration file.
func getPluginDiscoveries() ([]configtypes.PluginDiscovery, error) {
cfg, err := configlib.GetClientConfig()
if err != nil {
return []configtypes.PluginDiscovery{}, errors.Wrapf(err, "unable to get client configuration")
}

if cfg == nil || cfg.ClientOptions == nil || cfg.ClientOptions.CLI == nil {
return []configtypes.PluginDiscovery{}, nil
}
return cfg.ClientOptions.CLI.DiscoverySources, nil
}

// discoverSpecificPlugins returns the available plugin matching the parameters.
func discoverSpecificPlugins(pluginCriteria *discovery.PluginDiscoveryCriteria) ([]discovery.Discovered, error) {
discoveries, err := getPluginDiscoveries()
if err != nil || len(discoveries) == 0 {
return nil, err
}

allPlugins := make([]discovery.Discovered, 0)
for _, d := range discoveries {
if d.OCI == nil {
log.Warning("invalid discovery type. Only OCI is supported")
continue
}

discObject := discovery.NewOCIDiscovery(d.OCI.Name, d.OCI.Image, pluginCriteria)
plugins, err := discObject.List()
if err != nil {
log.Warningf("unable to list plugin from discovery '%v': %v", discObject.Name(), err.Error())
continue
}

allPlugins = append(allPlugins, plugins...)
}
return allPlugins, nil
}
2 changes: 1 addition & 1 deletion pkg/pluginmanager/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func Test_InstallPlugin_InstalledPlugins(t *testing.T) {
// Try installing cluster plugin through context-type=k8s with incorrect version
err = InstallPlugin("cluster", "v1.0.0", configtypes.TargetK8s)
assertions.NotNil(err)
assertions.Contains(err.Error(), "unable to fetch the plugin metadata")
assertions.Contains(err.Error(), "plugin pre-download verification failed")

// Try installing cluster plugin through context-type=k8s
err = InstallPlugin("cluster", "v1.6.0", configtypes.TargetK8s)
Expand Down
2 changes: 1 addition & 1 deletion pkg/utils/semver.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/Masterminds/semver"
)

// SortVersions sorts the supported version strings in semver 2.0 order.
// SortVersions sorts the supported version strings in ascending semver 2.0 order.
func SortVersions(vStrArr []string) error {
vArr := make([]*semver.Version, len(vStrArr))
for i, vStr := range vStrArr {
Expand Down

0 comments on commit cf7c395

Please sign in to comment.