Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugin installation UX update #703

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion pkg/command/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,18 +323,21 @@ func syncContextPlugins(cmd *cobra.Command, contextType configtypes.ContextType,

errList := make([]error, 0)
log.Infof("Installing the following plugins recommended by context '%s':", ctxName)
pluginmanager.SetTotalPluginsToInstall(len(pluginsNeedToBeInstalled))
defer pluginmanager.ResetPluginInstallationCounts()
displayToBeInstalledPluginsAsTable(plugins, cmd.ErrOrStderr())
for i := range pluginsNeedToBeInstalled {
err = pluginmanager.InstallStandalonePlugin(pluginsNeedToBeInstalled[i].Name, pluginsNeedToBeInstalled[i].RecommendedVersion, pluginsNeedToBeInstalled[i].Target)
if err != nil {
errList = append(errList, err)
} else {
pluginmanager.IncrementPluginsInstalledCount(1)
}
}
err = kerrors.NewAggregate(errList)
if err == nil {
log.Success("Successfully installed all recommended plugins.")
}

return err
}

Expand Down
2 changes: 2 additions & 0 deletions pkg/command/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ func newInstallPluginCmd() *cobra.Command {
return installPluginsForPluginGroup(cmd, args)
}

pluginmanager.SetTotalPluginsToInstall(1)
// Invoke install plugin from local source if local files are provided
if local != "" {
if len(args) == 0 {
Expand Down Expand Up @@ -340,6 +341,7 @@ func newUpgradePluginCmd() *cobra.Command {

// With the Central Repository feature we can simply request to install
// the recommendedVersion.
pluginmanager.SetTotalPluginsToInstall(1)
err = pluginmanager.UpgradePlugin(pluginName, cli.VersionLatest, getTarget())
if err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions pkg/pluginmanager/essentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func installPluginsFromEssentialPluginGroup(name, version string) (string, error
if err != nil {
return "", fmt.Errorf("failed to install plugins from group: %w", err)
}
log.Successf("successfully installed all plugins from group '%s'", groupWithVersion)

// If the installation is successful, return the group with version.
return groupWithVersion, nil
Expand Down
116 changes: 89 additions & 27 deletions pkg/pluginmanager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"

"github.com/Masterminds/semver"
Expand Down Expand Up @@ -56,7 +57,32 @@ const (
errorNoActiveContexForGivenContextType = "there is no active context for the given context type `%v`"
)

var totalPluginsToInstall = 0
var pluginNumberBeingInstalled = 1
var execCommand = exec.Command
var spinner component.OutputWriterSpinner

func init() {
// Initialize global spinner
spinner = component.NewOutputWriterSpinner(component.WithOutputStream(os.Stderr))
runtime.SetFinalizer(spinner, func(s component.OutputWriterSpinner) {
if s != nil {
s.StopSpinner()
}
})
}

func StopSpinner() {
if spinner != nil {
spinner.StopSpinner()
}
}

// ResetPluginInstallationCounts resets the total number of plugins to install and the number of plugins installed
func ResetPluginInstallationCounts() {
SetTotalPluginsToInstall(0)
SetPluginBeingInstalledCount(1)
}

type DeletePluginOptions struct {
Target configtypes.Target
Expand Down Expand Up @@ -472,9 +498,30 @@ func InitializePlugin(plugin *cli.PluginInfo) error {

// InstallStandalonePlugin installs a plugin by name, version and target as a standalone plugin.
func InstallStandalonePlugin(pluginName, version string, target configtypes.Target) error {
defer StopSpinner()
return installPlugin(pluginName, version, target, "")
}

// SetTotalPluginsToInstall sets the total number of plugins to install
func SetTotalPluginsToInstall(total int) {
totalPluginsToInstall = total
}

// SetPluginBeingInstalledCount sets the number of plugins installed already
func SetPluginBeingInstalledCount(count int) {
pluginNumberBeingInstalled = count
}

// GetPluginBeingInstalledCount returns the number of plugin being installed
func GetPluginBeingInstalledCount() int {
return pluginNumberBeingInstalled
}

// IncrementPluginsInstalledCount increments the number of plugins installed
func IncrementPluginsInstalledCount(count int) {
pluginNumberBeingInstalled += count
}

// installs a plugin by name, version and target.
// If the contextName is not empty, it implies the plugin is a context-scope plugin, otherwise
// we are installing a standalone plugin.
Expand Down Expand Up @@ -560,7 +607,7 @@ func installPlugin(pluginName, version string, target configtypes.Target, contex
if len(matchedPlugins) == 1 {
return installOrUpgradePlugin(&matchedPlugins[0], matchedPlugins[0].RecommendedVersion, false)
}

// there can be only one plugin with the same name and target, so we can safely return the first one
for i := range matchedPlugins {
if matchedPlugins[i].Target == target {
return installOrUpgradePlugin(&matchedPlugins[i], matchedPlugins[i].RecommendedVersion, false)
Expand Down Expand Up @@ -592,31 +639,37 @@ func InstallPluginsFromGroup(pluginName, groupIDAndVersion string, options ...Pl
// from the database
groupIDAndVersion = fmt.Sprintf("%s-%s/%s:%s", pg.Vendor, pg.Publisher, pg.Name, pg.RecommendedVersion)
log.Infof("Installing plugins from plugin group '%s'", groupIDAndVersion)

return InstallPluginsFromGivenPluginGroup(pluginName, groupIDAndVersion, pg)
}

// InstallPluginsFromGivenPluginGroup installs either the specified plugin or all plugins from given plugin group plugins.
func InstallPluginsFromGivenPluginGroup(pluginName, groupIDAndVersion string, pg *plugininventory.PluginGroup) (string, error) {
numErrors := 0
numInstalled := 0
mandatoryPluginsExist := false
pluginExist := false

pluginsToInstall := make([]*plugininventory.PluginGroupPluginEntry, 0)
for _, plugin := range pg.Versions[pg.RecommendedVersion] {
if pluginName == cli.AllPlugins || pluginName == plugin.Name {
pluginExist = true
if plugin.Mandatory {
mandatoryPluginsExist = true
err := InstallStandalonePlugin(plugin.Name, plugin.Version, plugin.Target)
if err != nil {
numErrors++
log.Warningf("unable to install plugin '%s': %v", plugin.Name, err.Error())
} else {
numInstalled++
}
pluginsToInstall = append(pluginsToInstall, plugin) // Add mandatory plugin to the slice
}
}
}
SetTotalPluginsToInstall(len(pluginsToInstall))
SetPluginBeingInstalledCount(1)
defer StopSpinner()
defer ResetPluginInstallationCounts()
for _, plugin := range pluginsToInstall {
err := InstallStandalonePlugin(plugin.Name, plugin.Version, plugin.Target)
if err != nil {
numErrors++
} else {
IncrementPluginsInstalledCount(1)
}
}

if !pluginExist {
return groupIDAndVersion, fmt.Errorf("plugin '%s' is not part of the group '%s'", pluginName, groupIDAndVersion)
Expand All @@ -633,7 +686,7 @@ func InstallPluginsFromGivenPluginGroup(pluginName, groupIDAndVersion string, pg
return groupIDAndVersion, fmt.Errorf("could not install %d plugin(s) from group '%s'", numErrors, groupIDAndVersion)
}

if numInstalled == 0 {
if GetPluginBeingInstalledCount() == 0 {
return groupIDAndVersion, fmt.Errorf("plugin '%s' is not part of the group '%s'", pluginName, groupIDAndVersion)
}

Expand Down Expand Up @@ -704,7 +757,7 @@ func getPluginInstallationMessage(p *discovery.Discovered, version string, isPlu
installingMsg = fmt.Sprintf("Installing plugin '%v:%v' %v(from cache)", p.Name, version, withTarget)
installedMsg = fmt.Sprintf("Installed plugin '%v:%v' %v(from cache)", p.Name, version, withTarget)
} else {
installingMsg = fmt.Sprintf("Plugin '%v:%v' %vis already installed. Reinitializing...", p.Name, version, withTarget)
installingMsg = fmt.Sprintf("Already installed : Plugin '%v:%v' %v", p.Name, version, withTarget)
installedMsg = fmt.Sprintf("Reinitialized plugin '%v:%v' %v", p.Name, version, withTarget)
}
} else {
Expand Down Expand Up @@ -734,25 +787,27 @@ func installOrUpgradePlugin(p *discovery.Discovered, version string, installTest
}

// Log message based on different installation conditions
installingMsg, installedMsg, errMsg := getPluginInstallationMessage(p, version, plugin != nil, isPluginAlreadyInstalled)
installingMsg, _, errMsg := getPluginInstallationMessage(p, version, plugin != nil, isPluginAlreadyInstalled)

var spinner component.OutputWriterSpinner
installingMsg = fmt.Sprintf("[%v/%v] %v", pluginNumberBeingInstalled, totalPluginsToInstall, installingMsg)
errMsg = fmt.Sprintf("[%v/%v] %v", pluginNumberBeingInstalled, totalPluginsToInstall, errMsg)
numPluginsInstalled := fmt.Sprintf("%d plugins installed out of %d", pluginNumberBeingInstalled, totalPluginsToInstall)
numPluginsInstalledWithLog := fmt.Sprintf("%s%s", log.GetLogTypeIndicator(log.LogTypeERROR), numPluginsInstalled)
errMsg = fmt.Sprintf("%s\n%s", errMsg, numPluginsInstalledWithLog)

// Initialize the spinner if the spinner is allowed
if component.IsTTYEnabled() {
// Initialize the spinner
spinner = component.NewOutputWriterSpinner(component.WithOutputStream(os.Stderr),
component.WithSpinnerText(installingMsg),
component.WithSpinnerStarted())
if component.IsTTYEnabled() && spinner != nil {
spinner.SetText(installingMsg)
spinner.SetFinalText(errMsg, log.LogTypeERROR)
defer spinner.StopSpinner()
spinner.StartSpinner()
} else {
log.Info(installingMsg)
}

pluginErr := verifyInstallAndInitializePlugin(plugin, p, version, installTestPlugin)
if pluginErr == nil && spinner != nil {
spinner.SetFinalText(installedMsg, log.LogTypeINFO)
// unset the final text to avoid the spinner from showing the final text
spinner.SetFinalText("", "")
}
return pluginErr
}
Expand Down Expand Up @@ -1104,7 +1159,6 @@ func InstallPluginsFromLocalSource(pluginName, version string, target configtype
if err != nil {
return errors.Wrap(err, "unable to discover plugins")
}

var errList []error

var matchedPlugins []discovery.Discovered
Expand All @@ -1128,20 +1182,28 @@ func InstallPluginsFromLocalSource(pluginName, version string, target configtype
return errors.Errorf("unable to find plugin '%v' matching version '%v'", pluginName, version)
}

defer StopSpinner()
if len(matchedPlugins) == 1 {
return installOrUpgradePlugin(&matchedPlugins[0], version, installTestPlugin)
chandrareddyp marked this conversation as resolved.
Show resolved Hide resolved
}

var pluginsToInstall []*discovery.Discovered
for i := range matchedPlugins {
// Install all plugins otherwise include all matching plugins
if pluginName == cli.AllPlugins || matchedPlugins[i].Target == target {
err = installOrUpgradePlugin(&matchedPlugins[i], version, installTestPlugin)
if err != nil {
errList = append(errList, err)
}
plugin := matchedPlugins[i]
pluginsToInstall = append(pluginsToInstall, &plugin)
}
}
SetTotalPluginsToInstall(len(pluginsToInstall))
SetPluginBeingInstalledCount(1)
defer ResetPluginInstallationCounts()
for _, plugin := range pluginsToInstall {
err = installOrUpgradePlugin(plugin, version, installTestPlugin)
if err != nil {
errList = append(errList, err)
}
}

err = kerrors.NewAggregate(errList)
if err != nil {
return err
Expand Down
Loading