diff --git a/cli/cmd/generate_execute.go b/cli/cmd/generate_execute.go index a050dd2b8..8b92b5260 100644 --- a/cli/cmd/generate_execute.go +++ b/cli/cmd/generate_execute.go @@ -11,6 +11,7 @@ import ( "github.com/AlecAivazis/survey/v2" "github.com/Masterminds/semver" + "github.com/abiosoft/colima/util/terminal" "github.com/hashicorp/go-version" "github.com/hashicorp/hc-install/product" "github.com/hashicorp/hc-install/releases" @@ -298,7 +299,6 @@ func TerraformExecPlan(tf *tfexec.Terraform) (*TfPlanChangesSummary, error) { // - Run plan // - Get plan file details (returned) func TerraformExecApply(tf *tfexec.Terraform) error { - // Plan cli.StartProgress("Running terraform apply") err := tf.Apply(context.Background()) cli.StopProgress() @@ -383,6 +383,10 @@ func TerraformPlanAndExecute(workingDir string) error { return err } + vw := terminal.NewVerboseWriter(10) + tf.SetStdout(vw) + tf.SetStderr(vw) + // Initialize tf project if err := TerraformInit(tf); err != nil { return err @@ -394,6 +398,8 @@ func TerraformPlanAndExecute(workingDir string) error { return err } + vw.Close() + // Display changes and determine if apply should proceed proceed, err := DisplayTerraformPlanChanges(tf, *changes) if err != nil { @@ -406,12 +412,17 @@ func TerraformPlanAndExecute(workingDir string) error { return nil } + vw = terminal.NewVerboseWriter(10) + tf.SetStdout(vw) + tf.SetStderr(vw) + // Apply plan if err := TerraformExecApply(tf); err != nil { return err } - cli.OutputHuman(provideGuidanceAfterSuccess(tf.WorkingDir(), GenerateAwsCommandState.LaceworkProfile)) + vw.Close() + cli.OutputHuman(provideGuidanceAfterSuccess(tf.WorkingDir(), GenerateAwsCommandState.LaceworkProfile)) return nil } diff --git a/go.mod b/go.mod index d03a66a66..da23ac649 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/BurntSushi/toml v0.4.1 github.com/Masterminds/semver v1.5.0 github.com/Netflix/go-expect v0.0.0-20200312175327-da48e75238e2 + github.com/abiosoft/colima v0.3.2 github.com/briandowns/spinner v1.17.0 github.com/fatih/color v1.13.0 github.com/fatih/structs v1.1.0 diff --git a/go.sum b/go.sum index fc2888deb..2ab122d96 100644 --- a/go.sum +++ b/go.sum @@ -62,6 +62,8 @@ github.com/Netflix/go-expect v0.0.0-20200312175327-da48e75238e2/go.mod h1:oX5x61 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= +github.com/abiosoft/colima v0.3.2 h1:Kvu/IXIvLAIHhqOsss6hwrbF1VhT/qie3fweGCn2ncM= +github.com/abiosoft/colima v0.3.2/go.mod h1:Ot7X2x/P9iY3tEGOojV4xDJIjq/Z4b2ogE9dHf4/ZLY= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8= @@ -105,6 +107,7 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -129,6 +132,7 @@ github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpm github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= @@ -323,6 +327,7 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -392,6 +397,7 @@ github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNX github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= diff --git a/vendor/github.com/abiosoft/colima/LICENSE b/vendor/github.com/abiosoft/colima/LICENSE new file mode 100644 index 000000000..42bf341bd --- /dev/null +++ b/vendor/github.com/abiosoft/colima/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Abiola Ibrahim + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/abiosoft/colima/util/terminal/output.go b/vendor/github.com/abiosoft/colima/util/terminal/output.go new file mode 100644 index 000000000..657c106b8 --- /dev/null +++ b/vendor/github.com/abiosoft/colima/util/terminal/output.go @@ -0,0 +1,137 @@ +package terminal + +import ( + "bytes" + "fmt" + "io" + "os" + "strings" + "time" + + "github.com/fatih/color" + "golang.org/x/crypto/ssh/terminal" +) + +var _ io.WriteCloser = (*verboseWriter)(nil) + +type verboseWriter struct { + buf bytes.Buffer + lines []string + + lineHeight int + termWidth int + + lastUpdate time.Time +} + +// NewVerboseWriter creates a new verbose writer. +// A verbose writer pipes the input received to the stdout while tailing the specified lines. +// Calling `Close` when done is recommended to clear the last uncleared output. +func NewVerboseWriter(lineHeight int) io.WriteCloser { + return &verboseWriter{lineHeight: lineHeight} +} + +func (v *verboseWriter) Write(p []byte) (n int, err error) { + // if it's not a terminal, simply write to stdout + if !isTerminal { + return os.Stdout.Write(p) + } + + for i, c := range p { + if c != '\n' { + v.buf.WriteByte(c) + continue + } + + if err := v.refresh(); err != nil { + return i + 1, err + } + + } + return len(p), nil +} + +func (v *verboseWriter) printLineVerbose() { + line := v.sanitizeLine(v.buf.String()) + line = color.HiBlackString(line) + _, _ = fmt.Fprintln(os.Stderr, line) +} + +func (v *verboseWriter) refresh() error { + v.clearScreen() + v.addLine() + return v.printScreen() +} + +func (v *verboseWriter) addLine() { + defer v.buf.Truncate(0) + + // if height <=0, do not scroll + if v.lineHeight <= 0 { + v.printLineVerbose() + return + } + + if len(v.lines) >= v.lineHeight { + v.lines = v.lines[1:] + } + v.lines = append(v.lines, v.buf.String()) +} + +func (v *verboseWriter) Close() error { + if v.buf.Len() > 0 { + if err := v.refresh(); err != nil { + return err + } + } + + v.clearScreen() + return nil +} + +func (v verboseWriter) sanitizeLine(line string) string { + // remove logrus noises + if strings.HasPrefix(line, "time=") { + line = line[strings.Index(line, "msg="):] + } + + return "> " + line +} + +func (v *verboseWriter) printScreen() error { + if err := v.updateTerm(); err != nil { + return err + } + + for _, line := range v.lines { + line = v.sanitizeLine(line) + if len(line) > v.termWidth { + line = line[:v.termWidth] + } + line = color.HiBlackString(line) + fmt.Println(line) + } + return nil +} + +func (v verboseWriter) clearScreen() { + for range v.lines { + ClearLine() + } +} + +func (v *verboseWriter) updateTerm() error { + // no need to refresh so quickly + if time.Since(v.lastUpdate) < time.Second*2 { + return nil + } + v.lastUpdate = time.Now().UTC() + + w, _, err := terminal.GetSize(int(os.Stdout.Fd())) + if err != nil { + return fmt.Errorf("error getting terminal size: %w", err) + } + v.termWidth = w + + return nil +} diff --git a/vendor/github.com/abiosoft/colima/util/terminal/terminal.go b/vendor/github.com/abiosoft/colima/util/terminal/terminal.go new file mode 100644 index 000000000..d8d60a245 --- /dev/null +++ b/vendor/github.com/abiosoft/colima/util/terminal/terminal.go @@ -0,0 +1,18 @@ +package terminal + +import ( + "fmt" + "golang.org/x/crypto/ssh/terminal" + "os" +) + +// ClearLine clears the previous line of the terminal +var isTerminal = terminal.IsTerminal(int(os.Stdout.Fd())) + +func ClearLine() { + if !isTerminal { + return + } + + fmt.Print("\033[1A \033[2K \r") +} diff --git a/vendor/golang.org/x/crypto/ssh/terminal/terminal.go b/vendor/golang.org/x/crypto/ssh/terminal/terminal.go new file mode 100644 index 000000000..a4d1919a9 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/terminal.go @@ -0,0 +1,76 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package terminal provides support functions for dealing with terminals, as +// commonly found on UNIX systems. +// +// Deprecated: this package moved to golang.org/x/term. +package terminal + +import ( + "io" + + "golang.org/x/term" +) + +// EscapeCodes contains escape sequences that can be written to the terminal in +// order to achieve different styles of text. +type EscapeCodes = term.EscapeCodes + +// Terminal contains the state for running a VT100 terminal that is capable of +// reading lines of input. +type Terminal = term.Terminal + +// NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is +// a local terminal, that terminal must first have been put into raw mode. +// prompt is a string that is written at the start of each input line (i.e. +// "> "). +func NewTerminal(c io.ReadWriter, prompt string) *Terminal { + return term.NewTerminal(c, prompt) +} + +// ErrPasteIndicator may be returned from ReadLine as the error, in addition +// to valid line data. It indicates that bracketed paste mode is enabled and +// that the returned line consists only of pasted data. Programs may wish to +// interpret pasted data more literally than typed data. +var ErrPasteIndicator = term.ErrPasteIndicator + +// State contains the state of a terminal. +type State = term.State + +// IsTerminal returns whether the given file descriptor is a terminal. +func IsTerminal(fd int) bool { + return term.IsTerminal(fd) +} + +// ReadPassword reads a line of input from a terminal without local echo. This +// is commonly used for inputting passwords and other sensitive data. The slice +// returned does not include the \n. +func ReadPassword(fd int) ([]byte, error) { + return term.ReadPassword(fd) +} + +// MakeRaw puts the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +func MakeRaw(fd int) (*State, error) { + return term.MakeRaw(fd) +} + +// Restore restores the terminal connected to the given file descriptor to a +// previous state. +func Restore(fd int, oldState *State) error { + return term.Restore(fd, oldState) +} + +// GetState returns the current state of a terminal which may be useful to +// restore the terminal after a signal. +func GetState(fd int) (*State, error) { + return term.GetState(fd) +} + +// GetSize returns the dimensions of the given terminal. +func GetSize(fd int) (width, height int, err error) { + return term.GetSize(fd) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index d16be67a0..ac52d2839 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -13,6 +13,9 @@ github.com/Masterminds/semver # github.com/Netflix/go-expect v0.0.0-20200312175327-da48e75238e2 ## explicit github.com/Netflix/go-expect +# github.com/abiosoft/colima v0.3.2 +## explicit; go 1.17 +github.com/abiosoft/colima/util/terminal # github.com/agext/levenshtein v1.2.1 ## explicit github.com/agext/levenshtein @@ -260,6 +263,7 @@ golang.org/x/crypto/poly1305 golang.org/x/crypto/ssh golang.org/x/crypto/ssh/internal/bcrypt_pbkdf golang.org/x/crypto/ssh/knownhosts +golang.org/x/crypto/ssh/terminal # golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf ## explicit; go 1.17 golang.org/x/sys/cpu