diff --git a/go.mod b/go.mod index f7c6705c02e..951428f40e5 100644 --- a/go.mod +++ b/go.mod @@ -6,11 +6,13 @@ require ( github.com/adrg/xdg v0.2.1 github.com/anchore/go-testutils v0.0.0-20200624184116-66aa578126db github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b - github.com/anchore/stereoscope v0.0.0-20200706164556-7cf39d7f4639 + github.com/anchore/stereoscope v0.0.0-20200803190343-146f38f8cc19 github.com/bmatcuk/doublestar v1.3.1 + github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe // indirect + github.com/dustin/go-humanize v1.0.0 github.com/go-test/deep v1.0.6 github.com/google/go-containerregistry v0.1.1 // indirect - github.com/gookit/color v1.2.5 + github.com/gookit/color v1.2.7 github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 // indirect github.com/hashicorp/go-multierror v1.1.0 github.com/hashicorp/go-version v1.2.0 @@ -27,7 +29,6 @@ require ( github.com/wagoodman/go-rpmdb v0.0.0-20200719223757-ce54a4b0607b github.com/wagoodman/jotframe v0.0.0-20200730190914-3517092dd163 github.com/x-cray/logrus-prefixed-formatter v0.5.2 - go.uber.org/zap v1.15.0 golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 golang.org/x/sys v0.0.0-20200610111108-226ff32320da // indirect google.golang.org/genproto v0.0.0-20200615140333-fd031eab31e7 // indirect diff --git a/go.sum b/go.sum index bc8876be47f..9639dfc1be0 100644 --- a/go.sum +++ b/go.sum @@ -129,8 +129,8 @@ github.com/anchore/go-testutils v0.0.0-20200624184116-66aa578126db/go.mod h1:D3r github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZVsCYMrIZBpFxwV26CbsuoEh5muXD5I1Ods= github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= github.com/anchore/stereoscope v0.0.0-20200520221116-025e07f1c93e/go.mod h1:bkyLl5VITnrmgErv4S1vDfVz/TGAZ5il6161IQo7w2g= -github.com/anchore/stereoscope v0.0.0-20200706164556-7cf39d7f4639 h1:J1oytkj+aBuACNF2whtEiVxRXIZ8zwT+EiPTqm/FvwA= -github.com/anchore/stereoscope v0.0.0-20200706164556-7cf39d7f4639/go.mod h1:WntReQTI/I27FOQ87UgLVVzWgku6+ZsqfOTLxpIZFCs= +github.com/anchore/stereoscope v0.0.0-20200803190343-146f38f8cc19 h1:iJ6du/cA9KJ0ctP905u2zCcpJubsy6kxLZBvG4XG+uY= +github.com/anchore/stereoscope v0.0.0-20200803190343-146f38f8cc19/go.mod h1:WntReQTI/I27FOQ87UgLVVzWgku6+ZsqfOTLxpIZFCs= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/apex/log v1.1.4/go.mod h1:AlpoD9aScyQfJDVHmLMEcx4oU6LqzkWp4Mg9GdAcEvQ= github.com/apex/log v1.3.0 h1:1fyfbPvUwD10nMoh3hY6MXzvZShJQn9/ck7ATgAt5pA= @@ -191,8 +191,9 @@ github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMX github.com/containerd/containerd v1.3.4 h1:3o0smo5SKY7H6AJCmJhsnCjR2/V2T8VmiHt7seN2/kI= github.com/containerd/containerd v1.3.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20200413184840-d3ef23f19fbb h1:nXPkFq8X1a9ycY3GYQpFNxHh3j2JgY7zDZfq2EXMIzk= github.com/containerd/continuity v0.0.0-20200413184840-d3ef23f19fbb/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY= +github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe h1:PEmIrUvwG9Yyv+0WKZqjXfSFDeZjs/q15g0m08BYS9k= +github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= @@ -438,6 +439,8 @@ github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTV github.com/gookit/color v1.2.4/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg= github.com/gookit/color v1.2.5 h1:s1gzb/fg3HhkSLKyWVUsZcVBUo+R1TwEYTmmxH8gGFg= github.com/gookit/color v1.2.5/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg= +github.com/gookit/color v1.2.7 h1:4qePMNWZhrmbfYJDix+J4V2l0iVW+6jQGjicELlN14E= +github.com/gookit/color v1.2.7/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw= @@ -495,6 +498,7 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= @@ -642,12 +646,14 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -851,16 +857,8 @@ go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= -go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= gocloud.dev v0.19.0 h1:EDRyaRAnMGSq/QBto486gWFxMLczAfIYUmusV7XLNBM= gocloud.dev v0.19.0/go.mod h1:SmKwiR8YwIMMJvQBKLsC3fHNyMwXLw3PMDO+VVteJMI= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1071,8 +1069,6 @@ golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1206,6 +1202,7 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8X gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= @@ -1219,6 +1216,7 @@ gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= diff --git a/internal/ui/etui/ephemeral_tui.go b/internal/ui/etui/ephemeral_tui.go index afb00c68882..18972d5b446 100644 --- a/internal/ui/etui/ephemeral_tui.go +++ b/internal/ui/etui/ephemeral_tui.go @@ -63,6 +63,10 @@ func OutputToEphemeralTUI(workerErrs <-chan error, subscription *partybus.Subscr // flush any errors to the screen before the report fmt.Fprint(output, logBuffer.String()) } + logWrapper, ok := log.Log.(*logger.LogrusLogger) + if ok { + logWrapper.Logger.SetOutput(output) + } }() var err error diff --git a/ui/event_handlers.go b/ui/event_handlers.go index 32f726e4ae3..ccba3ba60d7 100644 --- a/ui/event_handlers.go +++ b/ui/event_handlers.go @@ -4,9 +4,13 @@ import ( "context" "fmt" "io" + "strings" "sync" "time" + "github.com/anchore/stereoscope/pkg/image/docker" + "github.com/dustin/go-humanize" + stereoEventParsers "github.com/anchore/stereoscope/pkg/event/parsers" "github.com/anchore/syft/internal/ui/common" syftEventParsers "github.com/anchore/syft/syft/event/parsers" @@ -22,8 +26,15 @@ const statusSet = common.SpinnerDotSet // SpinnerCircleOutlineSet const completedStatus = "✔" // "●" const tileFormat = color.Bold const statusTitleTemplate = " %s %-28s " +const interval = 150 * time.Millisecond -var auxInfoFormat = color.HEX("#777777") +var ( + auxInfoFormat = color.HEX("#777777") + dockerPullCompletedColor = color.HEX("#fcba03") + dockerPullDownloadColor = color.HEX("#777777") + dockerPullExtractColor = color.White + dockerPullStageChars = strings.Split("▁▃▄▅▆▇█", "") +) func startProcess() (format.Simple, *common.Spinner) { width, _ := frame.GetTerminalSize() @@ -37,10 +48,137 @@ func startProcess() (format.Simple, *common.Spinner) { return formatter, &spinner } +func formatDockerPullPhase(phase docker.PullPhase, inputStr string) string { + switch phase { + case docker.WaitingPhase: + // ignore any progress related to waiting + return " " + case docker.PullingFsPhase, docker.DownloadingPhase: + return dockerPullDownloadColor.Sprint(inputStr) + case docker.DownloadCompletePhase: + return dockerPullDownloadColor.Sprint(dockerPullStageChars[len(dockerPullStageChars)-1]) + case docker.ExtractingPhase: + return dockerPullExtractColor.Sprint(inputStr) + case docker.VerifyingChecksumPhase, docker.PullCompletePhase: + return dockerPullCompletedColor.Sprint(inputStr) + case docker.AlreadyExistsPhase: + return dockerPullCompletedColor.Sprint(dockerPullStageChars[len(dockerPullStageChars)-1]) + default: + return inputStr + } +} + +// nolint:funlen +func formatDockerImagePullStatus(pullStatus *docker.PullStatus, spinner *common.Spinner, line *frame.Line) { + var size, current uint64 + + title := tileFormat.Sprint("Pulling image") + + layers := pullStatus.Layers() + status := make(map[docker.LayerID]docker.LayerState) + completed := make([]string, len(layers)) + + // fetch the current state + for idx, layer := range layers { + completed[idx] = " " + status[layer] = pullStatus.Current(layer) + } + + numCompleted := 0 + for idx, layer := range layers { + prog := status[layer].PhaseProgress + current := prog.Current() + size := prog.Size() + + if progress.IsCompleted(prog) { + input := dockerPullStageChars[len(dockerPullStageChars)-1] + completed[idx] = formatDockerPullPhase(status[layer].Phase, input) + } else if current != 0 { + var ratio float64 + switch { + case current == 0 || size < 0: + ratio = 0 + case current >= size: + ratio = 1 + default: + ratio = float64(current) / float64(size) + } + + i := int(ratio * float64(len(dockerPullStageChars)-1)) + input := dockerPullStageChars[i] + completed[idx] = formatDockerPullPhase(status[layer].Phase, input) + } + + if progress.IsErrCompleted(status[layer].DownloadProgress.Error()) { + numCompleted++ + } + } + + for _, layer := range layers { + prog := status[layer].DownloadProgress + size += uint64(prog.Size()) + current += uint64(prog.Current()) + } + + var progStr, auxInfo string + if len(layers) > 0 { + render := strings.Join(completed, "") + prefix := dockerPullCompletedColor.Sprintf("%d Layers", len(layers)) + auxInfo = auxInfoFormat.Sprintf("[%s / %s]", humanize.Bytes(current), humanize.Bytes(size)) + if len(layers) == numCompleted { + auxInfo = auxInfoFormat.Sprintf("[%s] Extracting...", humanize.Bytes(size)) + } + + progStr = fmt.Sprintf("%s▕%s▏", prefix, render) + } + + spin := color.Magenta.Sprint(spinner.Next()) + _, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate+"%s%s", spin, title, progStr, auxInfo)) +} + +func PullDockerImageHandler(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error { + _, pullStatus, err := stereoEventParsers.ParsePullDockerImage(event) + if err != nil { + return fmt.Errorf("bad %s event: %w", event.Type, err) + } + + line, err := fr.Append() + if err != nil { + return err + } + wg.Add(1) + + _, spinner := startProcess() + + go func() { + defer wg.Done() + + loop: + for { + select { + case <-ctx.Done(): + break loop + case <-time.After(interval): + formatDockerImagePullStatus(pullStatus, spinner, line) + if pullStatus.Complete() { + break loop + } + } + } + + if pullStatus.Complete() { + spin := color.Green.Sprint(completedStatus) + title := tileFormat.Sprint("Pulled image") + _, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate, spin, title)) + } + }() + return err +} + func FetchImageHandler(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error { _, prog, err := stereoEventParsers.ParseFetchImage(event) if err != nil { - return fmt.Errorf("bad FetchImage event: %w", err) + return fmt.Errorf("bad %s event: %w", event.Type, err) } line, err := fr.Append() @@ -50,8 +188,8 @@ func FetchImageHandler(ctx context.Context, fr *frame.Frame, event partybus.Even wg.Add(1) formatter, spinner := startProcess() - stream := progress.Stream(ctx, prog, 150*time.Millisecond) - title := tileFormat.Sprint("Fetching image...") + stream := progress.Stream(ctx, prog, interval) + title := tileFormat.Sprint("Fetching image") formatFn := func(p progress.Progress) { progStr, err := formatter.Format(p) @@ -82,7 +220,7 @@ func FetchImageHandler(ctx context.Context, fr *frame.Frame, event partybus.Even func ReadImageHandler(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error { _, prog, err := stereoEventParsers.ParseReadImage(event) if err != nil { - return fmt.Errorf("bad ReadImage event: %w", err) + return fmt.Errorf("bad %s event: %w", event.Type, err) } line, err := fr.Append() @@ -93,8 +231,8 @@ func ReadImageHandler(ctx context.Context, fr *frame.Frame, event partybus.Event wg.Add(1) formatter, spinner := startProcess() - stream := progress.Stream(ctx, prog, 150*time.Millisecond) - title := tileFormat.Sprint("Reading image...") + stream := progress.Stream(ctx, prog, interval) + title := tileFormat.Sprint("Reading image") formatFn := func(p progress.Progress) { progStr, err := formatter.Format(p) @@ -125,7 +263,7 @@ func ReadImageHandler(ctx context.Context, fr *frame.Frame, event partybus.Event func CatalogerStartedHandler(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error { monitor, err := syftEventParsers.ParseCatalogerStarted(event) if err != nil { - return fmt.Errorf("bad CatalogerStarted event: %w", err) + return fmt.Errorf("bad %s event: %w", event.Type, err) } line, err := fr.Append() @@ -136,8 +274,8 @@ func CatalogerStartedHandler(ctx context.Context, fr *frame.Frame, event partybu wg.Add(1) _, spinner := startProcess() - stream := progress.StreamMonitors(ctx, []progress.Monitorable{monitor.FilesProcessed, monitor.PackagesDiscovered}, 50*time.Millisecond) - title := tileFormat.Sprint("Cataloging image...") + stream := progress.StreamMonitors(ctx, []progress.Monitorable{monitor.FilesProcessed, monitor.PackagesDiscovered}, interval) + title := tileFormat.Sprint("Cataloging image") formatFn := func(p int64) { spin := color.Magenta.Sprint(spinner.Next()) diff --git a/ui/handler.go b/ui/handler.go index 62a8a9a4b51..105b3b0eeb4 100644 --- a/ui/handler.go +++ b/ui/handler.go @@ -19,7 +19,7 @@ func NewHandler() *Handler { func (r *Handler) RespondsTo(event partybus.Event) bool { switch event.Type { - case stereoscopeEvent.ReadImage, stereoscopeEvent.FetchImage, syftEvent.CatalogerStarted: + case stereoscopeEvent.PullDockerImage, stereoscopeEvent.ReadImage, stereoscopeEvent.FetchImage, syftEvent.CatalogerStarted: return true default: return false @@ -28,6 +28,9 @@ func (r *Handler) RespondsTo(event partybus.Event) bool { func (r *Handler) Handle(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error { switch event.Type { + case stereoscopeEvent.PullDockerImage: + return PullDockerImageHandler(ctx, fr, event, wg) + case stereoscopeEvent.ReadImage: return ReadImageHandler(ctx, fr, event, wg)