Skip to content

Commit e49986d

Browse files
committed
Merge remote-tracking branch 'origin/main' into k0s-1-28-8
2 parents f69717e + 9be7c0d commit e49986d

File tree

7 files changed

+190
-18
lines changed

7 files changed

+190
-18
lines changed

Makefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ ARCH := $(shell uname -m)
44
APP_NAME = embedded-cluster
55
ADMIN_CONSOLE_CHART_URL = oci://registry.replicated.com/library
66
ADMIN_CONSOLE_CHART_NAME = admin-console
7-
ADMIN_CONSOLE_CHART_VERSION = 1.108.5
7+
ADMIN_CONSOLE_CHART_VERSION = 1.108.6
88
ADMIN_CONSOLE_IMAGE_OVERRIDE =
99
ADMIN_CONSOLE_MIGRATIONS_IMAGE_OVERRIDE =
1010
EMBEDDED_OPERATOR_CHART_URL = oci://registry.replicated.com/library
1111
EMBEDDED_OPERATOR_CHART_NAME = embedded-cluster-operator
12-
EMBEDDED_OPERATOR_CHART_VERSION = 0.28.0
12+
EMBEDDED_OPERATOR_CHART_VERSION = 0.28.1
1313
EMBEDDED_OPERATOR_UTILS_IMAGE = busybox:1.36.1
1414
EMBEDDED_CLUSTER_OPERATOR_IMAGE_OVERRIDE =
1515
OPENEBS_CHART_URL = https://openebs.github.io/charts
@@ -149,6 +149,7 @@ e2e-test:
149149
clean:
150150
rm -rf output
151151
rm -rf pkg/goods/bins
152+
rm -rf pkg/goods/internal/bins
152153

153154
.PHONY: lint
154155
lint:

cmd/embedded-cluster/uninstall.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ var resetCommand = &cli.Command{
319319
},
320320
Usage: fmt.Sprintf("Uninstall %s from the current node", binName),
321321
Action: func(c *cli.Context) error {
322-
logrus.Info("This will remove this node from the cluster and completely reset it.")
322+
logrus.Info("This will remove this node from the cluster and completely reset it, removing all data stored on the node.")
323323
logrus.Info("Do not reset another node until this is complete.")
324324
if !c.Bool("force") && !c.Bool("no-prompt") && !prompts.New().Confirm("Do you want to continue?", false) {
325325
return fmt.Errorf("Aborting")

pkg/addons/adminconsole/adminconsole.go

Lines changed: 107 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"encoding/base64"
88
"fmt"
99
"os"
10+
"regexp"
11+
"strings"
1012
"time"
1113

1214
"github.com/k0sproject/dig"
@@ -41,6 +43,7 @@ var (
4143
Version = "v0.0.0"
4244
ImageOverride = ""
4345
MigrationsImageOverride = ""
46+
CounterRegex = regexp.MustCompile(`(\d+)/(\d+)`)
4447
)
4548

4649
// protectedFields are helm values that are not overwritten when upgrading the addon.
@@ -207,6 +210,99 @@ func (a *AdminConsole) GetAdditionalImages() []string {
207210
return nil
208211
}
209212

213+
// MaskKotsOutputForOnline masks the kots cli output during online installations. For
214+
// online installations we only want to print "Finalizing" until it is done and then
215+
// print "Finished!".
216+
func (a *AdminConsole) MaskKotsOutputForOnline() spinner.MaskFn {
217+
return func(message string) string {
218+
if strings.Contains(message, "Finished") {
219+
return message
220+
}
221+
return "Finalizing"
222+
}
223+
}
224+
225+
// MaskKotsOutputForAirgap masks the kots cli output during airgap installations. This
226+
// function replaces some of the messages being printed to the user so the output looks
227+
// nicer.
228+
func (a *AdminConsole) MaskKotsOutputForAirgap() spinner.MaskFn {
229+
current := "Uploading air gap bundle"
230+
return func(message string) string {
231+
switch {
232+
case strings.Contains(message, "Pushing application images"):
233+
current = message
234+
case strings.Contains(message, "Pushing embedded cluster artifacts"):
235+
current = message
236+
case strings.Contains(message, "Waiting for Admin Console"):
237+
current = "Finalizing"
238+
case strings.Contains(message, "Finished!"):
239+
current = message
240+
}
241+
return current
242+
}
243+
}
244+
245+
// KostsOutputLineBreaker creates a line break (new spinner) when some of the messages
246+
// are printed to the user. For example: after finishing all image uploads we want to
247+
// have a new spinner for the artifacts upload.
248+
func (a *AdminConsole) KostsOutputLineBreaker() spinner.LineBreakerFn {
249+
// finished is an auxiliary function that evaluates if a message refers to a
250+
// step that has been finished. We determine that by inspected if the message
251+
// contains %d/%d and both integers are equal.
252+
finished := func(message string) bool {
253+
matches := CounterRegex.FindStringSubmatch(message)
254+
if len(matches) != 3 {
255+
return false
256+
}
257+
var counter int
258+
if _, err := fmt.Sscanf(matches[1], "%d", &counter); err != nil {
259+
return false
260+
}
261+
var total int
262+
if _, err := fmt.Sscanf(matches[2], "%d", &total); err != nil {
263+
return false
264+
}
265+
return counter == total
266+
}
267+
268+
var previous string
269+
var seen = map[string]bool{}
270+
return func(current string) (bool, string) {
271+
defer func() {
272+
previous = current
273+
}()
274+
275+
// if we have already seen this message we certainly have already assessed
276+
// if a break line as necessary or not, on this case we return false so we
277+
// do not keep breaking lines indefinitely.
278+
if _, ok := seen[current]; ok {
279+
return false, ""
280+
}
281+
seen[current] = true
282+
283+
// if the previous message evaluated does not relate to an end of a process
284+
// we don't want to break the line. i.e. we only want to break the line when
285+
// the previous evaluated message contains %d/%d and both integers are equal.
286+
if !finished(previous) {
287+
return false, ""
288+
}
289+
290+
// if we are printing a message about pushing the embedded cluster artifacts
291+
// it means that we have finished with the images and we want to break the line.
292+
if strings.Contains(current, "Pushing embedded cluster artifacts") {
293+
return true, "Application images are ready!"
294+
}
295+
296+
// if we are printing a message about the finalization of the installation it
297+
// means that the embedded cluster artifacts are ready and we want to break the
298+
// line.
299+
if current == "Finalizing" {
300+
return true, "Embedded cluster artifacts are ready!"
301+
}
302+
return false, ""
303+
}
304+
}
305+
210306
// Outro waits for the adminconsole to be ready.
211307
func (a *AdminConsole) Outro(ctx context.Context, cli client.Client) error {
212308
loading := spinner.Start()
@@ -244,28 +340,27 @@ func (a *AdminConsole) Outro(ctx context.Context, cli client.Client) error {
244340
return fmt.Errorf("error waiting for admin console: %v", lasterr)
245341
}
246342

343+
loading.Closef("Admin Console is ready!")
247344
if a.licenseFile == "" {
248-
loading.Closef("Admin Console is ready!")
249345
return nil
250346
}
251347

252-
loading.Infof("Finalizing")
253-
254348
kotsBinPath, err := goods.MaterializeInternalBinary("kubectl-kots")
255349
if err != nil {
256-
loading.Close()
257350
return fmt.Errorf("unable to materialize kubectl-kots binary: %w", err)
258351
}
259352
defer os.Remove(kotsBinPath)
260353

261354
license, err := helpers.ParseLicense(a.licenseFile)
262355
if err != nil {
356+
loading.CloseWithError()
263357
return fmt.Errorf("unable to parse license: %w", err)
264358
}
265359

266360
var appVersionLabel string
267361
var channelSlug string
268362
if channelRelease, err := release.GetChannelRelease(); err != nil {
363+
loading.CloseWithError()
269364
return fmt.Errorf("unable to get channel release: %w", err)
270365
} else if channelRelease != nil {
271366
appVersionLabel = channelRelease.VersionLabel
@@ -277,6 +372,8 @@ func (a *AdminConsole) Outro(ctx context.Context, cli client.Client) error {
277372
upstreamURI = fmt.Sprintf("%s/%s", upstreamURI, channelSlug)
278373
}
279374

375+
var lbreakfn spinner.LineBreakerFn
376+
maskfn := a.MaskKotsOutputForOnline()
280377
installArgs := []string{
281378
"install",
282379
upstreamURI,
@@ -290,14 +387,17 @@ func (a *AdminConsole) Outro(ctx context.Context, cli client.Client) error {
290387
}
291388
if a.airgapBundle != "" {
292389
installArgs = append(installArgs, "--airgap-bundle", a.airgapBundle)
390+
maskfn = a.MaskKotsOutputForAirgap()
391+
lbreakfn = a.KostsOutputLineBreaker()
293392
}
294393

295-
if _, err := helpers.RunCommand(kotsBinPath, installArgs...); err != nil {
296-
loading.Close()
394+
loading = spinner.Start(spinner.WithMask(maskfn), spinner.WithLineBreaker(lbreakfn))
395+
if err := helpers.RunCommandWithWriter(loading, kotsBinPath, installArgs...); err != nil {
396+
loading.CloseWithError()
297397
return fmt.Errorf("unable to install the application: %w", err)
298398
}
299399

300-
loading.Closef("Admin Console is ready!")
400+
loading.Closef("Finished!")
301401
a.printSuccessMessage(license.Spec.AppSlug)
302402
return nil
303403
}

pkg/helpers/command.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,40 @@ package helpers
33
import (
44
"bytes"
55
"fmt"
6+
"io"
67
"os/exec"
78

89
"github.com/sirupsen/logrus"
910
)
1011

11-
// RunCommand spawns a command and capture its output. Outputs are logged using the
12-
// logrus package and stdout is returned as a string.
13-
func RunCommand(bin string, args ...string) (string, error) {
12+
// RunCommandWithWriter runs a the provided command. The stdout of the command is
13+
// written to the provider writer.
14+
func RunCommandWithWriter(to io.Writer, bin string, args ...string) error {
1415
fullcmd := append([]string{bin}, args...)
1516
logrus.Debugf("running command: %v", fullcmd)
1617

17-
stdout := bytes.NewBuffer(nil)
1818
stderr := bytes.NewBuffer(nil)
19+
stdout := bytes.NewBuffer(nil)
1920
cmd := exec.Command(bin, args...)
20-
cmd.Stdout = stdout
21+
cmd.Stdout = io.MultiWriter(to, stdout)
2122
cmd.Stderr = stderr
2223
if err := cmd.Run(); err != nil {
2324
logrus.Debugf("failed to run command:")
2425
logrus.Debugf("stdout: %s", stdout.String())
2526
logrus.Debugf("stderr: %s", stderr.String())
2627
if stderr.String() != "" {
27-
return "", fmt.Errorf("%w: %s", err, stderr.String())
28+
return fmt.Errorf("%w: %s", err, stderr.String())
2829
}
30+
return err
31+
}
32+
return nil
33+
}
34+
35+
// RunCommand spawns a command and capture its output. Outputs are logged using the
36+
// logrus package and stdout is returned as a string.
37+
func RunCommand(bin string, args ...string) (string, error) {
38+
stdout := bytes.NewBuffer(nil)
39+
if err := RunCommandWithWriter(stdout, bin, args...); err != nil {
2940
return "", err
3041
}
3142
return stdout.String(), nil

pkg/spinner/options.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ func WithWriter(w WriteFn) Option {
1010
}
1111
}
1212

13+
// WithLineBreaker sets a function that determines if if is time
14+
// to break the line thus creating a new spinner line. The previous
15+
// step is flagged as success.
16+
func WithLineBreaker(lb LineBreakerFn) Option {
17+
return func(m *MessageWriter) {
18+
m.lbreak = lb
19+
}
20+
}
21+
1322
// WithMask sets the MaskFn on the MessageWriter.
1423
func WithMask(mfn MaskFn) Option {
1524
return func(m *MessageWriter) {

pkg/spinner/spinner.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ var blocks = []string{"◐", "◓", "◑", "◒"}
1212
// WriteFn is a function that writes a formatted string.
1313
type WriteFn func(string, ...any) (int, error)
1414

15+
// LineBreakerFn is a function that determines if it is time to break the
16+
// line thus creating a new spinner line. Returns a boolean and a string
17+
// indicating what needs to be printed before the line break.
18+
type LineBreakerFn func(string) (bool, string)
19+
1520
// MaskFn is a function that masks a message. Receives a string and
1621
// returns a string, the returned string is printed to the terminal.
1722
type MaskFn func(string) string
@@ -23,6 +28,7 @@ type MessageWriter struct {
2328
err bool
2429
printf WriteFn
2530
mask MaskFn
31+
lbreak LineBreakerFn
2632
}
2733

2834
// Write implements io.Writer for the MessageWriter.
@@ -90,6 +96,8 @@ func (m *MessageWriter) loop() {
9096
var ticker = time.NewTicker(50 * time.Millisecond)
9197
var end bool
9298
for {
99+
previous := message
100+
93101
select {
94102
case msg, open := <-m.ch:
95103
if !open {
@@ -100,19 +108,32 @@ func (m *MessageWriter) loop() {
100108
case <-ticker.C:
101109
counter++
102110
}
111+
103112
if m.mask != nil {
104113
message = m.mask(message)
105114
}
115+
116+
if m.lbreak != nil && previous != message {
117+
if lbreak, lcontent := m.lbreak(message); lbreak {
118+
if diff := len(previous) - len(lcontent); diff > 0 {
119+
suffix := strings.Repeat(" ", diff)
120+
lcontent = fmt.Sprintf("%s%s", lcontent, suffix)
121+
}
122+
m.printf("\033[K\r✔ %s\n", lcontent)
123+
}
124+
}
125+
106126
pos := counter % len(blocks)
107127
if !end {
108-
_, _ = m.printf("\033[K\r%s %s ", blocks[pos], message)
128+
m.printf("\033[K\r%s %s", blocks[pos], message)
109129
continue
110130
}
131+
111132
prefix := "✔"
112133
if m.err {
113134
prefix = "✗"
114135
}
115-
_, _ = m.printf("\033[K\r%s %s\n", prefix, message)
136+
m.printf("\033[K\r%s %s\n", prefix, message)
116137
close(m.end)
117138
return
118139
}

pkg/spinner/spinner_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"fmt"
66
"io"
7+
"strings"
78
"testing"
89
"time"
910

@@ -107,3 +108,32 @@ func TestMask(t *testing.T) {
107108
assert.Contains(t, buf.String(), "masked 0")
108109
assert.Contains(t, buf.String(), "masked 1")
109110
}
111+
112+
func TestLineBreak(t *testing.T) {
113+
buf := bytes.NewBuffer(nil)
114+
lbreak := func(s string) (bool, string) {
115+
if s == "test 3" {
116+
return true, "ping 2"
117+
}
118+
if s == "test 8" {
119+
return true, "ping 7"
120+
}
121+
return false, ""
122+
}
123+
pb := Start(
124+
WithWriter(WriteTo(buf)),
125+
WithLineBreaker(lbreak),
126+
)
127+
for i := 0; i < 100; i++ {
128+
pb.Infof("test %d", i)
129+
}
130+
pb.Close()
131+
// we expect the following output:
132+
// ✔ ping 2 (\n)
133+
// ✔ ping 7 (\n)
134+
// ✔ test 99 (\n)
135+
assert.Equal(t, strings.Count(buf.String(), "\n"), 3)
136+
assert.Contains(t, buf.String(), "ping 2")
137+
assert.Contains(t, buf.String(), "ping 7")
138+
assert.Contains(t, buf.String(), "test 99")
139+
}

0 commit comments

Comments
 (0)