diff --git a/pkg/minikube/cruntime/containerd.go b/pkg/minikube/cruntime/containerd.go index 931ee6bb1bf7..4e44a62b0233 100644 --- a/pkg/minikube/cruntime/containerd.go +++ b/pkg/minikube/cruntime/containerd.go @@ -39,6 +39,7 @@ import ( "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/download" + "k8s.io/minikube/pkg/minikube/out" "k8s.io/minikube/pkg/minikube/style" "k8s.io/minikube/pkg/minikube/sysinit" "k8s.io/minikube/pkg/util/retry" @@ -227,6 +228,7 @@ func generateContainerdConfig(cr CommandRunner, imageRepository string, kv semve // Enable idempotently enables containerd on a host // It is also called by docker.Enable() - if bound to containerd, to enforce proper containerd configuration completed by service restart. func (r *Containerd) Enable(disOthers bool, cgroupDriver string, inUserNamespace bool) error { + out.Styled(style.SubStep, "starting container runtime...") if inUserNamespace { if err := CheckKernelCompatibility(r.Runner, 5, 11); err != nil { // For using overlayfs @@ -521,6 +523,7 @@ func (r *Containerd) Preload(cc config.ClusterConfig) error { if !download.PreloadExists(cc.KubernetesConfig.KubernetesVersion, cc.KubernetesConfig.ContainerRuntime, cc.Driver) { return nil } + out.Styled(style.SubStep, "Loading preloaded images...") k8sVersion := cc.KubernetesConfig.KubernetesVersion cRuntime := cc.KubernetesConfig.ContainerRuntime diff --git a/pkg/minikube/cruntime/crio.go b/pkg/minikube/cruntime/crio.go index 85699fa0d375..7b0256240a97 100644 --- a/pkg/minikube/cruntime/crio.go +++ b/pkg/minikube/cruntime/crio.go @@ -35,6 +35,7 @@ import ( "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/download" + "k8s.io/minikube/pkg/minikube/out" "k8s.io/minikube/pkg/minikube/style" "k8s.io/minikube/pkg/minikube/sysinit" ) @@ -215,6 +216,7 @@ Environment="_CRIO_ROOTLESS=1" // Enable idempotently enables CRIO on a host func (r *CRIO) Enable(disOthers bool, cgroupDriver string, inUserNamespace bool) error { + out.Styled(style.SubStep, "starting container runtime...") if disOthers { if err := disableOthers(r, r.Runner); err != nil { klog.Warningf("disableOthers: %v", err) @@ -420,7 +422,7 @@ func (r *CRIO) Preload(cc config.ClusterConfig) error { if !download.PreloadExists(cc.KubernetesConfig.KubernetesVersion, cc.KubernetesConfig.ContainerRuntime, cc.Driver) { return nil } - + out.Styled(style.SubStep, "Loading preloaded images...") k8sVersion := cc.KubernetesConfig.KubernetesVersion cRuntime := cc.KubernetesConfig.ContainerRuntime diff --git a/pkg/minikube/cruntime/docker.go b/pkg/minikube/cruntime/docker.go index db97878d0cad..1f83a88d4c37 100644 --- a/pkg/minikube/cruntime/docker.go +++ b/pkg/minikube/cruntime/docker.go @@ -39,6 +39,7 @@ import ( "k8s.io/minikube/pkg/minikube/docker" "k8s.io/minikube/pkg/minikube/download" "k8s.io/minikube/pkg/minikube/image" + "k8s.io/minikube/pkg/minikube/out" "k8s.io/minikube/pkg/minikube/style" "k8s.io/minikube/pkg/minikube/sysinit" "k8s.io/minikube/pkg/util/retry" @@ -133,6 +134,7 @@ func (r *Docker) Active() bool { // Enable idempotently enables Docker on a host func (r *Docker) Enable(disOthers bool, cgroupDriver string, inUserNamespace bool) error { + out.Styled(style.SubStep, "starting container runtime...") if inUserNamespace { if err := CheckKernelCompatibility(r.Runner, 5, 11); err != nil { // For using overlayfs @@ -621,6 +623,7 @@ func (r *Docker) Preload(cc config.ClusterConfig) error { if !download.PreloadExists(cc.KubernetesConfig.KubernetesVersion, cc.KubernetesConfig.ContainerRuntime, cc.Driver) { return nil } + out.Styled(style.SubStep, "Loading preloaded images...") k8sVersion := cc.KubernetesConfig.KubernetesVersion cRuntime := cc.KubernetesConfig.ContainerRuntime diff --git a/pkg/minikube/machine/delete.go b/pkg/minikube/machine/delete.go index a883f1028a6c..6f05026e9973 100644 --- a/pkg/minikube/machine/delete.go +++ b/pkg/minikube/machine/delete.go @@ -121,6 +121,7 @@ func deleteHost(api libmachine.API, h *host.Host, machineName string) error { // demolish destroys a host by any means necessary - use only if state is inconsistent func demolish(api libmachine.API, cc config.ClusterConfig, n config.Node, h *host.Host) { machineName := config.MachineName(cc, n) + out.Styled(style.SubStep, "Demolishing {{.machine_name}}...", out.V{"machine_name": machineName}) klog.Infof("DEMOLISHING %s ...", machineName) // This will probably fail diff --git a/pkg/minikube/machine/filesync.go b/pkg/minikube/machine/filesync.go index cd4bfdaf262f..7700b368845a 100644 --- a/pkg/minikube/machine/filesync.go +++ b/pkg/minikube/machine/filesync.go @@ -28,6 +28,8 @@ import ( "k8s.io/minikube/pkg/minikube/assets" "k8s.io/minikube/pkg/minikube/command" "k8s.io/minikube/pkg/minikube/localpath" + "k8s.io/minikube/pkg/minikube/out" + "k8s.io/minikube/pkg/minikube/style" "k8s.io/minikube/pkg/minikube/vmpath" ) @@ -42,6 +44,8 @@ var guaranteed = map[string]bool{ // syncLocalAssets syncs files from MINIKUBE_HOME into the cluster func syncLocalAssets(cr command.Runner) error { + out.Styled(style.SubStep, "Syncing local assets ...") + fs, err := localAssets() defer func() { for _, f := range fs { diff --git a/pkg/minikube/machine/fix.go b/pkg/minikube/machine/fix.go index 082e624d422c..b8717c97fc08 100644 --- a/pkg/minikube/machine/fix.go +++ b/pkg/minikube/machine/fix.go @@ -191,6 +191,7 @@ func ensureSyncedGuestClock(h hostRunner, drv string) error { if !driver.IsVM(drv) { return nil } + out.Styled(style.SubStep, "Syncing guest system clock ...") d, err := guestClockDelta(h, time.Now()) if err != nil { klog.Warningf("Unable to measure system clock delta: %v", err) diff --git a/pkg/minikube/machine/machine.go b/pkg/minikube/machine/machine.go index 62a24d4bae65..5b8729e867b0 100644 --- a/pkg/minikube/machine/machine.go +++ b/pkg/minikube/machine/machine.go @@ -29,6 +29,8 @@ import ( "k8s.io/klog/v2" "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/driver" + "k8s.io/minikube/pkg/minikube/out" + "k8s.io/minikube/pkg/minikube/style" "k8s.io/minikube/pkg/minikube/vmpath" "k8s.io/minikube/pkg/provision" "k8s.io/minikube/pkg/util/retry" @@ -100,7 +102,7 @@ func provisionDockerMachine(h *host.Host) error { if err != nil { return errors.Wrap(err, "fast detect") } - + out.Styled(style.SubStep, "Waiting for SSH server ...") // avoid costly need to stop/power off/delete and then re-create docker machine due to the un-ready ssh server and hence errors like: // 'error starting host: creating host: create: provisioning: ssh command error: command : sudo hostname minikube-m02 && echo "minikube-m02" | sudo tee /etc/hostname; err: exit status 255' // so retry only on "exit status 255" ssh error and fall through in all other cases diff --git a/pkg/minikube/machine/start.go b/pkg/minikube/machine/start.go index 34a1f1b58caf..7cbaa996417e 100644 --- a/pkg/minikube/machine/start.go +++ b/pkg/minikube/machine/start.go @@ -122,6 +122,7 @@ func engineOptions(cfg config.ClusterConfig) *engine.Options { } func createHost(api libmachine.API, cfg *config.ClusterConfig, n *config.Node) (*host.Host, error) { + out.Styled(style.SubStep, "Creating host {{.name}}...", out.V{"name": n.Name}) klog.Infof("createHost starting for %q (driver=%q)", n.Name, cfg.Driver) start := time.Now() defer func() { @@ -205,6 +206,8 @@ func postStartValidations(h *host.Host, drvName string) { if !driver.IsKIC(drvName) { return } + out.Styled(style.SubStep, "Post start validation ...") + r, err := CommandRunner(h) if err != nil { klog.Warningf("error getting command runner: %v", err) @@ -320,6 +323,7 @@ func postStartSetup(h *host.Host, mc config.ClusterConfig) error { } klog.Infof("creating required directories: %v", requiredDirectories) + out.Styled(style.SubStep, "Creating Dirs ...") r, err := CommandRunner(h) if err != nil { diff --git a/pkg/minikube/out/out.go b/pkg/minikube/out/out.go index 065abc7dc324..3973b6f8e785 100644 --- a/pkg/minikube/out/out.go +++ b/pkg/minikube/out/out.go @@ -91,7 +91,7 @@ func Step(st style.Enum, format string, a ...V) { Infof(format, a...) return } - outStyled, _ := stylized(st, useColor, format, a...) + outStyled, _, _ := stylized(st, useColor, format, a...) if JSON { register.PrintStep(outStyled) klog.Info(outStyled) @@ -107,9 +107,9 @@ func Styled(st style.Enum, format string, a ...V) { Infof(format, a...) return } - outStyled, useSpinner := stylized(st, useColor, format, a...) + outStyled, useSpinner, hideAfterSpin := stylized(st, useColor, format, a...) if useSpinner { - spinnerString(outStyled) + spinnerString(outStyled, hideAfterSpin) } else { String(outStyled) } @@ -146,13 +146,13 @@ func BoxedWithConfig(cfg box.Config, st style.Enum, title string, text string, a // Sprintf is used for returning the string (doesn't write anything) func Sprintf(st style.Enum, format string, a ...V) string { - outStyled, _ := stylized(st, useColor, format, a...) + outStyled, _, _ := stylized(st, useColor, format, a...) return outStyled } // Infof is used for informational logs (options, env variables, etc) func Infof(format string, a ...V) { - outStyled, _ := stylized(style.Option, useColor, format, a...) + outStyled, _, _ := stylized(style.Option, useColor, format, a...) if JSON { register.PrintInfo(outStyled) } @@ -214,6 +214,21 @@ func Output(file fdWriter, s string) { } } +// outputSpining writes a basic string with spinining +func outputSpining(file fdWriter, s string, hideAfterSpin bool) { + spin.Writer = file + spin.Prefix = s + if hideAfterSpin { + spin.UpdateCharSet(spinner.CharSets[style.SpinnerSubStepCharacter]) + spin.FinalMSG = "" + } else { + spin.UpdateCharSet(spinner.CharSets[style.SpinnerCharacter]) + spin.FinalMSG = s + "\n" + } + spin.Start() + +} + // Outputf writes a basic formatted string func Outputf(file fdWriter, format string, a ...interface{}) { _, err := fmt.Fprintf(file, format, a...) @@ -223,7 +238,7 @@ func Outputf(file fdWriter, format string, a ...interface{}) { } // spinnerString writes a basic formatted string to stdout with spinner character -func spinnerString(s string) { +func spinnerString(s string, hideAfterSpin bool) { // Flush log buffer so that output order makes sense klog.Flush() @@ -237,9 +252,7 @@ func spinnerString(s string) { if spin.Active() { spin.Stop() } - Output(outFile, s) - // Start spinning at the end of the printed line - spin.Start() + outputSpining(outFile, s, hideAfterSpin) } // Ln writes a basic formatted string with a newline to stdout @@ -249,7 +262,7 @@ func Ln(format string, a ...interface{}) { // ErrT writes a stylized and templated error message to stderr func ErrT(st style.Enum, format string, a ...V) { - errStyled, _ := stylized(st, useColor, format, a...) + errStyled, _, _ := stylized(st, useColor, format, a...) Err(errStyled) } @@ -316,7 +329,7 @@ func WarningT(format string, a ...V) { if spin.Active() { spin.Stop() } - st, _ := stylized(style.Warning, useColor, format, a...) + st, _, _ := stylized(style.Warning, useColor, format, a...) register.PrintWarning(st) klog.Warning(st) return diff --git a/pkg/minikube/out/out_reason.go b/pkg/minikube/out/out_reason.go index bd1ec43e3dfe..111569cfb78b 100644 --- a/pkg/minikube/out/out_reason.go +++ b/pkg/minikube/out/out_reason.go @@ -106,6 +106,6 @@ func determineOutput(st style.Enum, format string, a ...V) { ErrT(st, format, a...) return } - errStyled, _ := stylized(st, useColor, format, a...) + errStyled, _, _ := stylized(st, useColor, format, a...) klog.Warning(errStyled) } diff --git a/pkg/minikube/out/out_style.go b/pkg/minikube/out/out_style.go index 6943b91e2b40..f13d3154628f 100644 --- a/pkg/minikube/out/out_style.go +++ b/pkg/minikube/out/out_style.go @@ -31,31 +31,33 @@ func applyPrefix(prefix, format string) string { } // applyStyle translates the given string if necessary then adds any appropriate style prefix. -func applyStyle(st style.Enum, useColor bool, format string) (string, bool) { +func applyStyle(st style.Enum, useColor bool, format string) (string, bool, bool) { format = translate.T(format) s, ok := style.Config[st] - if !s.OmitNewline { + // becaue of https://github.com/kubernetes/minikube/issues/21148 + // will handle making new lines with spinner library itself + if !s.ShouldSpin { format += "\n" } // Similar to CSS styles, if no style matches, output an unformatted string. if !ok || JSON { - return format, s.Spinner + return format, s.ShouldSpin, s.HideAfterSpin } if !useColor { - return applyPrefix(style.LowPrefix(s), format), s.Spinner + return applyPrefix(style.LowPrefix(s), format), s.ShouldSpin, s.HideAfterSpin } - return applyPrefix(s.Prefix, format), s.Spinner + return applyPrefix(s.Prefix, format), s.ShouldSpin, s.HideAfterSpin } // stylized applies formatting to the provided template -func stylized(st style.Enum, useColor bool, format string, a ...V) (string, bool) { - var spinner bool +func stylized(st style.Enum, useColor bool, format string, a ...V) (string, bool, bool) { + var shouldSpin, hideAfterSpin bool if a == nil { a = []V{} } - format, spinner = applyStyle(st, useColor, format) - return Fmt(format, a...), spinner + format, shouldSpin, hideAfterSpin = applyStyle(st, useColor, format) + return Fmt(format, a...), shouldSpin, hideAfterSpin } diff --git a/pkg/minikube/out/out_style_test.go b/pkg/minikube/out/out_style_test.go index 66491fd6ec05..235c8d14ee7c 100644 --- a/pkg/minikube/out/out_style_test.go +++ b/pkg/minikube/out/out_style_test.go @@ -83,7 +83,7 @@ func TestApplyStyle(t *testing.T) { } for _, test := range tests { t.Run(test.description, func(t *testing.T) { - rawGot, _ := applyStyle(test.styleEnum, test.useColor, test.format) + rawGot, _, _ := applyStyle(test.styleEnum, test.useColor, test.format) got := strings.TrimSpace(rawGot) if got != test.expected { t.Errorf("Expected '%v' but got '%v'", test.expected, got) @@ -139,7 +139,7 @@ func TestApplyTemplateFormating(t *testing.T) { } for _, test := range tests { t.Run(test.description, func(t *testing.T) { - rawGot, _ := stylized(test.styleEnum, test.useColor, test.format, test.a...) + rawGot, _, _ := stylized(test.styleEnum, test.useColor, test.format, test.a...) got := strings.TrimSpace(rawGot) if got != test.expected { t.Errorf("Expected '%v' but got '%v'", test.expected, got) diff --git a/pkg/minikube/registry/drvs/krunkit/krunkit.go b/pkg/minikube/registry/drvs/krunkit/krunkit.go index d5388372d334..21af1dd6a131 100644 --- a/pkg/minikube/registry/drvs/krunkit/krunkit.go +++ b/pkg/minikube/registry/drvs/krunkit/krunkit.go @@ -87,7 +87,7 @@ func configure(cfg config.ClusterConfig, n config.Node) (interface{}, error) { func status() registry.State { if runtime.GOOS != "darwin" && runtime.GOARCH != "arm64" { - err := errors.New("The krunkit driver is only supported on macOS arm64 machines") + err := errors.New("the krunkit driver is only supported on macOS arm64 machines") return registry.State{Error: err, Fix: "Use another driver", Doc: docURL} } if _, err := exec.LookPath("krunkit"); err != nil { diff --git a/pkg/minikube/style/style.go b/pkg/minikube/style/style.go index 411ee2162590..4dbb96ce3c12 100644 --- a/pkg/minikube/style/style.go +++ b/pkg/minikube/style/style.go @@ -41,15 +41,17 @@ type Options struct { Prefix string // LowPrefix is the 7-bit compatible prefix we fallback to for less-awesome terminals LowPrefix string - // OmitNewline omits a newline at the end of a message. - OmitNewline bool - // Spinner is a character to place at ending of message - Spinner bool + // ShouldSpin is a character to place at ending of message + ShouldSpin bool + HideAfterSpin bool // Hide the prefix after spinning } // SpinnerCharacter is which of the spinner.CharSets to use const SpinnerCharacter = 9 +// SpinnerSubStepCharacter is Character to use for sub-steps in a spinner (it looks like a progress bar) +const SpinnerSubStepCharacter = 35 + // Config is a map of style name to style struct // For consistency, ensure that emojis added render with the same width across platforms. var Config = map[Enum]Options{ @@ -72,8 +74,8 @@ var Config = map[Enum]Options{ Pause: {Prefix: "⏸️ "}, Provisioning: {Prefix: "🌱 "}, Ready: {Prefix: "πŸ„ "}, - Restarting: {Prefix: "πŸ”„ "}, - Running: {Prefix: "πŸƒ "}, + Restarting: {Prefix: "πŸ”„ ", ShouldSpin: true}, + Running: {Prefix: "πŸƒ ", ShouldSpin: true}, // this is used when minikube start for a second time (already started) Sparkle: {Prefix: "✨ "}, Stopped: {Prefix: "πŸ›‘ "}, Stopping: {Prefix: "βœ‹ "}, @@ -84,7 +86,7 @@ var Config = map[Enum]Options{ URL: {Prefix: "πŸ‘‰ ", LowPrefix: LowIndent}, Usage: {Prefix: "πŸ’‘ "}, Waiting: {Prefix: "βŒ› "}, - WaitingWithSpinner: {Prefix: "βŒ› ", OmitNewline: true, Spinner: true}, + WaitingWithSpinner: {Prefix: "βŒ› ", ShouldSpin: true}, Unsupported: {Prefix: "🚑 "}, Workaround: {Prefix: "πŸ‘‰ ", LowPrefix: LowIndent}, @@ -113,11 +115,11 @@ var Config = map[Enum]Options{ Copying: {Prefix: "✨ "}, CRIO: {Prefix: "🎁 "}, // This should be a snow-flake, but the emoji has a strange width on macOS DeletingHost: {Prefix: "πŸ”₯ "}, - Docker: {Prefix: "🐳 ", OmitNewline: true, Spinner: true}, + Docker: {Prefix: "🐳 ", ShouldSpin: true}, DryRun: {Prefix: "🌡 "}, Enabling: {Prefix: "πŸ”Œ "}, FileDownload: {Prefix: "πŸ’Ύ "}, - Fileserver: {Prefix: "πŸš€ ", OmitNewline: true}, + Fileserver: {Prefix: "πŸš€ "}, HealthCheck: {Prefix: "πŸ”Ž "}, Internet: {Prefix: "🌐 "}, ISODownload: {Prefix: "πŸ’Ώ "}, @@ -132,11 +134,11 @@ var Config = map[Enum]Options{ Shutdown: {Prefix: "πŸ›‘ "}, StartingNone: {Prefix: "🀹 "}, StartingSSH: {Prefix: "πŸ”— "}, - StartingVM: {Prefix: "πŸ”₯ ", OmitNewline: true, Spinner: true}, - SubStep: {Prefix: " β–ͺ ", LowPrefix: LowIndentBullet, OmitNewline: true, Spinner: true}, + StartingVM: {Prefix: "πŸ”₯ ", ShouldSpin: true}, + SubStep: {Prefix: " β–ͺ ", LowPrefix: LowIndentBullet, ShouldSpin: true, HideAfterSpin: true}, Tip: {Prefix: "πŸ’‘ "}, Unmount: {Prefix: "πŸ”₯ "}, - VerifyingNoLine: {Prefix: "πŸ€” ", OmitNewline: true}, + VerifyingNoLine: {Prefix: "πŸ€” "}, Verifying: {Prefix: "πŸ€” "}, CNI: {Prefix: "πŸ”— "}, Toolkit: {Prefix: "πŸ› οΈ "}, diff --git a/test/integration/error_spam_test.go b/test/integration/error_spam_test.go index bbd7df4d782e..a4e42fe3a64d 100644 --- a/test/integration/error_spam_test.go +++ b/test/integration/error_spam_test.go @@ -111,16 +111,6 @@ func TestErrorSpam(t *testing.T) { t.Logf("minikube stderr:\n%s", stderr) } - steps := []string{ - "Generating certificates and keys ...", - "Booting up control plane ...", - "Configuring RBAC rules ...", - } - for _, step := range steps { - if !strings.Contains(stdout, step) { - t.Errorf("missing kubeadm init sub-step %q", step) - } - } }) logTests := []struct {