Skip to content

Commit 3a4a982

Browse files
chore: adding unit tests for progressbar package (#57)
adds unit tests on progressbar package and simplifies its usage by providing a proper close and a new closef functions.
1 parent e2b68db commit 3a4a982

File tree

8 files changed

+174
-86
lines changed

8 files changed

+174
-86
lines changed

cmd/helmvm/bundle.go

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515
"github.com/replicatedhq/helmvm/pkg/defaults"
1616
"github.com/replicatedhq/helmvm/pkg/goods"
1717
"github.com/replicatedhq/helmvm/pkg/hembed"
18-
"github.com/replicatedhq/helmvm/pkg/progressbar"
18+
pb "github.com/replicatedhq/helmvm/pkg/progressbar"
1919
)
2020

2121
func pullImage(ctx context.Context, imgurl string) error {
@@ -53,21 +53,18 @@ var bundleCommand = &cli.Command{
5353
return fmt.Errorf("unable to open bundle file: %w", err)
5454
}
5555
defer dst.Close()
56-
writer, end := progressbar.Start()
57-
_, _ = writer.Write([]byte("Downloading base images bundle."))
56+
loading := pb.Start(nil)
57+
loading.Infof("Downloading base images bundle.")
5858
src, err := goods.DownloadImagesBundle(defaults.K0sVersion)
5959
if err != nil {
60-
writer.Close()
61-
<-end
60+
loading.Close()
6261
return fmt.Errorf("unable to download bundle: %w", err)
6362
}
6463
if _, err := io.Copy(dst, src); err != nil {
65-
writer.Close()
66-
<-end
64+
loading.Close()
6765
return fmt.Errorf("unable to copy bundle: %w", err)
6866
}
69-
writer.Close()
70-
<-end
67+
loading.Close()
7168
images, err := goods.ListImages()
7269
if err != nil {
7370
return fmt.Errorf("unable to list images: %w", err)
@@ -78,15 +75,13 @@ var bundleCommand = &cli.Command{
7875
}
7976
images = append(images, embed...)
8077
for _, img := range images {
81-
writer, end = progressbar.Start()
82-
_, _ = writer.Write([]byte(fmt.Sprintf("Pulling image %s", img)))
78+
loading = pb.Start(nil)
79+
loading.Infof(fmt.Sprintf("Pulling image %s", img))
8380
if err := pullImage(c.Context, img); err != nil {
84-
writer.Close()
85-
<-end
81+
loading.Close()
8682
return fmt.Errorf("unable to pull image %s: %w", img, err)
8783
}
88-
writer.Close()
89-
<-end
84+
loading.Close()
9085
}
9186
logrus.Info("Bundle stored under ./bundle directory.")
9287
return nil

cmd/helmvm/install.go

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,11 @@ import (
3333
// k0sctl. Iterates over all hosts and calls runPostApply on each.
3434
func runPostApply(ctx context.Context) error {
3535
logrus.Infof("Running post-apply script on nodes")
36-
logger, end := pb.Start()
36+
loading := pb.Start(nil)
3737
orig := log.Log
38-
rig.SetLogger(logger)
38+
rig.SetLogger(loading)
3939
defer func() {
40-
logger.Infof("Post apply process finished")
41-
close(logger)
42-
<-end
40+
loading.Closef("Post apply process finished")
4341
log.Log = orig
4442
}()
4543
cfg, err := config.ReadConfigFile(defaults.PathToConfig("k0sctl.yaml"))
@@ -256,13 +254,11 @@ func ensureK0sctlConfig(c *cli.Context, nodes []infra.Node, useprompt bool) erro
256254
// the configuration directory. Returns when the command is finished.
257255
func runK0sctlApply(ctx context.Context) error {
258256
bin := defaults.PathToHelmVMBinary("k0sctl")
259-
messages, pbwait := pb.Start()
257+
loading := pb.Start(nil)
260258
defer func() {
261-
messages.Close()
262-
<-pbwait
259+
loading.Close()
263260
}()
264261
cfgpath := defaults.PathToConfig("k0sctl.yaml")
265-
_, _ = messages.Write([]byte("Running k0sctl apply"))
266262
kctl := exec.Command(bin, "apply", "-c", cfgpath, "--disable-telemetry")
267263
kctl.Stderr = logrus.StandardLogger().Writer()
268264
kctl.Stdout = logrus.StandardLogger().Writer()

pkg/addons/applier.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121
"github.com/replicatedhq/helmvm/pkg/addons/adminconsole"
2222
"github.com/replicatedhq/helmvm/pkg/addons/custom"
2323
"github.com/replicatedhq/helmvm/pkg/addons/openebs"
24-
"github.com/replicatedhq/helmvm/pkg/progressbar"
24+
pb "github.com/replicatedhq/helmvm/pkg/progressbar"
2525
)
2626

2727
// getLogger creates a logger to be used in an addon.
@@ -147,11 +147,9 @@ func (a *Applier) kubeClient() (client.Client, error) {
147147
// waitForKubernetes waits until we manage to make a successful connection to the
148148
// Kubernetes API server.
149149
func (a *Applier) waitForKubernetes(ctx context.Context) error {
150-
pb, end := progressbar.Start()
150+
loading := pb.Start(nil)
151151
defer func() {
152-
pb.Infof("Kubernetes API server is ready.")
153-
pb.Close()
154-
<-end
152+
loading.Closef("Kubernetes API server is ready.")
155153
}()
156154
kcli, err := a.kubeClient()
157155
if err != nil {
@@ -160,7 +158,7 @@ func (a *Applier) waitForKubernetes(ctx context.Context) error {
160158
ticker := time.NewTicker(3 * time.Second)
161159
defer ticker.Stop()
162160
counter := 1
163-
pb.Infof("1/n Waiting for Kubernetes API server to be ready.")
161+
loading.Infof("1/n Waiting for Kubernetes API server to be ready.")
164162
for {
165163
select {
166164
case <-ticker.C:
@@ -169,7 +167,10 @@ func (a *Applier) waitForKubernetes(ctx context.Context) error {
169167
}
170168
counter++
171169
if err := kcli.List(ctx, &corev1.NamespaceList{}); err != nil {
172-
pb.Infof("%d/n Waiting for Kubernetes API server to be ready.", counter)
170+
loading.Infof(
171+
"%d/n Waiting for Kubernetes API server to be ready.",
172+
counter,
173+
)
173174
continue
174175
}
175176
return nil

pkg/config/host.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
"github.com/k0sproject/rig/log"
77

88
"github.com/replicatedhq/helmvm/pkg/infra"
9-
"github.com/replicatedhq/helmvm/pkg/progressbar"
9+
pb "github.com/replicatedhq/helmvm/pkg/progressbar"
1010
)
1111

1212
// hostcfg is a helper struct for collecting a node's configuration.
@@ -42,14 +42,13 @@ func (h *hostcfg) render() *cluster.Host {
4242

4343
// testConnection attempts to connect to the host via SSH.
4444
func (h *hostcfg) testConnection() error {
45-
logger, end := progressbar.Start()
45+
loading := pb.Start(nil)
4646
orig := log.Log
4747
defer func() {
48-
close(logger)
49-
<-end
48+
loading.Close()
5049
log.Log = orig
5150
}()
52-
rig.SetLogger(logger)
51+
rig.SetLogger(loading)
5352
return h.render().Connection.Connect()
5453
}
5554

pkg/infra/infra.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,13 @@ type Node struct {
2929
// Apply uses "terraform apply" to apply the infrastructe defined in the
3030
// directory passed as argument.
3131
func Apply(ctx context.Context, dir string, useprompt bool) ([]Node, error) {
32-
log, end := pb.Start()
33-
outputs, err := runApply(ctx, dir, log)
32+
loading := pb.Start(nil)
33+
outputs, err := runApply(ctx, dir, loading)
3434
if err != nil {
35-
log.Close()
36-
<-end
35+
loading.Close()
3736
return nil, fmt.Errorf("unable to apply infrastructure: %w", err)
3837
}
39-
log.Close()
40-
<-end
38+
loading.Close()
4139
fmt.Println("Infrastructure applied successfully")
4240
nodes, err := readNodes(outputs)
4341
if err != nil {

pkg/preflights/preflights.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,12 @@ func startProgressbar(addr string) (func(), error) {
115115
logfile := logrus.New()
116116
logfile.SetLevel(logrus.DebugLevel)
117117
logfile.SetOutput(fp)
118-
loading, end := pb.Start()
118+
loading := pb.Start(nil)
119119
loading.Infof("Running host preflight on %s", addr)
120120
orig := log.Log
121121
rig.SetLogger(logfile)
122122
return func() {
123-
loading.Infof("Host Preflight checks completed on host %s", addr)
124-
close(loading)
125-
<-end
123+
loading.Closef("Host Preflight checks completed on host %s", addr)
126124
log.Log = orig
127125
fp.Close()
128126
}, nil

pkg/progressbar/progressbar.go

Lines changed: 64 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,15 @@ import (
1111

1212
var blocks = []string{"◐", "◓", "◑", "◒"}
1313

14+
// WriterFn is a function that writes a formatted string.
15+
type WriteFn func(string, ...any) (int, error)
16+
1417
// MessageWriter implements io.Writer on top of a channel of strings.
15-
type MessageWriter chan string
18+
type MessageWriter struct {
19+
ch chan string
20+
end chan struct{}
21+
printf WriteFn
22+
}
1623

1724
// Write implements io.Writer for the MessageWriter.
1825
func (m MessageWriter) Write(p []byte) (n int, err error) {
@@ -22,74 +29,91 @@ func (m MessageWriter) Write(p []byte) (n int, err error) {
2229
if len(line) == 0 {
2330
continue
2431
}
25-
m <- line
32+
m.ch <- line
2633
}
2734
return len(p), nil
2835
}
2936

37+
// Closef closes the MessageWriter after writing a message.
38+
func (m MessageWriter) Closef(format string, args ...interface{}) {
39+
m.ch <- fmt.Sprintf(format, args...)
40+
m.Close()
41+
}
42+
3043
// Close closes the MessageWriter inner channel.
3144
func (m MessageWriter) Close() {
32-
close(m)
45+
close(m.ch)
46+
<-m.end
3347
}
3448

3549
// Tracef is implemeted to comply with rig log.Logger interface.
3650
func (m MessageWriter) Tracef(format string, args ...interface{}) {
37-
m <- fmt.Sprintf(format, args...)
51+
m.ch <- fmt.Sprintf(format, args...)
3852
}
3953

4054
// Debugf is implemeted to comply with rig log.Logger interface.
4155
func (m MessageWriter) Debugf(format string, args ...interface{}) {
42-
m <- fmt.Sprintf(format, args...)
56+
m.ch <- fmt.Sprintf(format, args...)
4357
}
4458

4559
// Infof is implemeted to comply with rig log.Logger interface.
4660
func (m MessageWriter) Infof(format string, args ...interface{}) {
47-
m <- fmt.Sprintf(format, args...)
61+
m.ch <- fmt.Sprintf(format, args...)
4862
}
4963

5064
// Warnf is implemeted to comply with rig log.Logger interface.
5165
func (m MessageWriter) Warnf(format string, args ...interface{}) {
52-
m <- fmt.Sprintf(format, args...)
66+
m.ch <- fmt.Sprintf(format, args...)
5367
}
5468

5569
// Errorf is implemeted to comply with rig log.Logger interface.
5670
func (m MessageWriter) Errorf(format string, args ...interface{}) {
57-
m <- fmt.Sprintf(format, args...)
71+
m.ch <- fmt.Sprintf(format, args...)
5872
}
5973

60-
// Start starts the progress bar. Returns a writer the caller can use to
61-
// send us messages to be printed on the screen and a channel to indicate
62-
// that the progress bar has finished its work (channel is closed at end)
63-
// The progress bar will keep running until the MessageWriter is closed.
64-
func Start() (MessageWriter, chan struct{}) {
65-
finished := make(chan struct{})
66-
mwriter := make(chan string, 1024)
67-
go func() {
68-
var counter int
69-
var message string
70-
var ticker = time.NewTicker(50 * time.Millisecond)
71-
var end bool
72-
for {
73-
select {
74-
case msg, open := <-mwriter:
75-
if !open {
76-
end = true
77-
} else {
78-
message = msg
79-
}
80-
case <-ticker.C:
81-
counter++
74+
// loop keeps reading messages from the channel and printint them
75+
// using the provided WriteFn. Exits when the channel is closed.
76+
func (m MessageWriter) loop() {
77+
var counter int
78+
var message string
79+
var ticker = time.NewTicker(50 * time.Millisecond)
80+
var end bool
81+
for {
82+
select {
83+
case msg, open := <-m.ch:
84+
if !open {
85+
end = true
86+
} else {
87+
message = msg
8288
}
89+
case <-ticker.C:
90+
counter++
91+
}
8392

84-
pos := counter % len(blocks)
85-
if !end {
86-
fmt.Printf("\033[K\r%s %s ", blocks[pos], message)
87-
continue
88-
}
89-
fmt.Printf("\033[K\r✓ %s\n", message)
90-
close(finished)
91-
return
93+
pos := counter % len(blocks)
94+
if !end {
95+
_, _ = m.printf("\033[K\r%s %s ", blocks[pos], message)
96+
continue
9297
}
93-
}()
94-
return mwriter, finished
98+
_, _ = m.printf("\033[K\r✓ %s\n", message)
99+
close(m.end)
100+
return
101+
}
102+
}
103+
104+
// Start starts the progress bar. Returns a writer the caller can use to
105+
// send us messages to be printed using the provided WriteFn (if nil then
106+
// we print to stdout). MessageWriter should be closed when done.
107+
// MessageWriter is closed.
108+
func Start(fn WriteFn) MessageWriter {
109+
if fn == nil {
110+
fn = fmt.Printf
111+
}
112+
mw := MessageWriter{
113+
ch: make(chan string, 1024),
114+
end: make(chan struct{}),
115+
printf: fn,
116+
}
117+
go mw.loop()
118+
return mw
95119
}

0 commit comments

Comments
 (0)