Merge branch 'main' into feat/htmlOutput
commit
9ead8d1523
|
@ -29,7 +29,7 @@ func NewComputedDiffAlert() *ComputedDiffAlert {
|
|||
}
|
||||
|
||||
func (c *ComputedDiffAlert) Message() string {
|
||||
return "You have diffs on computed fields, check the documentation for potential false positive drifts"
|
||||
return "You have diffs on computed fields, check the documentation for potential false positive drifts: https://docs.driftctl.com/limitations"
|
||||
}
|
||||
|
||||
func (c *ComputedDiffAlert) ShouldIgnoreResource() bool {
|
||||
|
|
|
@ -157,6 +157,11 @@ func NewScanCmd() *cobra.Command {
|
|||
false,
|
||||
"Includes cloud provider service-linked roles (disabled by default)",
|
||||
)
|
||||
fl.StringVar(&opts.DriftignorePath,
|
||||
"driftignore",
|
||||
".driftignore",
|
||||
"Path to the driftignore file",
|
||||
)
|
||||
|
||||
configDir, err := homedir.Dir()
|
||||
if err != nil {
|
||||
|
|
|
@ -80,7 +80,7 @@
|
|||
"alerts": {
|
||||
"": [
|
||||
{
|
||||
"message": "You have diffs on computed fields, check the documentation for potential false positive drifts"
|
||||
"message": "You have diffs on computed fields, check the documentation for potential false positive drifts: https://docs.driftctl.com/limitations"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -11,4 +11,4 @@ Found 1 resource(s)
|
|||
- 0 not covered by IaC
|
||||
- 0 missing on cloud provider
|
||||
- 1/1 changed outside of IaC
|
||||
You have diffs on computed fields, check the documentation for potential false positive drifts
|
||||
You have diffs on computed fields, check the documentation for potential false positive drifts: https://docs.driftctl.com/limitations
|
||||
|
|
|
@ -45,6 +45,8 @@ func TestScanCmd_Valid(t *testing.T) {
|
|||
{args: []string{"scan", "--strict"}},
|
||||
{args: []string{"scan", "--tf-provider-version", "1.2.3"}},
|
||||
{args: []string{"scan", "--tf-provider-version", "3.30.2"}},
|
||||
{args: []string{"scan", "--driftignore", "./path/to/driftignore.s3"}},
|
||||
{args: []string{"scan", "--driftignore", ".driftignore"}},
|
||||
}
|
||||
|
||||
for _, tt := range cases {
|
||||
|
@ -85,6 +87,7 @@ func TestScanCmd_Invalid(t *testing.T) {
|
|||
{args: []string{"scan", "--filter", "Type='test'", "--filter", "Type='test2'"}, expected: "Filter flag should be specified only once"},
|
||||
{args: []string{"scan", "--tf-provider-version", ".30.2"}, expected: "Invalid version argument .30.2, expected a valid semver string (e.g. 2.13.4)"},
|
||||
{args: []string{"scan", "--tf-provider-version", "foo"}, expected: "Invalid version argument foo, expected a valid semver string (e.g. 2.13.4)"},
|
||||
{args: []string{"scan", "--driftignore"}, expected: "flag needs an argument: --driftignore"},
|
||||
}
|
||||
|
||||
for _, tt := range cases {
|
||||
|
|
|
@ -31,6 +31,7 @@ type ScanOptions struct {
|
|||
DisableTelemetry bool
|
||||
ProviderVersion string
|
||||
ConfigDir string
|
||||
DriftignorePath string
|
||||
}
|
||||
|
||||
type DriftCTL struct {
|
||||
|
@ -38,12 +39,11 @@ type DriftCTL struct {
|
|||
iacSupplier resource.Supplier
|
||||
alerter alerter.AlerterInterface
|
||||
analyzer analyser.Analyzer
|
||||
filter *jmespath.JMESPath
|
||||
resourceFactory resource.ResourceFactory
|
||||
strictMode bool
|
||||
scanProgress globaloutput.Progress
|
||||
iacProgress globaloutput.Progress
|
||||
resourceSchemaRepository resource.SchemaRepositoryInterface
|
||||
opts *ScanOptions
|
||||
}
|
||||
|
||||
func NewDriftCTL(remoteSupplier resource.Supplier,
|
||||
|
@ -59,12 +59,11 @@ func NewDriftCTL(remoteSupplier resource.Supplier,
|
|||
iacSupplier,
|
||||
alerter,
|
||||
analyser.NewAnalyzer(alerter),
|
||||
opts.Filter,
|
||||
resFactory,
|
||||
opts.StrictMode,
|
||||
scanProgress,
|
||||
iacProgress,
|
||||
resourceSchemaRepository,
|
||||
opts,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,9 +97,10 @@ func (d DriftCTL) Run() (*analyser.Analysis, error) {
|
|||
middlewares.NewAwsDefaultSqsQueuePolicy(),
|
||||
middlewares.NewAwsSNSTopicPolicyExpander(d.resourceFactory, d.resourceSchemaRepository),
|
||||
middlewares.NewAwsRoleManagedPolicyExpander(d.resourceFactory),
|
||||
middlewares.NewTagsAllManager(),
|
||||
)
|
||||
|
||||
if !d.strictMode {
|
||||
if !d.opts.StrictMode {
|
||||
middleware = append(middleware,
|
||||
middlewares.NewAwsDefaults(),
|
||||
)
|
||||
|
@ -112,8 +112,8 @@ func (d DriftCTL) Run() (*analyser.Analysis, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if d.filter != nil {
|
||||
engine := filter.NewFilterEngine(d.filter)
|
||||
if d.opts.Filter != nil {
|
||||
engine := filter.NewFilterEngine(d.opts.Filter)
|
||||
remoteResources, err = engine.Run(remoteResources)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -125,7 +125,7 @@ func (d DriftCTL) Run() (*analyser.Analysis, error) {
|
|||
}
|
||||
|
||||
logrus.Debug("Checking for driftignore")
|
||||
driftIgnore := filter.NewDriftIgnore()
|
||||
driftIgnore := filter.NewDriftIgnore(d.opts.DriftignorePath)
|
||||
|
||||
analysis, err := d.analyzer.Analyze(remoteResources, resourcesFromState, driftIgnore)
|
||||
if err != nil {
|
||||
|
|
|
@ -1559,6 +1559,14 @@ func TestDriftctlRun_Middlewares(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
&resource.AbstractResource{
|
||||
Id: "role_with_managed_policy_attr-arn2",
|
||||
Type: aws.AwsIamPolicyAttachmentResourceType,
|
||||
Attrs: &resource.Attributes{
|
||||
"policy_arn": "arn2",
|
||||
"roles": []interface{}{"role_with_managed_policy_attr"},
|
||||
},
|
||||
},
|
||||
&resource.AbstractResource{
|
||||
Id: "role_with_empty_managed_policy_attribute",
|
||||
Type: aws.AwsIamRoleResourceType,
|
||||
|
|
|
@ -15,13 +15,15 @@ type DriftIgnore struct {
|
|||
resExclusionList map[string]struct{} // map[type.id] exists to ignore
|
||||
resExclusionWildcardList map[string]struct{} // map[type.id] exists with wildcard to ignore
|
||||
driftExclusionList map[string][]string // map[type.id] contains path for drift to ignore
|
||||
driftignorePath string
|
||||
}
|
||||
|
||||
func NewDriftIgnore() *DriftIgnore {
|
||||
func NewDriftIgnore(path string) *DriftIgnore {
|
||||
d := DriftIgnore{
|
||||
resExclusionList: map[string]struct{}{},
|
||||
resExclusionWildcardList: map[string]struct{}{},
|
||||
driftExclusionList: map[string][]string{},
|
||||
driftignorePath: path,
|
||||
}
|
||||
err := d.readIgnoreFile()
|
||||
if err != nil {
|
||||
|
@ -31,7 +33,7 @@ func NewDriftIgnore() *DriftIgnore {
|
|||
}
|
||||
|
||||
func (r *DriftIgnore) readIgnoreFile() error {
|
||||
file, err := os.Open(".driftignore")
|
||||
file, err := os.Open(r.driftignorePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package filter
|
|||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -19,6 +18,7 @@ func TestDriftIgnore_IsResourceIgnored(t *testing.T) {
|
|||
name string
|
||||
resources []resource.Resource
|
||||
want []bool
|
||||
path string
|
||||
}{
|
||||
{
|
||||
name: "drift_ignore_no_file",
|
||||
|
@ -28,10 +28,10 @@ func TestDriftIgnore_IsResourceIgnored(t *testing.T) {
|
|||
Id: "id1",
|
||||
},
|
||||
},
|
||||
|
||||
want: []bool{
|
||||
false,
|
||||
},
|
||||
path: "testdata/drift_ignore_no_file/.driftignore",
|
||||
},
|
||||
{
|
||||
name: "drift_ignore_empty",
|
||||
|
@ -44,6 +44,7 @@ func TestDriftIgnore_IsResourceIgnored(t *testing.T) {
|
|||
want: []bool{
|
||||
false,
|
||||
},
|
||||
path: "testdata/drift_ignore_empty/.driftignore",
|
||||
},
|
||||
{
|
||||
name: "drift_ignore_invalid_lines",
|
||||
|
@ -61,6 +62,7 @@ func TestDriftIgnore_IsResourceIgnored(t *testing.T) {
|
|||
false,
|
||||
true,
|
||||
},
|
||||
path: "testdata/drift_ignore_invalid_lines/.driftignore",
|
||||
},
|
||||
{
|
||||
name: "drift_ignore_valid",
|
||||
|
@ -108,6 +110,7 @@ func TestDriftIgnore_IsResourceIgnored(t *testing.T) {
|
|||
true,
|
||||
true,
|
||||
},
|
||||
path: "testdata/drift_ignore_valid/.driftignore",
|
||||
},
|
||||
{
|
||||
name: "drift_ignore_wildcard",
|
||||
|
@ -150,16 +153,15 @@ func TestDriftIgnore_IsResourceIgnored(t *testing.T) {
|
|||
false,
|
||||
true,
|
||||
},
|
||||
path: "testdata/drift_ignore_wildcard/.driftignore",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cwd, _ := os.Getwd()
|
||||
defer func() { _ = os.Chdir(cwd) }()
|
||||
if err := os.Chdir(path.Join("testdata", tt.name)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
r := NewDriftIgnore()
|
||||
|
||||
r := NewDriftIgnore(tt.path)
|
||||
got := make([]bool, 0, len(tt.want))
|
||||
for _, res := range tt.resources {
|
||||
got = append(got, r.IsResourceIgnored(res))
|
||||
|
@ -180,6 +182,7 @@ func TestDriftIgnore_IsFieldIgnored(t *testing.T) {
|
|||
tests := []struct {
|
||||
name string
|
||||
args []Args
|
||||
path string
|
||||
}{
|
||||
{
|
||||
name: "drift_ignore_no_file",
|
||||
|
@ -196,6 +199,7 @@ func TestDriftIgnore_IsFieldIgnored(t *testing.T) {
|
|||
Want: false,
|
||||
},
|
||||
},
|
||||
path: "testdata/drift_ignore_no_file/.driftignore",
|
||||
},
|
||||
{
|
||||
name: "drift_ignore_empty",
|
||||
|
@ -211,6 +215,7 @@ func TestDriftIgnore_IsFieldIgnored(t *testing.T) {
|
|||
Want: false,
|
||||
},
|
||||
},
|
||||
path: "testdata/drift_ignore_empty/.driftignore",
|
||||
},
|
||||
{
|
||||
name: "drift_ignore_fields",
|
||||
|
@ -281,16 +286,15 @@ func TestDriftIgnore_IsFieldIgnored(t *testing.T) {
|
|||
Want: true,
|
||||
},
|
||||
},
|
||||
path: "testdata/drift_ignore_fields/.driftignore",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cwd, _ := os.Getwd()
|
||||
defer func() { _ = os.Chdir(cwd) }()
|
||||
if err := os.Chdir(path.Join("testdata", tt.name)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
r := NewDriftIgnore()
|
||||
|
||||
r := NewDriftIgnore(tt.path)
|
||||
for _, arg := range tt.args {
|
||||
got := r.IsFieldIgnored(arg.Res, arg.Path)
|
||||
if arg.Want != got {
|
||||
|
|
|
@ -47,7 +47,10 @@ func (a AwsInstanceBlockDeviceResourceMapper) Execute(remoteResources, resources
|
|||
"size": rootBlock["volume_size"],
|
||||
"type": rootBlock["volume_type"],
|
||||
"multi_attach_enabled": false,
|
||||
"tags": (*instance.Attrs)["volume_tags"],
|
||||
"tags": a.volumeTags(instance, rootBlock),
|
||||
}
|
||||
if throughput, exist := rootBlock["throughput"]; exist {
|
||||
data["throughput"] = throughput
|
||||
}
|
||||
newRes := a.resourceFactory.CreateAbstractResource("aws_ebs_volume", rootBlock["volume_id"].(string), data)
|
||||
newStateResources = append(newStateResources, newRes)
|
||||
|
@ -72,7 +75,10 @@ func (a AwsInstanceBlockDeviceResourceMapper) Execute(remoteResources, resources
|
|||
"size": blockDevice["volume_size"],
|
||||
"type": blockDevice["volume_type"],
|
||||
"multi_attach_enabled": false,
|
||||
"tags": (*instance.Attrs)["volume_tags"],
|
||||
"tags": a.volumeTags(instance, blockDevice),
|
||||
}
|
||||
if throughput, exist := blockDevice["throughput"]; exist {
|
||||
data["throughput"] = throughput
|
||||
}
|
||||
newRes := a.resourceFactory.CreateAbstractResource("aws_ebs_volume", blockDevice["volume_id"].(string), data)
|
||||
newStateResources = append(newStateResources, newRes)
|
||||
|
@ -101,3 +107,10 @@ func (a AwsInstanceBlockDeviceResourceMapper) Execute(remoteResources, resources
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AwsInstanceBlockDeviceResourceMapper) volumeTags(instance *resource.AbstractResource, blockDevice map[string]interface{}) interface{} {
|
||||
if tags, exist := instance.Attrs.Get("volume_tags"); exist {
|
||||
return tags
|
||||
}
|
||||
return blockDevice["tags"]
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ func TestAwsInstanceBlockDeviceResourceMapper_Execute(t *testing.T) {
|
|||
"kms_key_id": "kms",
|
||||
"size": 8,
|
||||
"type": "gp2",
|
||||
"throughput": 125,
|
||||
"tags": map[string]interface{}{
|
||||
"Name": "rootVol",
|
||||
},
|
||||
|
@ -63,6 +64,7 @@ func TestAwsInstanceBlockDeviceResourceMapper_Execute(t *testing.T) {
|
|||
"availability_zone": "eu-west-3",
|
||||
"size": 23,
|
||||
"type": "gp2",
|
||||
"throughput": 125,
|
||||
"tags": map[string]interface{}{
|
||||
"Name": "rootVol",
|
||||
},
|
||||
|
@ -96,6 +98,7 @@ func TestAwsInstanceBlockDeviceResourceMapper_Execute(t *testing.T) {
|
|||
"encrypted": true,
|
||||
"kms_key_id": "kms",
|
||||
"volume_size": 8,
|
||||
"throughput": 125,
|
||||
"iops": 1234,
|
||||
},
|
||||
},
|
||||
|
@ -107,6 +110,7 @@ func TestAwsInstanceBlockDeviceResourceMapper_Execute(t *testing.T) {
|
|||
"encrypted": true,
|
||||
"delete_on_termination": true,
|
||||
"volume_size": 23,
|
||||
"throughput": 125,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -126,6 +130,7 @@ func TestAwsInstanceBlockDeviceResourceMapper_Execute(t *testing.T) {
|
|||
"kms_key_id": "kms",
|
||||
"size": 8,
|
||||
"type": "gp2",
|
||||
"throughput": 125,
|
||||
"tags": map[string]interface{}{
|
||||
"Name": "rootVol",
|
||||
},
|
||||
|
@ -145,6 +150,7 @@ func TestAwsInstanceBlockDeviceResourceMapper_Execute(t *testing.T) {
|
|||
"availability_zone": "eu-west-3",
|
||||
"size": 23,
|
||||
"type": "gp2",
|
||||
"throughput": 125,
|
||||
"tags": map[string]interface{}{
|
||||
"Name": "rootVol",
|
||||
},
|
||||
|
@ -156,6 +162,139 @@ func TestAwsInstanceBlockDeviceResourceMapper_Execute(t *testing.T) {
|
|||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"Test with tags inside root/ebs block device",
|
||||
struct {
|
||||
expectedResource *[]resource.Resource
|
||||
resourcesFromState *[]resource.Resource
|
||||
}{
|
||||
expectedResource: &[]resource.Resource{
|
||||
&resource.AbstractResource{
|
||||
Id: "dummy-instance",
|
||||
Type: "aws_instance",
|
||||
Attrs: &resource.Attributes{
|
||||
"availability_zone": "eu-west-3",
|
||||
},
|
||||
},
|
||||
&resource.AbstractResource{
|
||||
Id: "vol-02862d9b39045a3a4",
|
||||
Type: "aws_ebs_volume",
|
||||
Attrs: &resource.Attributes{
|
||||
"id": "vol-02862d9b39045a3a4",
|
||||
"encrypted": true,
|
||||
"multi_attach_enabled": false,
|
||||
"availability_zone": "eu-west-3",
|
||||
"iops": 1234,
|
||||
"kms_key_id": "kms",
|
||||
"size": 8,
|
||||
"type": "gp2",
|
||||
"throughput": 125,
|
||||
"tags": map[string]interface{}{
|
||||
"Name": "rootVol",
|
||||
},
|
||||
},
|
||||
},
|
||||
&resource.AbstractResource{
|
||||
Id: "vol-018c5ae89895aca4c",
|
||||
Type: "aws_ebs_volume",
|
||||
Attrs: &resource.Attributes{
|
||||
"id": "vol-018c5ae89895aca4c",
|
||||
"encrypted": true,
|
||||
"multi_attach_enabled": false,
|
||||
"availability_zone": "eu-west-3",
|
||||
"size": 23,
|
||||
"type": "gp2",
|
||||
"throughput": 125,
|
||||
"tags": map[string]interface{}{
|
||||
"Name": "ebsVol",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
resourcesFromState: &[]resource.Resource{
|
||||
&resource.AbstractResource{
|
||||
Id: "dummy-instance",
|
||||
Type: "aws_instance",
|
||||
Attrs: &resource.Attributes{
|
||||
"availability_zone": "eu-west-3",
|
||||
"root_block_device": []interface{}{
|
||||
map[string]interface{}{
|
||||
"volume_id": "vol-02862d9b39045a3a4",
|
||||
"volume_type": "gp2",
|
||||
"device_name": "/dev/sda1",
|
||||
"encrypted": true,
|
||||
"kms_key_id": "kms",
|
||||
"volume_size": 8,
|
||||
"throughput": 125,
|
||||
"iops": 1234,
|
||||
"tags": map[string]interface{}{
|
||||
"Name": "rootVol",
|
||||
},
|
||||
},
|
||||
},
|
||||
"ebs_block_device": []interface{}{
|
||||
map[string]interface{}{
|
||||
"volume_id": "vol-018c5ae89895aca4c",
|
||||
"volume_type": "gp2",
|
||||
"device_name": "/dev/sdb",
|
||||
"encrypted": true,
|
||||
"delete_on_termination": true,
|
||||
"volume_size": 23,
|
||||
"throughput": 125,
|
||||
"tags": map[string]interface{}{
|
||||
"Name": "ebsVol",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
func(factory *terraform.MockResourceFactory) {
|
||||
foo := resource.AbstractResource{
|
||||
Id: "vol-02862d9b39045a3a4",
|
||||
Type: "aws_ebs_volume",
|
||||
Attrs: &resource.Attributes{
|
||||
"id": "vol-02862d9b39045a3a4",
|
||||
"encrypted": true,
|
||||
"multi_attach_enabled": false,
|
||||
"availability_zone": "eu-west-3",
|
||||
"iops": 1234,
|
||||
"kms_key_id": "kms",
|
||||
"size": 8,
|
||||
"type": "gp2",
|
||||
"throughput": 125,
|
||||
"tags": map[string]interface{}{
|
||||
"Name": "rootVol",
|
||||
},
|
||||
},
|
||||
}
|
||||
factory.On("CreateAbstractResource", "aws_ebs_volume", mock.Anything, mock.MatchedBy(func(input map[string]interface{}) bool {
|
||||
return input["id"] == "vol-02862d9b39045a3a4" && len(input["tags"].(map[string]interface{})) == 1
|
||||
})).Times(1).Return(&foo, nil)
|
||||
|
||||
bar := resource.AbstractResource{
|
||||
Id: "vol-018c5ae89895aca4c",
|
||||
Type: "aws_ebs_volume",
|
||||
Attrs: &resource.Attributes{
|
||||
"id": "vol-018c5ae89895aca4c",
|
||||
"encrypted": true,
|
||||
"multi_attach_enabled": false,
|
||||
"availability_zone": "eu-west-3",
|
||||
"size": 23,
|
||||
"type": "gp2",
|
||||
"throughput": 125,
|
||||
"tags": map[string]interface{}{
|
||||
"Name": "ebsVol",
|
||||
},
|
||||
},
|
||||
}
|
||||
factory.On("CreateAbstractResource", "aws_ebs_volume", mock.Anything, mock.MatchedBy(func(input map[string]interface{}) bool {
|
||||
return input["id"] == "vol-018c5ae89895aca4c" && len(input["tags"].(map[string]interface{})) == 1
|
||||
})).Times(1).Return(&bar, nil)
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, c := range tests {
|
||||
t.Run(c.name, func(tt *testing.T) {
|
||||
|
|
|
@ -61,6 +61,8 @@ func (a AwsRoleManagedPolicyExpander) Execute(remoteResources, resourcesFromStat
|
|||
|
||||
for _, arn := range managedPolicyArns {
|
||||
arn := arn.(string)
|
||||
id := fmt.Sprintf("%s-%s", *roleName, arn)
|
||||
|
||||
policyAttachmentData := resource.Attributes{
|
||||
"policy_arn": arn,
|
||||
"users": []interface{}{},
|
||||
|
@ -73,7 +75,19 @@ func (a AwsRoleManagedPolicyExpander) Execute(remoteResources, resourcesFromStat
|
|||
"policy_arn": arn,
|
||||
}).Debug("Expanded managed_policy_arns from role")
|
||||
|
||||
newList = append(newList, a.resourceFactory.CreateAbstractResource(aws.AwsIamPolicyAttachmentResourceType, fmt.Sprintf("%s-%s", *roleName, arn), policyAttachmentData))
|
||||
newRes := a.resourceFactory.CreateAbstractResource(aws.AwsIamPolicyAttachmentResourceType, id, policyAttachmentData)
|
||||
|
||||
alreadyExist := false
|
||||
for _, resInState := range *resourcesFromState {
|
||||
if resource.IsSameResource(resInState, newRes) {
|
||||
alreadyExist = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !alreadyExist {
|
||||
newList = append(newList, newRes)
|
||||
}
|
||||
}
|
||||
|
||||
res.Attributes().SafeDelete([]string{"managed_policy_arns"})
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package middlewares
|
||||
|
||||
import (
|
||||
"github.com/cloudskiff/driftctl/pkg/resource"
|
||||
)
|
||||
|
||||
// Manage tags_all attribute on each compatible resources
|
||||
type TagsAllManager struct{}
|
||||
|
||||
func NewTagsAllManager() TagsAllManager {
|
||||
return TagsAllManager{}
|
||||
}
|
||||
|
||||
func (a TagsAllManager) Execute(remoteResources, resourcesFromState *[]resource.Resource) error {
|
||||
for _, remoteRes := range *remoteResources {
|
||||
if res, ok := remoteRes.(*resource.AbstractResource); ok {
|
||||
if _, exist := res.Attrs.Get("tags_all"); exist {
|
||||
res.Attrs.SafeDelete([]string{"tags_all"})
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, stateRes := range *resourcesFromState {
|
||||
if res, ok := stateRes.(*resource.AbstractResource); ok {
|
||||
if allTags, exist := res.Attrs.Get("tags_all"); exist {
|
||||
_ = res.Attrs.SafeSet([]string{"tags"}, allTags)
|
||||
res.Attrs.SafeDelete([]string{"tags_all"})
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
package middlewares
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||
"github.com/cloudskiff/driftctl/pkg/resource"
|
||||
"github.com/r3labs/diff/v2"
|
||||
)
|
||||
|
||||
func TestTagsAllManager_Execute(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
remoteResources *[]resource.Resource
|
||||
resourcesFromState *[]resource.Resource
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "With multiple resources that are tags_all compatible",
|
||||
remoteResources: &[]resource.Resource{
|
||||
&resource.AbstractResource{
|
||||
Id: "dummy-instance",
|
||||
Type: "aws_instance",
|
||||
Attrs: &resource.Attributes{
|
||||
"tags": map[string]interface{}{
|
||||
"Name": "toto",
|
||||
"Terraform": "true",
|
||||
},
|
||||
"tags_all": map[string]interface{}{
|
||||
"Name": "toto",
|
||||
"Terraform": "true",
|
||||
},
|
||||
},
|
||||
},
|
||||
&resource.AbstractResource{
|
||||
Id: "dummy-ebs-volume",
|
||||
Type: "aws_ebs_volume",
|
||||
Attrs: &resource.Attributes{
|
||||
"tags": map[string]interface{}{
|
||||
"Name": "tata",
|
||||
"Terraform": "true",
|
||||
},
|
||||
"tags_all": map[string]interface{}{
|
||||
"Name": "tata",
|
||||
"Terraform": "true",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
resourcesFromState: &[]resource.Resource{
|
||||
&resource.AbstractResource{
|
||||
Id: "dummy-instance",
|
||||
Type: "aws_instance",
|
||||
Attrs: &resource.Attributes{
|
||||
"tags": map[string]interface{}{
|
||||
"Name": "toto",
|
||||
},
|
||||
"tags_all": map[string]interface{}{
|
||||
"Name": "toto",
|
||||
"Terraform": "true",
|
||||
},
|
||||
},
|
||||
},
|
||||
&resource.AbstractResource{
|
||||
Id: "dummy-ebs-volume",
|
||||
Type: "aws_ebs_volume",
|
||||
Attrs: &resource.Attributes{
|
||||
"tags": map[string]interface{}{
|
||||
"Name": "tata",
|
||||
},
|
||||
"tags_all": map[string]interface{}{
|
||||
"Name": "tata",
|
||||
"Terraform": "true",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := NewTagsAllManager()
|
||||
if err := a.Execute(tt.remoteResources, tt.resourcesFromState); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Execute() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
changelog, err := diff.Diff(tt.resourcesFromState, tt.remoteResources)
|
||||
if err != nil {
|
||||
t.Error(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))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue