diff --git a/pkg/iac/terraform/state/enumerator/file.go b/pkg/iac/terraform/state/enumerator/file.go index a96416dc..abc81b56 100644 --- a/pkg/iac/terraform/state/enumerator/file.go +++ b/pkg/iac/terraform/state/enumerator/file.go @@ -1,6 +1,7 @@ package enumerator import ( + "fmt" "os" "path/filepath" @@ -51,6 +52,13 @@ func (s *FileEnumerator) Enumerate() ([]string, error) { } keys, err := Glob(path) + if err != nil { + return keys, err + } + + if len(keys) == 0 { + return keys, fmt.Errorf("no Terraform state was found in %s, exiting", s.config.Path) + } return keys, err } diff --git a/pkg/iac/terraform/state/enumerator/file_test.go b/pkg/iac/terraform/state/enumerator/file_test.go index 2ba71d70..dba1b6c3 100644 --- a/pkg/iac/terraform/state/enumerator/file_test.go +++ b/pkg/iac/terraform/state/enumerator/file_test.go @@ -101,6 +101,30 @@ func TestFileEnumerator_Enumerate(t *testing.T) { want: nil, err: "lstat testdata/invalid_symlink/test: no such file or directory", }, + { + name: "test no state found with simple path", + config: config.SupplierConfig{ + Path: "testdata/no_state_here", + }, + want: nil, + err: "no Terraform state was found in testdata/no_state_here, exiting", + }, + { + name: "test no state found with double star glob path", + config: config.SupplierConfig{ + Path: "testdata/no_state_here/**/*.tfstate", + }, + want: nil, + err: "no Terraform state was found in testdata/no_state_here/**/*.tfstate, exiting", + }, + { + name: "test no state found with simple glob path", + config: config.SupplierConfig{ + Path: "testdata/no_state_here/test/*", + }, + want: nil, + err: "no Terraform state was found in testdata/no_state_here/test/*, exiting", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/iac/terraform/state/enumerator/s3.go b/pkg/iac/terraform/state/enumerator/s3.go index 3faa856c..29a839c2 100644 --- a/pkg/iac/terraform/state/enumerator/s3.go +++ b/pkg/iac/terraform/state/enumerator/s3.go @@ -1,6 +1,7 @@ package enumerator import ( + "fmt" "path/filepath" "strings" @@ -65,5 +66,9 @@ func (s *S3Enumerator) Enumerate() ([]string, error) { return nil, err } + if len(files) == 0 { + return files, fmt.Errorf("no Terraform state was found in %s, exiting", s.config.Path) + } + return files, nil } diff --git a/pkg/iac/terraform/state/enumerator/s3_test.go b/pkg/iac/terraform/state/enumerator/s3_test.go index 3b531459..50d5ff1a 100644 --- a/pkg/iac/terraform/state/enumerator/s3_test.go +++ b/pkg/iac/terraform/state/enumerator/s3_test.go @@ -71,6 +71,7 @@ func TestS3Enumerator_Enumerate(t *testing.T) { ).Return(nil) }, want: []string{}, + err: "no Terraform state was found in bucket-name/a/nested/prefix, exiting", }, { name: "one test result is returned", @@ -265,6 +266,93 @@ func TestS3Enumerator_Enumerate(t *testing.T) { want: nil, err: "error when listing", }, + { + name: "test no state found with simple path", + config: config.SupplierConfig{ + Path: "bucket-name/a/nested/prefix", + }, + mocks: func(client *awstest.MockFakeS3) { + input := &s3.ListObjectsV2Input{ + Bucket: awssdk.String("bucket-name"), + Prefix: awssdk.String("a/nested/prefix"), + } + client.On( + "ListObjectsV2Pages", + input, + mock.MatchedBy(func(callback func(res *s3.ListObjectsV2Output, lastPage bool) bool) bool { + callback(&s3.ListObjectsV2Output{ + Contents: []*s3.Object{ + { + Key: awssdk.String("a/nested/prefix/1/state1.tfstate"), + Size: awssdk.Int64(5), + }, + }, + }, true) + return true + }), + ).Return(nil) + }, + want: []string{}, + err: "no Terraform state was found in bucket-name/a/nested/prefix, exiting", + }, + { + name: "test no state found with simple glob path", + config: config.SupplierConfig{ + Path: "bucket-name/a/nested/prefix/*", + }, + mocks: func(client *awstest.MockFakeS3) { + input := &s3.ListObjectsV2Input{ + Bucket: awssdk.String("bucket-name"), + Prefix: awssdk.String("a/nested/prefix"), + } + client.On( + "ListObjectsV2Pages", + input, + mock.MatchedBy(func(callback func(res *s3.ListObjectsV2Output, lastPage bool) bool) bool { + callback(&s3.ListObjectsV2Output{ + Contents: []*s3.Object{ + { + Key: awssdk.String("a/nested/prefix/1/state1.tfstate"), + Size: awssdk.Int64(5), + }, + }, + }, true) + return true + }), + ).Return(nil) + }, + want: []string{}, + err: "no Terraform state was found in bucket-name/a/nested/prefix/*, exiting", + }, + { + name: "test no state found with double star glob path", + config: config.SupplierConfig{ + Path: "bucket-name/a/nested/prefix/**/*.tfstate", + }, + mocks: func(client *awstest.MockFakeS3) { + input := &s3.ListObjectsV2Input{ + Bucket: awssdk.String("bucket-name"), + Prefix: awssdk.String("a/nested/prefix"), + } + client.On( + "ListObjectsV2Pages", + input, + mock.MatchedBy(func(callback func(res *s3.ListObjectsV2Output, lastPage bool) bool) bool { + callback(&s3.ListObjectsV2Output{ + Contents: []*s3.Object{ + { + Key: awssdk.String("a/nested/prefix/1/dummy.json"), + Size: awssdk.Int64(5), + }, + }, + }, true) + return true + }), + ).Return(nil) + }, + want: []string{}, + err: "no Terraform state was found in bucket-name/a/nested/prefix/**/*.tfstate, exiting", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/iac/terraform/state/enumerator/testdata/no_state_here/dummy.json b/pkg/iac/terraform/state/enumerator/testdata/no_state_here/dummy.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/pkg/iac/terraform/state/enumerator/testdata/no_state_here/dummy.json @@ -0,0 +1 @@ +{} \ No newline at end of file