From a0cdf44558e99c4027eb008adeaea1ec17582559 Mon Sep 17 00:00:00 2001 From: Priyanshi Khetwani Date: Fri, 4 Oct 2024 01:02:13 +0530 Subject: [PATCH] added spinner for long running tasks added spinner for long running tasks add spinner for long running tasks --- cmd/image/import/import.go | 24 ++++++++++++------------ go.mod | 6 +++++- go.sum | 10 ++++++++-- pkg/utils/utils.go | 35 +++++++++++++++++++++++++++++------ 4 files changed, 54 insertions(+), 21 deletions(-) diff --git a/cmd/image/import/import.go b/cmd/image/import/import.go index e70cfc91..5cb5bdd1 100644 --- a/cmd/image/import/import.go +++ b/cmd/image/import/import.go @@ -262,20 +262,20 @@ pvsadm image import -n upstream-core-lon04 -b --object rhel-83-1003 return err } start := time.Now() - err = utils.PollUntil(time.Tick(2*time.Minute), time.After(opt.WatchTimeout), func() (bool, error) { + var message string + err = utils.PollUntil(time.Tick(2*time.Minute), time.After(opt.WatchTimeout), func() (string, bool, error) { job, err := pvmclient.JobClient.Get(*jobRef.ID) if err != nil { - return false, fmt.Errorf("image import job failed to complete, err: %v", err) + return "", false, fmt.Errorf("image import job failed to complete, err: %v", err) } if *job.Status.State == jobStateCompleted { - klog.V(2).Infof("Image uploaded successfully, took %s", time.Since(start).Round(time.Second)) - return true, nil + return "", true, nil } if *job.Status.State == jobStateFailed { - return false, fmt.Errorf("image import job failed to complete, err: %v", job.Status.Message) + return "", false, fmt.Errorf("image import job failed to complete, err: %v", job.Status.Message) } - klog.Infof("Image import is in-progress, current state: %s", *job.Status.State) - return false, nil + message = fmt.Sprintf("Image import is in-progress, current state: %s", *job.Status.State) + return message, false, nil }) if err != nil { return err @@ -296,17 +296,17 @@ pvsadm image import -n upstream-core-lon04 -b --object rhel-83-1003 return nil } klog.Infof("Waiting for image %s to be active. Please wait...", opt.ImageName) - return utils.PollUntil(time.Tick(10*time.Second), time.After(opt.WatchTimeout), func() (bool, error) { + return utils.PollUntil(time.Tick(10*time.Second), time.After(opt.WatchTimeout), func() (string, bool, error) { img, err := pvmclient.ImgClient.Get(*image.ImageID) if err != nil { - return false, fmt.Errorf("failed to import the image, err: %v\n\nRun the command \"pvsadm get events -i %s\" to get more information about the failure", err, pvmclient.InstanceID) + return "", false, fmt.Errorf("failed to import the image, err: %v\n\nRun the command \"pvsadm get events -i %s\" to get more information about the failure", err, pvmclient.InstanceID) } if img.State == imageStateActive { klog.Infof("Successfully imported the image: %s with ID: %s Total time taken: %s", *image.Name, *image.ImageID, time.Since(start).Round(time.Second)) - return true, nil + return "", true, nil } - klog.Infof("Waiting for image to be active. Current state: %s", img.State) - return false, nil + message = fmt.Sprintf("Waiting for image to be active. Current state: %s", img.State) + return message, false, nil }) }, } diff --git a/go.mod b/go.mod index 78caa9ba..9428a9e7 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/IBM/go-sdk-core/v5 v5.17.5 github.com/IBM/ibm-cos-sdk-go v1.11.1 github.com/IBM/platform-services-go-sdk v0.69.2 + github.com/briandowns/spinner v1.23.1 github.com/charmbracelet/huh v0.6.0 github.com/fsnotify/fsnotify v1.7.0 github.com/go-openapi/strfmt v0.23.0 @@ -32,7 +33,7 @@ require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/catppuccin/go v0.2.0 // indirect github.com/charmbracelet/bubbles v0.20.0 // indirect - github.com/charmbracelet/bubbletea v1.1.0 // indirect + github.com/charmbracelet/bubbletea v1.1.1 // indirect github.com/charmbracelet/lipgloss v0.13.0 // indirect github.com/charmbracelet/x/ansi v0.2.3 // indirect github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect @@ -41,6 +42,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/fatih/color v1.16.0 // indirect github.com/gabriel-vasile/mimetype v1.4.5 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -67,6 +69,7 @@ require ( github.com/leodido/go-urn v1.4.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect @@ -88,6 +91,7 @@ require ( golang.org/x/net v0.28.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.25.0 // indirect + golang.org/x/term v0.23.0 // indirect golang.org/x/text v0.18.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index bb2ec011..7aaa696b 100644 --- a/go.sum +++ b/go.sum @@ -26,13 +26,15 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/briandowns/spinner v1.23.1 h1:t5fDPmScwUjozhDj4FA46p5acZWIPXYE30qW2Ptu650= +github.com/briandowns/spinner v1.23.1/go.mod h1:LaZeM4wm2Ywy6vO571mvhQNRcWfRUnXOs0RcKV0wYKM= github.com/catppuccin/go v0.2.0 h1:ktBeIrIP42b/8FGiScP9sgrWOss3lw0Z5SktRoithGA= github.com/catppuccin/go v0.2.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= -github.com/charmbracelet/bubbletea v1.1.0 h1:FjAl9eAL3HBCHenhz/ZPjkKdScmaS5SK69JAK2YJK9c= -github.com/charmbracelet/bubbletea v1.1.0/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4= +github.com/charmbracelet/bubbletea v1.1.1 h1:KJ2/DnmpfqFtDNVTvYZ6zpPFL9iRCRr0qqKOCvppbPY= +github.com/charmbracelet/bubbletea v1.1.1/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4= github.com/charmbracelet/huh v0.6.0 h1:mZM8VvZGuE0hoDXq6XLxRtgfWyTI3b2jZNKh0xWmax8= github.com/charmbracelet/huh v0.6.0/go.mod h1:GGNKeWCeNzKpEOh/OJD8WBwTQjV3prFAtQPpLv+AVwU= github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw= @@ -239,6 +241,7 @@ github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= @@ -424,9 +427,12 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 94c28ecc..9cb1ab1d 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -18,6 +18,8 @@ import ( "fmt" "strconv" "time" + + "github.com/briandowns/spinner" ) func FormatProcessor(proc *float64) string { @@ -56,17 +58,38 @@ func RetrieveValFromMap[K comparable, V any](m map[K]V, key K) V { // PollUntil validates if a certain condition is met at defined poll intervals. // If a timeout is reached, an associated error is returned to the caller. // condition contains the use-case specific code that returns true when a certain condition is achieved. -func PollUntil(pollInterval, timeOut <-chan time.Time, condition func() (bool, error)) error { +func PollUntil(pollInterval, timeOut <-chan time.Time, condition func() (string, bool, error)) error { + s := spinner.New(spinner.CharSets[11], 100*time.Millisecond) + s.Color("cyan") + startTime := time.Now() + + var message string + var done bool + var err error + secondTicker := time.NewTicker(1 * time.Second) + defer secondTicker.Stop() + + _, done, err = condition() + if err != nil || done { + return fmt.Errorf("initial condition check failed: %w", err) + } + s.Start() for { select { - case <-timeOut: - return fmt.Errorf("timed out while waiting for job to complete") + case <-secondTicker.C: + s.Suffix = fmt.Sprintf(" %s (Time elapsed: %s)", message, time.Since(startTime).Round(time.Second)) case <-pollInterval: - if done, err := condition(); err != nil { + message, done, err = condition() + if err != nil || done { + s.Stop() return err - } else if done { - return nil } + case <-timeOut: + s.Stop() + timeoutMessage := " timed out while waiting for job to complete" + s.Suffix = timeoutMessage + return fmt.Errorf("timed out while waiting for job to complete") + } } }