Skip to content

Commit

Permalink
Merge pull request #323 from cloudskiff/fea/progress
Browse files Browse the repository at this point in the history
Show progress while reading resources from provider
  • Loading branch information
eliecharra authored Mar 16, 2021
2 parents 23c4fcf + 28d3a6d commit 98d7da2
Show file tree
Hide file tree
Showing 16 changed files with 247 additions and 29 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ deps:
.PHONY: install-tools
install-tools:
$(GOGET) gotest.tools/gotestsum
$(GOGET) github.com/vektra/mockery/.../
$(GOGET) github.com/vektra/mockery/v2/.../


go.mod: FORCE
Expand Down
18 changes: 16 additions & 2 deletions pkg/cmd/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/cloudskiff/driftctl/pkg/iac/config"
"github.com/cloudskiff/driftctl/pkg/iac/supplier"
"github.com/cloudskiff/driftctl/pkg/iac/terraform/state/backend"
globaloutput "github.com/cloudskiff/driftctl/pkg/output"
"github.com/cloudskiff/driftctl/pkg/remote"
"github.com/cloudskiff/driftctl/pkg/resource"
"github.com/cloudskiff/driftctl/pkg/terraform"
Expand All @@ -32,6 +33,7 @@ type ScanOptions struct {
To string
Output output.OutputConfig
Filter *jmespath.JMESPath
Quiet bool
}

func NewScanCmd() *cobra.Command {
Expand Down Expand Up @@ -77,6 +79,8 @@ func NewScanCmd() *cobra.Command {
opts.Filter = expr
}

opts.Quiet, _ = cmd.Flags().GetBool("quiet")

return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
Expand All @@ -85,6 +89,12 @@ func NewScanCmd() *cobra.Command {
}

fl := cmd.Flags()
fl.BoolP(
"quiet",
"",
false,
"Do not display anything but scan results",
)
fl.StringP(
"filter",
"",
Expand Down Expand Up @@ -123,7 +133,7 @@ func NewScanCmd() *cobra.Command {
}

func scanRun(opts *ScanOptions) error {
selectedOutput := output.GetOutput(opts.Output)
selectedOutput := output.GetOutput(opts.Output, opts.Quiet)

c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
Expand All @@ -132,7 +142,9 @@ func scanRun(opts *ScanOptions) error {
providerLibrary := terraform.NewProviderLibrary()
supplierLibrary := resource.NewSupplierLibrary()

err := remote.Activate(opts.To, alerter, providerLibrary, supplierLibrary)
progress := globaloutput.NewProgress()

err := remote.Activate(opts.To, alerter, providerLibrary, supplierLibrary, progress)
if err != nil {
return err
}
Expand All @@ -158,7 +170,9 @@ func scanRun(opts *ScanOptions) error {
ctl.Stop()
}()

progress.Start()
analysis, err := ctl.Run()
progress.Stop()

if err != nil {
return err
Expand Down
10 changes: 7 additions & 3 deletions pkg/cmd/scan/output/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ func IsSupported(key string) bool {
return false
}

func GetOutput(config OutputConfig) Output {
output.ChangePrinter(GetPrinter(config))
func GetOutput(config OutputConfig, quiet bool) Output {
output.ChangePrinter(GetPrinter(config, quiet))

switch config.Key {
case JSONOutputType:
Expand All @@ -60,7 +60,11 @@ func GetOutput(config OutputConfig) Output {
}
}

func GetPrinter(config OutputConfig) output.Printer {
func GetPrinter(config OutputConfig, quiet bool) output.Printer {
if quiet {
return &output.VoidPrinter{}
}

switch config.Key {
case JSONOutputType:
if isStdOut(config.Options["path"]) {
Expand Down
25 changes: 20 additions & 5 deletions pkg/cmd/scan/output/output_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,17 +267,25 @@ func fakeAnalysisWithGithubEnumerationError() *analyser.Analysis {

func TestGetPrinter(t *testing.T) {
tests := []struct {
name string
path string
key string
want output.Printer
name string
path string
key string
quiet bool
want output.Printer
}{
{
name: "json file output",
path: "/path/to/file",
key: JSONOutputType,
want: output.NewConsolePrinter(),
},
{
name: "json file output quiet",
path: "/path/to/file",
key: JSONOutputType,
quiet: true,
want: &output.VoidPrinter{},
},
{
name: "json stdout output",
path: "stdout",
Expand All @@ -296,6 +304,13 @@ func TestGetPrinter(t *testing.T) {
key: ConsoleOutputType,
want: output.NewConsolePrinter(),
},
{
name: "quiet console stdout output",
path: "stdout",
quiet: true,
key: ConsoleOutputType,
want: &output.VoidPrinter{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -304,7 +319,7 @@ func TestGetPrinter(t *testing.T) {
Options: map[string]string{
"path": tt.path,
},
}); !reflect.DeepEqual(got, tt.want) {
}, tt.quiet); !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetPrinter() = %v, want %v", got, tt.want)
}
})
Expand Down
10 changes: 8 additions & 2 deletions pkg/iac/terraform/state/terraform_state_reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"strings"
"testing"

"github.com/cloudskiff/driftctl/pkg/output"

"github.com/cloudskiff/driftctl/pkg/iac"
"github.com/cloudskiff/driftctl/pkg/iac/config"
"github.com/cloudskiff/driftctl/pkg/remote/aws"
Expand Down Expand Up @@ -96,7 +98,9 @@ func TestTerraformStateReader_AWS_Resources(t *testing.T) {

if shouldUpdate {
var err error
realProvider, err = aws.NewAWSTerraformProvider()
progress := &output.MockProgress{}
progress.On("Inc").Return()
realProvider, err = aws.NewAWSTerraformProvider(progress)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -171,7 +175,9 @@ func TestTerraformStateReader_Github_Resources(t *testing.T) {

if shouldUpdate {
var err error
realProvider, err = github.NewGithubTerraformProvider()
progress := &output.MockProgress{}
progress.On("Inc").Return()
realProvider, err = github.NewGithubTerraformProvider(progress)
if err != nil {
t.Fatal(err)
}
Expand Down
39 changes: 39 additions & 0 deletions pkg/output/mock_Progress.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

98 changes: 98 additions & 0 deletions pkg/output/progress.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package output

import (
"time"

"go.uber.org/atomic"

"github.com/sirupsen/logrus"
)

var spinner = []string{"⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"}

const (
progressTimeout = 10 * time.Second
progressRefreshRate = 200 * time.Millisecond
)

type Progress interface {
Start()
Stop()
Inc()
Val() uint64
}

type progress struct {
ticChan chan struct{}
endChan chan struct{}
started *atomic.Bool
count *atomic.Uint64
}

func NewProgress() *progress {
return &progress{
make(chan struct{}),
make(chan struct{}),
atomic.NewBool(false),
atomic.NewUint64(0),
}
}

func (p *progress) Start() {
if !p.started.Swap(true) {
go p.watch()
go p.render()
}
}

func (p *progress) Stop() {
if p.started.Swap(false) {
p.endChan <- struct{}{}
Printf("\n")
}
}

func (p *progress) Inc() {
if p.started.Load() {
p.ticChan <- struct{}{}
}
}

func (p *progress) Val() uint64 {
return p.count.Load()
}

func (p *progress) render() {
i := -1
Printf("Scanning resources:\r")
for {
select {
case <-p.endChan:
return
case <-time.After(progressRefreshRate):
i++
if i >= len(spinner) {
i = 0
}
Printf("Scanning resources: %s (%d)\r", spinner[i], p.count.Load())
}
}
}

func (p *progress) watch() {
Loop:
for {
select {
case <-p.ticChan:
p.count.Inc()
continue Loop
case <-time.After(progressTimeout):
p.started.Store(false)
break Loop
case <-p.endChan:
return
}
}
logrus.Debug("Progress did not receive any tic. Stopping...")
p.endChan <- struct{}{}
}
27 changes: 27 additions & 0 deletions pkg/output/progress_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package output

import (
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func TestProgressTimeout(t *testing.T) {
progress := NewProgress()
progress.Start()
time.Sleep(progressTimeout + 1)
progress.Inc() // should not hang
progress.Stop() // should not hang
assert.Equal(t, uint64(0), progress.Val())
}

func TestProgress(t *testing.T) {
progress := NewProgress()
progress.Start()
progress.Inc()
progress.Inc()
progress.Inc()
progress.Stop()
assert.Equal(t, uint64(3), progress.Val())
}
5 changes: 3 additions & 2 deletions pkg/remote/aws/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package aws

import (
"github.com/cloudskiff/driftctl/pkg/alerter"
"github.com/cloudskiff/driftctl/pkg/output"
"github.com/cloudskiff/driftctl/pkg/remote/aws/client"
"github.com/cloudskiff/driftctl/pkg/remote/aws/repository"
"github.com/cloudskiff/driftctl/pkg/resource"
Expand All @@ -14,8 +15,8 @@ const RemoteAWSTerraform = "aws+tf"
* Initialize remote (configure credentials, launch tf providers and start gRPC clients)
* Required to use Scanner
*/
func Init(alerter *alerter.Alerter, providerLibrary *terraform.ProviderLibrary, supplierLibrary *resource.SupplierLibrary) error {
provider, err := NewAWSTerraformProvider()
func Init(alerter *alerter.Alerter, providerLibrary *terraform.ProviderLibrary, supplierLibrary *resource.SupplierLibrary, progress output.Progress) error {
provider, err := NewAWSTerraformProvider(progress)
if err != nil {
return err
}
Expand Down
7 changes: 5 additions & 2 deletions pkg/remote/aws/init_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package aws

import "github.com/cloudskiff/driftctl/pkg/terraform"
import (
"github.com/cloudskiff/driftctl/pkg/output"
"github.com/cloudskiff/driftctl/pkg/terraform"
)

func InitTestAwsProvider(providerLibrary *terraform.ProviderLibrary) (*AWSTerraformProvider, error) {
provider, err := NewAWSTerraformProvider()
provider, err := NewAWSTerraformProvider(&output.MockProgress{})
if err != nil {
return nil, err
}
Expand Down
Loading

0 comments on commit 98d7da2

Please sign in to comment.