Merge branch 'main' into GeraldC13-patch-1

main
Elie 2021-01-28 20:15:16 +01:00 committed by GitHub
commit d0860d33a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 222 additions and 2 deletions

View File

@ -8,6 +8,7 @@ import (
"github.com/cloudskiff/driftctl/build"
"github.com/cloudskiff/driftctl/logger"
"github.com/cloudskiff/driftctl/pkg/cmd"
cmderrors "github.com/cloudskiff/driftctl/pkg/cmd/errors"
"github.com/cloudskiff/driftctl/pkg/config"
"github.com/cloudskiff/driftctl/pkg/version"
"github.com/fatih/color"
@ -56,6 +57,9 @@ func run() int {
}()
if _, err := driftctlCmd.ExecuteC(); err != nil {
if _, isNotInSync := err.(cmderrors.InfrastructureNotInSync); isNotInSync {
return 1
}
if cmd.IsReportingEnabled(&driftctlCmd.Command) {
sentry.CaptureException(err)
}

View File

@ -50,6 +50,7 @@ func NewDriftctlCmd(build build.BuildInterface) *DriftctlCmd {
},
Long: "Detect, track and alert on infrastructure drift.",
SilenceErrors: true,
SilenceUsage: true,
},
build,
}

7
pkg/cmd/errors/scan.go Normal file
View File

@ -0,0 +1,7 @@
package errors
type InfrastructureNotInSync struct{}
func (i InfrastructureNotInSync) Error() string {
return "Infrastructure is not in sync"
}

View File

@ -10,6 +10,7 @@ import (
"github.com/cloudskiff/driftctl/pkg"
"github.com/cloudskiff/driftctl/pkg/alerter"
cmderrors "github.com/cloudskiff/driftctl/pkg/cmd/errors"
"github.com/cloudskiff/driftctl/pkg/cmd/scan/output"
"github.com/cloudskiff/driftctl/pkg/filter"
"github.com/cloudskiff/driftctl/pkg/iac/config"
@ -157,8 +158,17 @@ func scanRun(opts *ScanOptions) error {
if analysis == nil {
return errors.New("unable to run driftctl")
}
out := output.GetOutput(opts.Output)
return out.Write(analysis)
err = output.GetOutput(opts.Output).Write(analysis)
if err != nil {
return err
}
if !analysis.IsSync() {
return cmderrors.InfrastructureNotInSync{}
}
return nil
}
func parseFromFlag(from []string) ([]config.SupplierConfig, error) {

View File

@ -46,6 +46,7 @@ func (d DriftCTL) Run() *analyser.Analysis {
middlewares.NewAwsDefaultRouteTable(),
middlewares.NewAwsDefaultRoute(),
middlewares.NewAwsNatGatewayEipAssoc(),
middlewares.NewAwsBucketPolicyExpander(),
)
logrus.Debug("Ready to run middlewares")

View File

@ -0,0 +1,77 @@
package middlewares
import (
"github.com/cloudskiff/driftctl/pkg/resource"
"github.com/cloudskiff/driftctl/pkg/resource/aws"
"github.com/sirupsen/logrus"
)
// Explodes policy found in aws_s3_bucket.policy from state resources to dedicated resources
type AwsBucketPolicyExpander struct{}
func NewAwsBucketPolicyExpander() AwsBucketPolicyExpander {
return AwsBucketPolicyExpander{}
}
func (m AwsBucketPolicyExpander) Execute(_, resourcesFromState *[]resource.Resource) error {
newList := make([]resource.Resource, 0)
for _, res := range *resourcesFromState {
// Ignore all resources other than s3_bucket
if res.TerraformType() != aws.AwsS3BucketResourceType {
newList = append(newList, res)
continue
}
bucket, _ := res.(*aws.AwsS3Bucket)
newList = append(newList, res)
if hasPolicyAttached(bucket, resourcesFromState) {
bucket.Policy = nil
continue
}
err := m.handlePolicy(bucket, &newList)
if err != nil {
return err
}
}
*resourcesFromState = newList
return nil
}
func (m *AwsBucketPolicyExpander) handlePolicy(bucket *aws.AwsS3Bucket, results *[]resource.Resource) error {
if bucket.Policy == nil || *bucket.Policy == "" {
return nil
}
newPolicy := &aws.AwsS3BucketPolicy{
Id: bucket.Id,
Bucket: bucket.Bucket,
Policy: bucket.Policy,
}
normalizedRes, err := newPolicy.NormalizeForState()
if err != nil {
return err
}
*results = append(*results, normalizedRes)
logrus.WithFields(logrus.Fields{
"id": newPolicy.TerraformId(),
}).Debug("Created new policy from bucket")
bucket.Policy = nil
return nil
}
// Return true if the bucket has a aws_bucket_policy resource attached to itself.
// It is mandatory since it's possible to have a aws_bucket with an inline policy
// AND a aws_bucket_policy resource at the same time. At the end, on the AWS console,
// the aws_bucket_policy will be used.
func hasPolicyAttached(bucket *aws.AwsS3Bucket, resourcesFromState *[]resource.Resource) bool {
for _, res := range *resourcesFromState {
if res.TerraformType() == aws.AwsS3BucketPolicyResourceType &&
res.TerraformId() == bucket.Id {
return true
}
}
return false
}

View File

@ -0,0 +1,115 @@
package middlewares
import (
"strings"
"testing"
awssdk "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/cloudskiff/driftctl/pkg/resource"
"github.com/cloudskiff/driftctl/pkg/resource/aws"
"github.com/r3labs/diff/v2"
)
func TestAwsBucketPolicyExpander_Execute(t *testing.T) {
tests := []struct {
name string
resourcesFromState []resource.Resource
expected []resource.Resource
}{
{
"Inline policy, no aws_s3_bucket_policy attached",
[]resource.Resource{
&aws.AwsS3Bucket{
Id: "foo",
Bucket: awssdk.String("foo"),
Policy: awssdk.String("{\"Id\":\"MYINLINEBUCKETPOLICY\",\"Statement\":[{\"Action\":\"s3:*\",\"Condition\":{\"IpAddress\":{\"aws:SourceIp\":\"8.8.8.8/32\"}},\"Effect\":\"Deny\",\"Principal\":\"*\",\"Resource\":\"arn:aws:s3:::bucket-test-policy-like-sqs/*\",\"Sid\":\"IPAllow\"}],\"Version\":\"2012-10-17\"}"),
},
},
[]resource.Resource{
&aws.AwsS3Bucket{
Id: "foo",
Bucket: awssdk.String("foo"),
Policy: nil,
},
&aws.AwsS3BucketPolicy{
Id: "foo",
Bucket: awssdk.String("foo"),
Policy: awssdk.String("{\"Id\":\"MYINLINEBUCKETPOLICY\",\"Statement\":[{\"Action\":\"s3:*\",\"Condition\":{\"IpAddress\":{\"aws:SourceIp\":\"8.8.8.8/32\"}},\"Effect\":\"Deny\",\"Principal\":\"*\",\"Resource\":\"arn:aws:s3:::bucket-test-policy-like-sqs/*\",\"Sid\":\"IPAllow\"}],\"Version\":\"2012-10-17\"}"),
},
},
},
{
"No inline policy, aws_s3_bucket_policy attached",
[]resource.Resource{
&aws.AwsS3Bucket{
Id: "foo",
Bucket: awssdk.String("foo"),
Policy: nil,
},
&aws.AwsS3BucketPolicy{
Id: "foo",
Bucket: awssdk.String("foo"),
Policy: awssdk.String("{\"Id\":\"MYBUCKETPOLICY\",\"Statement\":[{\"Action\":\"s3:*\",\"Condition\":{\"IpAddress\":{\"aws:SourceIp\":\"8.8.8.8/32\"}},\"Effect\":\"Deny\",\"Principal\":\"*\",\"Resource\":\"arn:aws:s3:::bucket-test-policy-like-sqs/*\",\"Sid\":\"IPAllow\"}],\"Version\":\"2012-10-17\"}"),
},
},
[]resource.Resource{
&aws.AwsS3Bucket{
Id: "foo",
Bucket: awssdk.String("foo"),
Policy: nil,
},
&aws.AwsS3BucketPolicy{
Id: "foo",
Bucket: awssdk.String("foo"),
Policy: awssdk.String("{\"Id\":\"MYBUCKETPOLICY\",\"Statement\":[{\"Action\":\"s3:*\",\"Condition\":{\"IpAddress\":{\"aws:SourceIp\":\"8.8.8.8/32\"}},\"Effect\":\"Deny\",\"Principal\":\"*\",\"Resource\":\"arn:aws:s3:::bucket-test-policy-like-sqs/*\",\"Sid\":\"IPAllow\"}],\"Version\":\"2012-10-17\"}"),
},
},
},
{
"Inline policy and aws_s3_bucket_policy",
[]resource.Resource{
&aws.AwsS3Bucket{
Id: "foo",
Bucket: awssdk.String("foo"),
Policy: awssdk.String("{\"Id\":\"MYINLINEBUCKETPOLICY\",\"Statement\":[{\"Action\":\"s3:*\",\"Condition\":{\"IpAddress\":{\"aws:SourceIp\":\"8.8.8.8/32\"}},\"Effect\":\"Deny\",\"Principal\":\"*\",\"Resource\":\"arn:aws:s3:::bucket-test-policy-like-sqs/*\",\"Sid\":\"IPAllow\"}],\"Version\":\"2012-10-17\"}"),
},
&aws.AwsS3BucketPolicy{
Id: "foo",
Bucket: awssdk.String("foo"),
Policy: awssdk.String("{\"Id\":\"MYBUCKETPOLICY\",\"Statement\":[{\"Action\":\"s3:*\",\"Condition\":{\"IpAddress\":{\"aws:SourceIp\":\"8.8.8.8/32\"}},\"Effect\":\"Deny\",\"Principal\":\"*\",\"Resource\":\"arn:aws:s3:::bucket-test-policy-like-sqs/*\",\"Sid\":\"IPAllow\"}],\"Version\":\"2012-10-17\"}"),
},
},
[]resource.Resource{
&aws.AwsS3Bucket{
Id: "foo",
Bucket: awssdk.String("foo"),
Policy: nil,
},
&aws.AwsS3BucketPolicy{
Id: "foo",
Bucket: awssdk.String("foo"),
Policy: awssdk.String("{\"Id\":\"MYBUCKETPOLICY\",\"Statement\":[{\"Action\":\"s3:*\",\"Condition\":{\"IpAddress\":{\"aws:SourceIp\":\"8.8.8.8/32\"}},\"Effect\":\"Deny\",\"Principal\":\"*\",\"Resource\":\"arn:aws:s3:::bucket-test-policy-like-sqs/*\",\"Sid\":\"IPAllow\"}],\"Version\":\"2012-10-17\"}"),
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
m := NewAwsBucketPolicyExpander()
err := m.Execute(&[]resource.Resource{}, &tt.resourcesFromState)
if err != nil {
t.Fatal(err)
}
changelog, err := diff.Diff(tt.expected, tt.resourcesFromState)
if err != nil {
t.Fatal(err)
}
if len(changelog) > 0 {
for _, change := range changelog {
t.Errorf("%s got = %v, want %v", strings.Join(change.Path, "."), awsutil.Prettify(change.From), awsutil.Prettify(change.To))
}
}
})
}
}

View File

@ -13,6 +13,7 @@ import (
"testing"
"github.com/cloudskiff/driftctl/pkg/analyser"
cmderrors "github.com/cloudskiff/driftctl/pkg/cmd/errors"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@ -168,6 +169,10 @@ func runDriftCtlCmd(driftctlCmd *cmd.DriftctlCmd) (*cobra.Command, string, error
r, w, _ := os.Pipe()
os.Stdout = w
cmd, cmdErr := driftctlCmd.ExecuteC()
// Ignore not in sync errors in acceptance test context
if _, isNotInSyncErr := cmdErr.(cmderrors.InfrastructureNotInSync); isNotInSyncErr {
cmdErr = nil
}
outC := make(chan string)
// copy the output in a separate goroutine so printing can't block indefinitely
go func() {