diff --git a/changelog/pending/20221101--cli-display--improve-the-usability-of-the-interactive-dipslay-by-making-the-treetable-scrollable.yaml b/changelog/pending/20221101--cli-display--improve-the-usability-of-the-interactive-dipslay-by-making-the-treetable-scrollable.yaml new file mode 100644 index 000000000000..39a750741c28 --- /dev/null +++ b/changelog/pending/20221101--cli-display--improve-the-usability-of-the-interactive-dipslay-by-making-the-treetable-scrollable.yaml @@ -0,0 +1,4 @@ +changes: +- type: feat + scope: cli/display + description: Improve the usability of the interactive dipslay by making the treetable scrollable diff --git a/pkg/backend/display/jsonmessage.go b/pkg/backend/display/jsonmessage.go index b351c907a641..09f80480976b 100644 --- a/pkg/backend/display/jsonmessage.go +++ b/pkg/backend/display/jsonmessage.go @@ -140,14 +140,37 @@ type messageRenderer struct { nonInteractiveSpinner cmdutil.Spinner progressOutput chan<- Progress + closed <-chan bool // Cache of lines we've already printed. We don't print a progress message again if it hasn't // changed between the last time we printed and now. printedProgressCache map[string]Progress } +func newMessageRenderer(stdout io.Writer, op string, opts Options) progressRenderer { + progressOutput, closed := make(chan Progress), make(chan bool) + go func() { + ShowProgressOutput(progressOutput, stdout, false) + close(closed) + }() + + spinner, ticker := cmdutil.NewSpinnerAndTicker( + fmt.Sprintf("%s%s...", cmdutil.EmojiOr("✨ ", "@ "), op), + nil, opts.Color, 1 /*timesPerSecond*/) + ticker.Stop() + + return &messageRenderer{ + opts: opts, + progressOutput: progressOutput, + closed: closed, + printedProgressCache: make(map[string]Progress), + nonInteractiveSpinner: spinner, + } +} + func (r *messageRenderer) Close() error { close(r.progressOutput) + <-r.closed return nil } @@ -196,7 +219,7 @@ func (r *messageRenderer) println(display *ProgressDisplay, line string) { func (r *messageRenderer) tick(display *ProgressDisplay) { if r.isTerminal { - r.render(display) + r.render(display, false) } else { // Update the spinner to let the user know that that work is still happening. r.nonInteractiveSpinner.Tick() @@ -233,7 +256,7 @@ func (r *messageRenderer) renderRow(display *ProgressDisplay, func (r *messageRenderer) rowUpdated(display *ProgressDisplay, row Row) { if r.isTerminal { // if we're in a terminal, then refresh everything so that all our columns line up - r.render(display) + r.render(display, false) } else { // otherwise, just print out this single row. colorizedColumns := row.ColorizedColumns() @@ -246,7 +269,7 @@ func (r *messageRenderer) systemMessage(display *ProgressDisplay, payload engine if r.isTerminal { // if we're in a terminal, then refresh everything. The system events will come after // all the normal rows - r.render(display) + r.render(display, false) } else { // otherwise, in a non-terminal, just print out the actual event. r.writeSimpleMessage(renderStdoutColorEvent(payload, display.opts)) @@ -254,9 +277,12 @@ func (r *messageRenderer) systemMessage(display *ProgressDisplay, payload engine } func (r *messageRenderer) done(display *ProgressDisplay) { + if r.isTerminal { + r.render(display, false) + } } -func (r *messageRenderer) render(display *ProgressDisplay) { +func (r *messageRenderer) render(display *ProgressDisplay, done bool) { if !r.isTerminal || display.headerRow == nil { return } @@ -316,6 +342,10 @@ func (r *messageRenderer) render(display *ProgressDisplay) { systemID++ } } + + if done { + r.println(display, "") + } } // Ensure our stored dimension info is up to date. diff --git a/pkg/backend/display/options.go b/pkg/backend/display/options.go index bea8c466eade..2221675d8c1e 100644 --- a/pkg/backend/display/options.go +++ b/pkg/backend/display/options.go @@ -50,6 +50,7 @@ type Options struct { JSONDisplay bool // true if we should emit the entire diff as JSON. EventLogPath string // the path to the file to use for logging events, if any. Debug bool // true to enable debug output. + Stdin io.Reader // the reader to use for stdin. Defaults to os.Stdin if unset. Stdout io.Writer // the writer to use for stdout. Defaults to os.Stdout if unset. Stderr io.Writer // the writer to use for stderr. Defaults to os.Stderr if unset. SuppressTimings bool // true to suppress displaying timings of resource actions diff --git a/pkg/backend/display/progress.go b/pkg/backend/display/progress.go index 0c5d22d1d661..c3f452e7cb9d 100644 --- a/pkg/backend/display/progress.go +++ b/pkg/backend/display/progress.go @@ -19,7 +19,6 @@ import ( "bytes" "fmt" "io" - "math" "os" "sort" "strings" @@ -27,9 +26,6 @@ import ( "unicode" "unicode/utf8" - "github.com/moby/term" - "golang.org/x/crypto/ssh/terminal" - "github.com/pulumi/pulumi/pkg/v3/engine" "github.com/pulumi/pulumi/pkg/v3/resource/deploy" "github.com/pulumi/pulumi/sdk/v3/go/common/apitype" @@ -228,45 +224,26 @@ func getEventUrnAndMetadata(event engine.Event) (resource.URN, *engine.StepEvent func ShowProgressEvents(op string, action apitype.UpdateKind, stack tokens.Name, proj tokens.PackageName, events <-chan engine.Event, done chan<- bool, opts Options, isPreview bool) { + stdin := opts.Stdin + if stdin == nil { + stdin = os.Stdin + } stdout := opts.Stdout if stdout == nil { stdout = os.Stdout } - stderr := opts.Stderr - if stderr == nil { - stderr = os.Stderr - } - - // Create a ticker that will update all our status messages once a second. Any - // in-flight resources will get a varying . .. ... ticker appended to them to - // let the user know what is still being worked on. - var spinner cmdutil.Spinner - var ticker *time.Ticker - if stdout == os.Stdout && stderr == os.Stderr { - spinner, ticker = cmdutil.NewSpinnerAndTicker( - fmt.Sprintf("%s%s...", cmdutil.EmojiOr("✨ ", "@ "), op), - nil, opts.Color, 1 /*timesPerSecond*/) - } else { - spinner = &nopSpinner{} - ticker = time.NewTicker(math.MaxInt64) - } - - // The channel we push progress messages into, and which ShowProgressOutput pulls - // from to display to the console. - progressOutput := make(chan Progress) - - opStopwatch := newOpStopwatch() - renderer := &messageRenderer{ - opts: opts, - progressOutput: progressOutput, - printedProgressCache: make(map[string]Progress), - nonInteractiveSpinner: spinner, + isTerminal := true + renderer, err := newTreeRenderer(stdin, stdout, opts) + if err != nil { + fmt.Println(err) + isTerminal, renderer = false, newMessageRenderer(stdout, op, opts) } display := &ProgressDisplay{ action: action, isPreview: isPreview, + isTerminal: isTerminal, opts: opts, renderer: renderer, stack: stack, @@ -277,39 +254,12 @@ func ShowProgressEvents(op string, action apitype.UpdateKind, stack tokens.Name, urnToID: make(map[resource.URN]string), colorizedToUncolorized: make(map[string]string), displayOrderCounter: 1, - opStopwatch: opStopwatch, + opStopwatch: newOpStopwatch(), } - // Assume we are not displaying in a terminal by default. - renderer.isTerminal = false - if stdout == os.Stdout { - terminalWidth, terminalHeight, err := terminal.GetSize(int(os.Stdout.Fd())) - if err == nil { - // If the terminal has a size, use it. - renderer.isTerminal = opts.IsInteractive - renderer.terminalWidth = terminalWidth - renderer.terminalHeight = terminalHeight - - // Don't bother attempting to treat this display as a terminal if it has no width/height. - if renderer.isTerminal && (renderer.terminalWidth == 0 || renderer.terminalHeight == 0) { - renderer.isTerminal = false - _, err = fmt.Fprintln(stderr, "Treating display as non-terminal due to 0 width/height.") - contract.IgnoreError(err) - } - - // Fetch the canonical stdout stream, configured appropriately. - _, stdout, _ = term.StdStreams() - } - } - display.isTerminal = renderer.isTerminal - - go func() { - display.processEvents(ticker, events) - contract.IgnoreClose(display.renderer) - }() - - ShowProgressOutput(progressOutput, stdout, display.isTerminal) - + ticker := time.NewTicker(1 * time.Second) + display.processEvents(ticker, events) + contract.IgnoreClose(display.renderer) ticker.Stop() // let our caller know we're done. diff --git a/pkg/backend/display/tree.go b/pkg/backend/display/tree.go new file mode 100644 index 000000000000..e8c37aa268f8 --- /dev/null +++ b/pkg/backend/display/tree.go @@ -0,0 +1,417 @@ +// Copyright 2016-2022, Pulumi Corporation. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// nolint: goconst +package display + +import ( + "errors" + "fmt" + "io" + "os" + "strings" + "sync" + "syscall" + "time" + "unicode/utf8" + + terminal "golang.org/x/term" + + gotty "github.com/ijc/Gotty" + "github.com/muesli/cancelreader" + "github.com/pulumi/pulumi/pkg/v3/engine" + "github.com/pulumi/pulumi/sdk/v3/go/common/diag/colors" + "github.com/pulumi/pulumi/sdk/v3/go/common/util/contract" +) + +type treeRenderer struct { + m sync.Mutex + + opts Options + + // The file descriptor, width, and height of the terminal. Used so we can trim resource messages that are too long. + termFD int + termInfo termInfo + termWidth int + termHeight int + termState *terminal.State + term io.Writer + + inFile cancelreader.CancelReader + + dirty bool // True if the display has changed since the last redraw. + rewind int // The number of lines we need to rewind to redraw the entire screen. + + treeTableRows []string + systemMessages []string + + ticker *time.Ticker + keys chan string + closed chan bool + + treeTableOffset int // The scroll offset into the tree table. + maxTreeTableOffset int // The maximum scroll offset. +} + +type fileLike interface { + Fd() uintptr +} + +func newTreeRenderer(in io.Reader, out io.Writer, opts Options) (progressRenderer, error) { + if !opts.IsInteractive { + return nil, fmt.Errorf("the tree display can only be used in interactive mode") + } + + outFile, ok := out.(fileLike) + if !ok { + return nil, fmt.Errorf("stdout must be a terminal") + } + outFD := int(outFile.Fd()) + + inFile, err := cancelreader.NewReader(in) + if err != nil { + return nil, fmt.Errorf("preparing stdin: %w", err) + } + + width, height, err := terminal.GetSize(outFD) + if err != nil { + return nil, fmt.Errorf("getting terminal dimensions: %w", err) + } + if width == 0 || height == 0 { + return nil, fmt.Errorf("terminal has unusable dimensions %v x %v", width, height) + } + + termType := os.Getenv("TERM") + if termType == "" { + termType = "vt102" + } + var info termInfo + if info, err = gotty.OpenTermInfo(termType); err != nil { + info = &noTermInfo{} + } + + state, err := terminal.MakeRaw(outFD) + if err != nil { + return nil, fmt.Errorf("enabling raw terminal: %w", err) + } + + r := &treeRenderer{ + opts: opts, + termFD: outFD, + termInfo: info, + termWidth: width, + termHeight: height, + termState: state, + term: out, + inFile: inFile, + ticker: time.NewTicker(16 * time.Millisecond), + keys: make(chan string), + closed: make(chan bool), + } + go r.handleEvents() + go r.pollInput() + return r, nil +} + +func (r *treeRenderer) Close() error { + r.inFile.Cancel() + return terminal.Restore(r.termFD, r.termState) +} + +func (r *treeRenderer) tick(display *ProgressDisplay) { + r.render(display) +} + +func (r *treeRenderer) rowUpdated(display *ProgressDisplay, _ Row) { + r.render(display) +} + +func (r *treeRenderer) systemMessage(display *ProgressDisplay, _ engine.StdoutEventPayload) { + r.render(display) +} + +func (r *treeRenderer) done(display *ProgressDisplay) { + r.render(display) + + r.ticker.Stop() + r.closed <- true + close(r.closed) + + r.frame(true) +} + +func (r *treeRenderer) println(display *ProgressDisplay, text string) { + _, err := fmt.Fprint(r.term, r.opts.Color.Colorize(strings.ReplaceAll(text, "\n", "\r\n"))) + contract.IgnoreError(err) + _, err = fmt.Fprint(r.term, "\r\n") + contract.IgnoreError(err) +} + +func (r *treeRenderer) render(display *ProgressDisplay) { + r.m.Lock() + defer r.m.Unlock() + + if display.headerRow == nil { + return + } + + // Render the resource tree table into rows. + rootNodes := display.generateTreeNodes() + rootNodes = display.filterOutUnnecessaryNodesAndSetDisplayTimes(rootNodes) + sortNodes(rootNodes) + display.addIndentations(rootNodes, true /*isRoot*/, "") + + maxSuffixLength := 0 + for _, v := range display.suffixesArray { + runeCount := utf8.RuneCountInString(v) + if runeCount > maxSuffixLength { + maxSuffixLength = runeCount + } + } + + var treeTableRows [][]string + var maxColumnLengths []int + display.convertNodesToRows(rootNodes, maxSuffixLength, &treeTableRows, &maxColumnLengths) + removeInfoColumnIfUnneeded(treeTableRows) + + r.treeTableRows = r.treeTableRows[:0] + for _, row := range treeTableRows { + r.treeTableRows = append(r.treeTableRows, r.renderRow(display, row, maxColumnLengths)) + } + + // Convert system events into lines. + r.systemMessages = r.systemMessages[:0] + for _, payload := range display.systemEventPayloads { + msg := payload.Color.Colorize(payload.Message) + r.systemMessages = append(r.systemMessages, splitIntoDisplayableLines(msg)...) + } + + r.dirty = true +} + +func (r *treeRenderer) markDirty() { + r.m.Lock() + defer r.m.Unlock() + + r.dirty = true +} + +// +--------------------------------------------+ +// | treetable header | +// | treetable contents... | +// +--------------------------------------------+ +func (r *treeRenderer) frame(done bool) { + r.m.Lock() + defer r.m.Unlock() + + if !done && !r.dirty { + return + } + r.dirty = false + + // Make sure our stored dimension info is up to date + r.updateTerminalDimensions() + + treeTableRows := r.treeTableRows + systemMessages := r.systemMessages + + var treeTableHeight int + var treeTableHeader string + if len(r.treeTableRows) > 0 { + treeTableHeader, treeTableRows = treeTableRows[0], treeTableRows[1:] + treeTableHeight = 1 + len(treeTableRows) + } + + systemMessagesHeight := len(systemMessages) + if len(systemMessages) > 0 { + systemMessagesHeight += 3 // Account for padding + title + } + + // Layout the display. The extra '1' accounts for the fact that we terminate each line with a newline. + totalHeight := treeTableHeight + systemMessagesHeight + 1 + r.maxTreeTableOffset = 0 + + // If this is not the final frame and the terminal is not large enough to show the entire display: + // - If there are no system messages, devote the entire display to the tree table + // - If there are system messages, devote the first two thirds of the display to the tree table and the + // last third to the system messages + if !done && totalHeight >= r.termHeight { + if systemMessagesHeight > 0 { + systemMessagesHeight = r.termHeight / 3 + if systemMessagesHeight <= 3 { + systemMessagesHeight = 0 + } else { + systemMessagesContentHeight := systemMessagesHeight - 3 + if len(systemMessages) > systemMessagesContentHeight { + systemMessages = systemMessages[len(systemMessages)-systemMessagesContentHeight:] + } + } + } + + treeTableHeight = r.termHeight - systemMessagesHeight - 1 + r.maxTreeTableOffset = len(treeTableRows) - treeTableHeight - 1 + + treeTableRows = treeTableRows[r.treeTableOffset : r.treeTableOffset+treeTableHeight-1] + + totalHeight = treeTableHeight + systemMessagesHeight + 1 + } + + // Re-home the cursor. + for ; r.rewind > 0; r.rewind-- { + cursorUp(r.term, r.termInfo, 1) + clearLine(r.term, r.termInfo) + } + r.rewind = totalHeight - 1 + + // Render the tree table. + r.println(nil, treeTableHeader) + for _, row := range treeTableRows { + r.println(nil, row) + } + + // Render the system messages. + if systemMessagesHeight > 0 { + r.println(nil, "") + r.println(nil, colors.Yellow+"System Messages"+colors.Reset) + + for _, line := range systemMessages { + r.println(nil, " "+line) + } + } + + if done && totalHeight > 0 { + r.println(nil, "") + } +} + +func (r *treeRenderer) renderRow(display *ProgressDisplay, colorizedColumns []string, maxColumnLengths []int) string { + uncolorizedColumns := display.uncolorizeColumns(colorizedColumns) + row := renderRow(colorizedColumns, uncolorizedColumns, maxColumnLengths) + + // Ensure we don't go past the end of the terminal. Note: this is made complex due to + // msgWithColors having the color code information embedded with it. So we need to get + // the right substring of it, assuming that embedded colors are just markup and do not + // actually contribute to the length + maxRowLength := r.termWidth - 1 + if maxRowLength < 0 { + maxRowLength = 0 + } + return colors.TrimColorizedString(row, maxRowLength) +} + +// Ensure our stored dimension info is up to date. +func (r *treeRenderer) updateTerminalDimensions() { + currentTermWidth, currentTermHeight, err := terminal.GetSize(r.termFD) + contract.IgnoreError(err) + + if currentTermWidth != r.termWidth || + currentTermHeight != r.termHeight { + r.termWidth = currentTermWidth + r.termHeight = currentTermHeight + } +} + +func (r *treeRenderer) handleEvents() { + for { + select { + case <-r.ticker.C: + r.frame(false) + case key := <-r.keys: + switch key { + case "ctrl+c": + err := syscall.Kill(syscall.Getpid(), syscall.SIGINT) + contract.IgnoreError(err) + case "up": + if r.treeTableOffset > 0 { + r.treeTableOffset-- + } + r.markDirty() + case "down": + if r.treeTableOffset < r.maxTreeTableOffset { + r.treeTableOffset++ + } + r.markDirty() + } + case <-r.closed: + return + } + } +} + +func (r *treeRenderer) pollInput() { + for { + key, err := readKey(r.inFile) + if err == nil { + r.keys <- key + } else if errors.Is(err, cancelreader.ErrCanceled) || errors.Is(err, io.EOF) { + close(r.keys) + return + } + } +} + +func readKey(r io.Reader) (string, error) { + type stateFunc func(b byte) (stateFunc, string) + + var stateIntermediate stateFunc + stateIntermediate = func(b byte) (stateFunc, string) { + if b >= 0x20 && b < 0x30 { + return stateIntermediate, "" + } + switch b { + case 'A': + return nil, "up" + case 'B': + return nil, "down" + default: + return nil, "" + } + } + var stateParameter stateFunc + stateParameter = func(b byte) (stateFunc, string) { + if b >= 0x30 && b < 0x40 { + return stateParameter, "" + } + return stateIntermediate(b) + } + stateBracket := func(b byte) (stateFunc, string) { + if b == '[' { + return stateParameter, "" + } + return nil, "" + } + stateEscape := func(b byte) (stateFunc, string) { + if b == 0x1b { + return stateBracket, "" + } + if b == 3 { + return nil, "ctrl+c" + } + return nil, string([]byte{b}) + } + + state := stateEscape + for { + var b [1]byte + if _, err := r.Read(b[:]); err != nil { + return "", err + } + + next, key := state(b[0]) + if next == nil { + return key, nil + } + state = next + } +} diff --git a/pkg/go.mod b/pkg/go.mod index a54fd0661dac..ec22ad5f84c0 100644 --- a/pkg/go.mod +++ b/pkg/go.mod @@ -65,11 +65,13 @@ require ( github.com/edsrzf/mmap-go v1.1.0 github.com/go-git/go-git/v5 v5.4.2 github.com/hexops/gotextdiff v1.0.3 + github.com/muesli/cancelreader v0.2.2 github.com/natefinch/atomic v1.0.1 github.com/pulumi/pulumi-java/pkg v0.6.0 github.com/pulumi/pulumi-yaml v0.5.10 github.com/segmentio/encoding v0.3.5 github.com/shirou/gopsutil/v3 v3.22.3 + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 ) require ( @@ -195,7 +197,6 @@ require ( go.opencensus.io v0.23.0 // indirect go.uber.org/atomic v1.9.0 // indirect golang.org/x/sys v0.0.0-20220823224334-20c2bfdbfe24 // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect diff --git a/pkg/go.sum b/pkg/go.sum index d600f16c0094..5fba65d09523 100644 --- a/pkg/go.sum +++ b/pkg/go.sum @@ -1279,6 +1279,8 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJ github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= diff --git a/tests/go.mod b/tests/go.mod index b75512959bf9..7ae1557a614f 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -11,7 +11,7 @@ replace ( require ( github.com/blang/semver v3.5.1+incompatible github.com/golang/protobuf v1.5.2 - github.com/pulumi/pulumi/pkg/v3 v3.34.1 + github.com/pulumi/pulumi/pkg/v3 v3.42.1-0.20221010121757-adef574983fa github.com/pulumi/pulumi/sdk/v3 v3.45.0 github.com/stretchr/testify v1.8.0 google.golang.org/grpc v1.49.0 @@ -30,7 +30,6 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.4.1 // indirect - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.28 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.21 // indirect @@ -124,7 +123,7 @@ require ( github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect github.com/mitchellh/reflectwalk v1.0.0 // indirect - github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect github.com/natefinch/atomic v1.0.1 // indirect github.com/opentracing/basictracer-go v1.1.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect diff --git a/tests/go.sum b/tests/go.sum index c0f9760bf747..cc6b1e6ad7be 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -134,7 +134,6 @@ github.com/Azure/go-amqp v0.17.0/go.mod h1:9YJ3RhxRT1gquYnzpZO1vcYMMpAdJT+QEg6fw github.com/Azure/go-amqp v0.17.5/go.mod h1:9YJ3RhxRT1gquYnzpZO1vcYMMpAdJT+QEg6fwmw9Zlg= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= @@ -1252,7 +1251,6 @@ github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGq github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -1264,6 +1262,8 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJ github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -2489,10 +2489,8 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=