Skip to content

Commit 48c0be6

Browse files
committed
Enhance plugin installation UX
PR updates the plugin installation UX, - As part of plugin installation in-progress message, it adds total plugins needs to installed and the number of plugins being installed The in-progress message will disappear once the installation completes. Next plugin in-progress will be shown. - No log messages will be showed once the plugin installation completes. - Signal handling logic has moved to util package
1 parent 90ca3fe commit 48c0be6

File tree

6 files changed

+97
-52
lines changed

6 files changed

+97
-52
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ require (
4242
github.com/vmware-tanzu/carvel-ytt v0.40.0
4343
github.com/vmware-tanzu/tanzu-cli/test/e2e/framework v0.0.0-00010101000000-000000000000
4444
github.com/vmware-tanzu/tanzu-framework/capabilities/client v0.0.0-20230523145612-1c6fbba34686
45-
github.com/vmware-tanzu/tanzu-plugin-runtime v1.3.0-alpha.2
45+
github.com/vmware-tanzu/tanzu-plugin-runtime v1.3.0-dev.0.20240410164649-72cfcba566be
4646
go.pinniped.dev v0.20.0
4747
golang.org/x/mod v0.12.0
4848
golang.org/x/oauth2 v0.8.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -738,8 +738,8 @@ github.com/vmware-tanzu/tanzu-framework/apis/run v0.0.0-20230419030809-7081502eb
738738
github.com/vmware-tanzu/tanzu-framework/apis/run v0.0.0-20230419030809-7081502ebf68/go.mod h1:e1Uef+Ux5BIHpYwqbeP2ZZmOzehBcez2vUEWXHe+xHE=
739739
github.com/vmware-tanzu/tanzu-framework/capabilities/client v0.0.0-20230523145612-1c6fbba34686 h1:VcuXqUXFxm5WDqWkzAlU/6cJXua0ozELnqD59fy7J6E=
740740
github.com/vmware-tanzu/tanzu-framework/capabilities/client v0.0.0-20230523145612-1c6fbba34686/go.mod h1:AFGOXZD4tH+KhpmtV0VjWjllXhr8y57MvOsIxTtywc4=
741-
github.com/vmware-tanzu/tanzu-plugin-runtime v1.3.0-alpha.2 h1:eZLXCLpw1N0XYW4fnVcE9gC5R6o8IrYrHEPqt7uB1Gs=
742-
github.com/vmware-tanzu/tanzu-plugin-runtime v1.3.0-alpha.2/go.mod h1:5m73y796B4EoeXZtvkq8jbQPPQXeYkLPLS2BBbxZp7o=
741+
github.com/vmware-tanzu/tanzu-plugin-runtime v1.3.0-dev.0.20240410164649-72cfcba566be h1:/rYyMeziawcrVaaq9W7Vogc9MdHsQRn0CcEqa8GwrOA=
742+
github.com/vmware-tanzu/tanzu-plugin-runtime v1.3.0-dev.0.20240410164649-72cfcba566be/go.mod h1:5m73y796B4EoeXZtvkq8jbQPPQXeYkLPLS2BBbxZp7o=
743743
github.com/xanzy/go-gitlab v0.83.0 h1:37p0MpTPNbsTMKX/JnmJtY8Ch1sFiJzVF342+RvZEGw=
744744
github.com/xanzy/go-gitlab v0.83.0/go.mod h1:5ryv+MnpZStBH8I/77HuQBsMbBGANtVpLWC15qOjWAw=
745745
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=

go.work.sum

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -727,8 +727,6 @@ github.com/veraison/go-cose v1.1.0 h1:AalPS4VGiKavpAzIlBjrn7bhqXiXi4jbMYY/2+UC+4
727727
github.com/veraison/go-cose v1.1.0/go.mod h1:7ziE85vSq4ScFTg6wyoMXjucIGOf4JkFEZi/an96Ct4=
728728
github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI=
729729
github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U=
730-
github.com/vmware-tanzu/tanzu-framework/capabilities/client v0.0.0-20230523145612-1c6fbba34686 h1:VcuXqUXFxm5WDqWkzAlU/6cJXua0ozELnqD59fy7J6E=
731-
github.com/vmware-tanzu/tanzu-framework/capabilities/client v0.0.0-20230523145612-1c6fbba34686/go.mod h1:AFGOXZD4tH+KhpmtV0VjWjllXhr8y57MvOsIxTtywc4=
732730
github.com/vmware-tanzu/tanzu-framework/capabilities/client v0.0.0-20230707192002-93a508a39d94 h1:Bql3MJ4/eXU/pDLAA/9UBsMY7Ou8WQnkOCy8DRzSujI=
733731
github.com/vmware-tanzu/tanzu-framework/capabilities/client v0.0.0-20230707192002-93a508a39d94/go.mod h1:AFGOXZD4tH+KhpmtV0VjWjllXhr8y57MvOsIxTtywc4=
734732
github.com/vmware-tanzu/tanzu-plugin-runtime v1.1.0-dev.0.20231011182819-6cabd029e112 h1:zTrAoW2vn6ap0WHJ5Jo/Vq9v5WYrVT455MtopOwzYNA=
@@ -741,6 +739,10 @@ github.com/vmware-tanzu/tanzu-plugin-runtime v1.2.0-dev.0.20240122211944-583955a
741739
github.com/vmware-tanzu/tanzu-plugin-runtime v1.2.0-dev.0.20240122211944-583955a05da9/go.mod h1:M7WVZoItdyQp53tEprQIa6PZmhbrLe3CzuyQphWuRyI=
742740
github.com/vmware-tanzu/tanzu-plugin-runtime v1.2.0-dev.0.20240122215628-021d60b78abe h1:jlR24NEVKyBiTGlEtqWBSGULBqZe0LJd40VzybAStY0=
743741
github.com/vmware-tanzu/tanzu-plugin-runtime v1.2.0-dev.0.20240122215628-021d60b78abe/go.mod h1:M7WVZoItdyQp53tEprQIa6PZmhbrLe3CzuyQphWuRyI=
742+
github.com/vmware-tanzu/tanzu-plugin-runtime v1.3.0-dev.0.20240321213409-329469f6da53 h1:BZZYyKImli+jismBvNY8NA7oFgwG4Na3MXGq7dsO62A=
743+
github.com/vmware-tanzu/tanzu-plugin-runtime v1.3.0-dev.0.20240321213409-329469f6da53/go.mod h1:5m73y796B4EoeXZtvkq8jbQPPQXeYkLPLS2BBbxZp7o=
744+
github.com/vmware-tanzu/tanzu-plugin-runtime v1.3.0-dev.0.20240410164649-72cfcba566be h1:/rYyMeziawcrVaaq9W7Vogc9MdHsQRn0CcEqa8GwrOA=
745+
github.com/vmware-tanzu/tanzu-plugin-runtime v1.3.0-dev.0.20240410164649-72cfcba566be/go.mod h1:5m73y796B4EoeXZtvkq8jbQPPQXeYkLPLS2BBbxZp7o=
744746
github.com/weppos/publicsuffix-go v0.20.1-0.20221031080346-e4081aa8a6de h1:eR9jm8DVMdrDUuVji4eOxPK4r/dANDlDBdISSUUV96s=
745747
github.com/weppos/publicsuffix-go v0.20.1-0.20221031080346-e4081aa8a6de/go.mod h1:g9GsAxnaxsUuTLZcQdYbi43vT2k9ubZGHsdCy819VLk=
746748
github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1 h1:+dBg5k7nuTE38VVdoroRsT0Z88fmvdYrI2EjzJst35I=
@@ -860,7 +862,6 @@ go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee33
860862
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
861863
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
862864
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
863-
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
864865
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
865866
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
866867
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
@@ -1158,6 +1159,7 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
11581159
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
11591160
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
11601161
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
1162+
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
11611163
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
11621164
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
11631165
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
@@ -1183,7 +1185,6 @@ gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg
11831185
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
11841186
gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE=
11851187
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
1186-
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
11871188
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
11881189
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
11891190
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=

pkg/pluginmanager/essentials.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ func installPluginsFromEssentialPluginGroup(name, version string) (string, error
6969
if err != nil {
7070
return "", fmt.Errorf("failed to install plugins from group: %w", err)
7171
}
72+
log.Successf("successfully installed all plugins from group '%s'", groupWithVersion)
7273

7374
// If the installation is successful, return the group with version.
7475
return groupWithVersion, nil

pkg/pluginmanager/manager.go

Lines changed: 59 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
"os/signal"
1414
"path/filepath"
1515
"strings"
16-
"syscall"
1716

1817
"github.com/Masterminds/semver"
1918
"github.com/pkg/errors"
@@ -58,6 +57,8 @@ const (
5857
errorNoActiveContexForGivenContextType = "there is no active context for the given context type `%v`"
5958
)
6059

60+
var totalPluginsToInstall = 0
61+
var pluginsInstalled = 0
6162
var execCommand = exec.Command
6263

6364
type DeletePluginOptions struct {
@@ -609,24 +610,30 @@ func InstallPluginsFromGroup(pluginName, groupIDAndVersion string, options ...Pl
609610
// InstallPluginsFromGivenPluginGroup installs either the specified plugin or all plugins from given plugin group plugins.
610611
func InstallPluginsFromGivenPluginGroup(pluginName, groupIDAndVersion string, pg *plugininventory.PluginGroup) (string, error) {
611612
numErrors := 0
612-
numInstalled := 0
613613
mandatoryPluginsExist := false
614614
pluginExist := false
615+
pluginsInstalled = 0
616+
617+
pluginsToInstall := make([]*plugininventory.PluginGroupPluginEntry, 0)
615618
for _, plugin := range pg.Versions[pg.RecommendedVersion] {
616619
if pluginName == cli.AllPlugins || pluginName == plugin.Name {
617620
pluginExist = true
618621
if plugin.Mandatory {
619622
mandatoryPluginsExist = true
620-
err := InstallStandalonePlugin(plugin.Name, plugin.Version, plugin.Target)
621-
if err != nil {
622-
numErrors++
623-
log.Warningf("unable to install plugin '%s': %v", plugin.Name, err.Error())
624-
} else {
625-
numInstalled++
626-
}
623+
pluginsToInstall = append(pluginsToInstall, plugin) // Add mandatory plugin to the slice
627624
}
628625
}
629626
}
627+
totalPluginsToInstall = len(pluginsToInstall)
628+
for _, plugin := range pluginsToInstall {
629+
err := InstallStandalonePlugin(plugin.Name, plugin.Version, plugin.Target)
630+
if err != nil {
631+
numErrors++
632+
log.Warningf("unable to install plugin '%s': %v", plugin.Name, err.Error())
633+
} else {
634+
pluginsInstalled++
635+
}
636+
}
630637

631638
if !pluginExist {
632639
return groupIDAndVersion, fmt.Errorf("plugin '%s' is not part of the group '%s'", pluginName, groupIDAndVersion)
@@ -643,7 +650,7 @@ func InstallPluginsFromGivenPluginGroup(pluginName, groupIDAndVersion string, pg
643650
return groupIDAndVersion, fmt.Errorf("could not install %d plugin(s) from group '%s'", numErrors, groupIDAndVersion)
644651
}
645652

646-
if numInstalled == 0 {
653+
if pluginsInstalled == 0 {
647654
return groupIDAndVersion, fmt.Errorf("plugin '%s' is not part of the group '%s'", pluginName, groupIDAndVersion)
648655
}
649656

@@ -744,40 +751,31 @@ func installOrUpgradePlugin(p *discovery.Discovered, version string, installTest
744751
}
745752

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

756+
installingMsg = fmt.Sprintf("[%v/%v] %v", pluginsInstalled, totalPluginsToInstall, installingMsg)
757+
errMsg = fmt.Sprintf("[%v/%v] %v", pluginsInstalled, totalPluginsToInstall, errMsg)
758+
errorMsgAfterSpinnerStop := fmt.Sprintf("%d plugins installed out of %d", pluginsInstalled, totalPluginsToInstall)
749759
var spinner component.OutputWriterSpinner
750760

751761
// Initialize the spinner if the spinner is allowed
752762
if component.IsTTYEnabled() {
763+
// Initialize the spinner
764+
spinner = component.NewOutputWriterSpinner(component.WithOutputStream(os.Stderr),
765+
component.WithSpinnerText(installingMsg),
766+
component.WithSpinnerStarted())
767+
753768
// Create a channel to receive OS signals
754769
signalChannel := make(chan os.Signal, 1)
755-
// Register the channel to receive interrupt signals (e.g., Ctrl+C)
756-
signal.Notify(signalChannel, syscall.SIGINT, syscall.SIGTERM)
770+
// Initialize the signal catcher
771+
go utils.SignalCatcherInitialization(signalChannel, spinner, errMsg, log.LogTypeERROR, errorMsgAfterSpinnerStop)
772+
757773
defer func() {
758774
signal.Stop(signalChannel)
759775
close(signalChannel)
776+
spinner.StopSpinner()
777+
component.StopAllSpinners()
760778
}()
761-
762-
// Initialize the spinner
763-
spinner = component.NewOutputWriterSpinner(component.WithOutputStream(os.Stderr),
764-
component.WithSpinnerText(installingMsg),
765-
component.WithSpinnerStarted(),
766-
component.WithSpinnerFinalText(installedMsg, log.LogTypeINFO))
767-
768-
defer spinner.StopSpinner()
769-
770-
// Start a goroutine that listens for interrupt signals
771-
go func(s component.OutputWriterSpinner) {
772-
sig := <-signalChannel
773-
if sig != nil {
774-
if s != nil {
775-
s.SetFinalText(errMsg, log.LogTypeERROR)
776-
s.StopSpinner()
777-
}
778-
os.Exit(128 + int(sig.(syscall.Signal)))
779-
}
780-
}(spinner)
781779
} else {
782780
log.Info(installingMsg)
783781
}
@@ -1154,29 +1152,45 @@ func UpdatePluginsInstallationStatus(plugins []discovery.Discovered) {
11541152
// InstallDiscoveredContextPlugins installs the given context scope plugins
11551153
func InstallDiscoveredContextPlugins(plugins []discovery.Discovered) error {
11561154
var errList []error
1157-
var err error
1158-
installed := false
1155+
1156+
// Slice to capture plugins to install
1157+
var pluginsToInstall []discovery.Discovered
1158+
11591159
UpdatePluginsInstallationStatus(plugins)
1160+
1161+
// Capture plugins that need install/update
11601162
for idx := range plugins {
1161-
if plugins[idx].Status == common.PluginStatusNotInstalled || plugins[idx].Status == common.PluginStatusUpdateAvailable {
1162-
installed = true
1163-
p := plugins[idx]
1164-
err = InstallPluginFromContext(p.Name, p.RecommendedVersion, p.Target, p.ContextName)
1165-
if err != nil {
1166-
errList = append(errList, err)
1167-
}
1163+
if plugins[idx].Status == common.PluginStatusNotInstalled ||
1164+
plugins[idx].Status == common.PluginStatusUpdateAvailable {
1165+
pluginsToInstall = append(pluginsToInstall, plugins[idx])
11681166
}
11691167
}
1170-
err = kerrors.NewAggregate(errList)
1168+
totalPluginsToInstall = len(pluginsToInstall)
1169+
// Now install captured plugins
1170+
for idx := range pluginsToInstall {
1171+
err := InstallPluginFromContext(pluginsToInstall[idx].Name, pluginsToInstall[idx].RecommendedVersion, pluginsToInstall[idx].Target, pluginsToInstall[idx].ContextName)
1172+
if err != nil {
1173+
errList = append(errList, err)
1174+
} else {
1175+
pluginsInstalled++
1176+
}
1177+
}
1178+
1179+
// Aggregate errors
1180+
err := kerrors.NewAggregate(errList)
11711181
if err != nil {
11721182
return err
11731183
}
11741184

1175-
if !installed {
1185+
// Output install status
1186+
if len(pluginsToInstall) == 0 {
11761187
log.Info("All required plugins are already installed and up-to-date")
1188+
} else if pluginsInstalled == totalPluginsToInstall {
1189+
log.Infof("Successfully installed all required %v plugins", pluginsInstalled)
11771190
} else {
1178-
log.Info("Successfully installed all required plugins")
1191+
log.Infof("Successfully installed %v of %v required plugins", pluginsInstalled, totalPluginsToInstall)
11791192
}
1193+
11801194
return nil
11811195
}
11821196

pkg/utils/signal_handler.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2022 VMware, Inc. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package utils
5+
6+
import (
7+
"os"
8+
"os/signal"
9+
"syscall"
10+
11+
"github.com/vmware-tanzu/tanzu-plugin-runtime/component"
12+
"github.com/vmware-tanzu/tanzu-plugin-runtime/log"
13+
)
14+
15+
// SignalCatcherInitialization initializes a signal catcher to catch OS signals and stop the spinner
16+
func SignalCatcherInitialization(signalChannel chan os.Signal, s component.OutputWriterSpinner, spinnerFinalText string, spinnerFinalTextLogType log.LogType, errorMsgAfterSpinnerStop string) {
17+
// Register the channel to receive interrupt signals (e.g., Ctrl+C)
18+
signal.Notify(signalChannel, syscall.SIGINT, syscall.SIGTERM)
19+
20+
sig := <-signalChannel
21+
if sig != nil {
22+
if s != nil {
23+
s.SetFinalText(spinnerFinalText, spinnerFinalTextLogType)
24+
s.StopSpinner()
25+
}
26+
log.Errorf(errorMsgAfterSpinnerStop)
27+
os.Exit(128 + int(sig.(syscall.Signal)))
28+
}
29+
}

0 commit comments

Comments
 (0)