diff --git a/go.mod b/go.mod index a3d449a00a09..0ecc3be78e1a 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.15 require ( cloud.google.com/go/storage v1.10.0 + contrib.go.opencensus.io/exporter/stackdriver v0.12.1 github.com/Azure/azure-sdk-for-go v42.3.0+incompatible github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v0.13.0 github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 // indirect @@ -11,6 +12,7 @@ require ( github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/VividCortex/godaemon v0.0.0-20201030160542-15e3f4925a21 github.com/blang/semver v3.5.0+incompatible + github.com/briandowns/spinner v1.11.1 github.com/c4milo/gotoolkit v0.0.0-20170318115440-bcc06269efa9 // indirect github.com/cenkalti/backoff v2.2.1+incompatible github.com/cenkalti/backoff/v4 v4.1.0 @@ -75,6 +77,7 @@ require ( github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f github.com/zchee/go-vmnet v0.0.0-20161021174912-97ebf9174097 + go.opencensus.io v0.22.4 go.opentelemetry.io/otel v0.13.0 go.opentelemetry.io/otel/sdk v0.13.0 golang.org/x/build v0.0.0-20190927031335-2835ba2e683f @@ -102,6 +105,7 @@ require ( replace ( git.apache.org/thrift.git => github.com/apache/thrift v0.0.0-20180902110319-2566ecd5d999 + github.com/briandowns/spinner => github.com/alonyb/spinner v1.12.1 github.com/docker/docker => github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7 github.com/docker/machine => github.com/machine-drivers/machine v0.7.1-0.20200810185219-7d42fed1b770 github.com/google/go-containerregistry => github.com/afbjorklund/go-containerregistry v0.0.0-20200902152226-fbad78ec2813 diff --git a/go.sum b/go.sum index 3d243d94917f..1ce4cf5266c0 100644 --- a/go.sum +++ b/go.sum @@ -143,6 +143,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZq github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alonyb/spinner v1.12.1 h1:zB6IQ29/kTR/NWHJhIgU2tXW+fhXa3K5zrDQMddd9H0= +github.com/alonyb/spinner v1.12.1/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/apex/log v1.1.4/go.mod h1:AlpoD9aScyQfJDVHmLMEcx4oU6LqzkWp4Mg9GdAcEvQ= diff --git a/pkg/minikube/out/out.go b/pkg/minikube/out/out.go index 9f71859c2531..df6cadc156ad 100644 --- a/pkg/minikube/out/out.go +++ b/pkg/minikube/out/out.go @@ -26,7 +26,9 @@ import ( "os" "strconv" "strings" + "time" + "github.com/briandowns/spinner" isatty "github.com/mattn/go-isatty" "k8s.io/klog/v2" @@ -58,10 +60,14 @@ var ( OverrideEnv = "MINIKUBE_IN_STYLE" // JSON is whether or not we should output stdout in JSON format. Set using SetJSON() JSON = false + // spin is spinner showed at starting minikube + spin = spinner.New(spinner.CharSets[style.SpinnerCharacter], 100*time.Millisecond) ) // MaxLogEntries controls the number of log entries to show for each source -const MaxLogEntries = 3 +const ( + MaxLogEntries = 3 +) // fdWriter is the subset of file.File that implements io.Writer and Fd() type fdWriter interface { @@ -78,18 +84,22 @@ func Step(st style.Enum, format string, a ...V) { Infof(format, a...) return } - outStyled := stylized(st, useColor, format, a...) + outStyled, spinner := stylized(st, useColor, format, a...) if JSON { register.PrintStep(outStyled) return } register.RecordStep(outStyled) - String(outStyled) + if spinner { + spinnerString(outStyled) + } else { + String(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) return @@ -106,13 +116,34 @@ func String(format string, a ...interface{}) { klog.Warningf("[unset outFile]: %s", fmt.Sprintf(format, a...)) return } - klog.Infof(format, a...) + // if spin is active from a previous step, it will stop spinner displaying + if spin.Active() { + spin.Stop() + } + _, err := fmt.Fprintf(outFile, format, a...) + if err != nil { + klog.Errorf("Fprintf failed: %v", err) + } +} +// spinnerString writes a basic formatted string to stdout with spinner character +func spinnerString(format string, a ...interface{}) { + // Flush log buffer so that output order makes sense + klog.Flush() + + if outFile == nil { + klog.Warningf("[unset outFile]: %s", fmt.Sprintf(format, a...)) + return + } + + klog.Infof(format, a...) _, err := fmt.Fprintf(outFile, format, a...) if err != nil { klog.Errorf("Fprintf failed: %v", err) } + // Start spinning at the end of the printed line + spin.Start() } // Ln writes a basic formatted string with a newline to stdout @@ -126,7 +157,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) } @@ -169,7 +200,8 @@ func FatalT(format string, a ...V) { // WarningT is a shortcut for writing a templated warning message to stderr func WarningT(format string, a ...V) { if JSON { - register.PrintWarning(stylized(style.Warning, useColor, format, a...)) + st, _ := stylized(style.Warning, useColor, format, a...) + register.PrintWarning(st) return } ErrT(style.Warning, format, a...) diff --git a/pkg/minikube/out/out_style.go b/pkg/minikube/out/out_style.go index 33260e179868..6bcc6a16e454 100644 --- a/pkg/minikube/out/out_style.go +++ b/pkg/minikube/out/out_style.go @@ -31,7 +31,7 @@ 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 { +func applyStyle(st style.Enum, useColor bool, format string) (string, bool) { format = translate.T(format) s, ok := style.Config[st] @@ -41,20 +41,21 @@ func applyStyle(st style.Enum, useColor bool, format string) string { // Similar to CSS styles, if no style matches, output an unformatted string. if !ok || JSON { - return format + return format, s.Spinner } if !useColor { - return applyPrefix(style.LowPrefix(s), format) + return applyPrefix(style.LowPrefix(s), format), s.Spinner } - return applyPrefix(s.Prefix, format) + return applyPrefix(s.Prefix, format), s.Spinner } // stylized applies formatting to the provided template -func stylized(st style.Enum, useColor bool, format string, a ...V) string { +func stylized(st style.Enum, useColor bool, format string, a ...V) (string, bool) { + var spinner bool if a == nil { a = []V{{}} } - format = applyStyle(st, useColor, format) - return Fmt(format, a...) + format, spinner = applyStyle(st, useColor, format) + return Fmt(format, a...), spinner } diff --git a/pkg/minikube/out/out_style_test.go b/pkg/minikube/out/out_style_test.go index fb231d333ac0..66491fd6ec05 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/style/style.go b/pkg/minikube/style/style.go index cc722796b056..4bd9eaaf3209 100644 --- a/pkg/minikube/style/style.go +++ b/pkg/minikube/style/style.go @@ -41,8 +41,12 @@ type Options struct { 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 } +const SpinnerCharacter = 9 + // 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{ @@ -104,7 +108,7 @@ 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: "🐳 "}, + Docker: {Prefix: "🐳 ", OmitNewline: true, Spinner: true}, DryRun: {Prefix: "🌵 "}, Enabling: {Prefix: "🔌 "}, FileDownload: {Prefix: "💾 "},