From 6cdd957b55cf40619a800cfddc354a7c4fade80f Mon Sep 17 00:00:00 2001 From: AJ Date: Mon, 23 Dec 2024 16:34:50 +0000 Subject: [PATCH 1/5] add tests for ShouldIncludeBasedOnTag & ShouldInclude in the config pkg, part of #822. --- config/config_test.go | 151 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/config/config_test.go b/config/config_test.go index b84a8bfb..161969f5 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -311,3 +311,154 @@ func TestAddIncludeAndExcludeAfterTime(t *testing.T) { assert.Equal(t, testConfig.ACM.ExcludeRule.TimeAfter, now) assert.Equal(t, testConfig.ACM.IncludeRule.TimeAfter, now) } + +func TestGetExclusionTag(t *testing.T) { + tests := []struct { + name string + want string + ExcludeRule FilterRule + }{ + { + name: "empty", + want: DefaultAwsResourceExclusionTagKey, + }, + { + name: "exclude custom tag", + ExcludeRule: FilterRule{ + Tag: aws.String("my-custom-tag"), + }, + want: "my-custom-tag", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testConfig := &Config{} + testConfig.ACM = ResourceType{ + ExcludeRule: tt.ExcludeRule, + } + + require.Equal(t, tt.want, testConfig.ACM.getExclusionTag()) + }) + } +} + +func TestShouldIncludeBasedOnTag(t *testing.T) { + timeIn2h := time.Now().Add(2 * time.Hour) + + type arg struct { + ExcludeRule FilterRule + ProtectUntilExpire bool + } + tests := []struct { + name string + given arg + when map[string]string + expect bool + }{ + { + name: "should exclude resource, with default tag", + given: arg{}, + when: map[string]string{DefaultAwsResourceExclusionTagKey: "true"}, + expect: false, + }, + { + name: "should exclude resource, with custom tag", + given: arg{ + ExcludeRule: FilterRule{ + Tag: aws.String("my-custom-skip-tag"), + }, + ProtectUntilExpire: false, + }, + when: map[string]string{"my-custom-skip-tag": "true"}, + expect: false, + }, + { + name: "should include resource when not explicitly set to true", + given: arg{ + ExcludeRule: FilterRule{ + Tag: aws.String(DefaultAwsResourceExclusionTagKey), + }, + ProtectUntilExpire: false, + }, + when: map[string]string{DefaultAwsResourceExclusionTagKey: "false"}, + expect: true, + }, + { + name: "should include resource when no tags are set", + given: arg{ + ExcludeRule: FilterRule{ + Tag: aws.String(DefaultAwsResourceExclusionTagKey), + }, + ProtectUntilExpire: false, + }, + when: map[string]string{}, + expect: true, + }, + { + name: "should skip resource with protect until expire is set", + given: arg{ + ExcludeRule: FilterRule{}, + ProtectUntilExpire: true, + }, + when: map[string]string{CloudNukeAfterExclusionTagKey: timeIn2h.Format(time.RFC3339)}, + expect: false, + }, + { + name: "should include resource with if protection expire is in the past", + given: arg{ + ExcludeRule: FilterRule{}, + ProtectUntilExpire: true, + }, + when: map[string]string{CloudNukeAfterExclusionTagKey: time.Now().Format(time.RFC3339)}, + expect: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := ResourceType{ + ExcludeRule: tt.given.ExcludeRule, + ProtectUntilExpire: tt.given.ProtectUntilExpire, + } + + require.Equal(t, tt.expect, r.ShouldIncludeBasedOnTag(tt.when)) + }) + } +} + +func TestShouldIncludeWithTags(t *testing.T) { + tests := []struct { + name string + tags map[string]string + want bool + }{ + { + name: "should include when there resource has no tags", + tags: map[string]string{}, + want: true, + }, + { + name: "should include when there resource has tags", + tags: map[string]string{"env": "production"}, + want: true, + }, + { + name: "should exclude when there resource has default skip tag", + tags: map[string]string{DefaultAwsResourceExclusionTagKey: "true"}, + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testConfig := &Config{ + ACM: ResourceType{}, + } + + assert.Equal(t, tt.want, testConfig.ACM.ShouldInclude(ResourceValue{ + Tags: tt.tags, + })) + }) + } +} From 7f4828dddf1fb766e68f66b9c9df816d71cd42b6 Mon Sep 17 00:00:00 2001 From: AJ Date: Sun, 22 Dec 2024 20:47:23 +0000 Subject: [PATCH 2/5] add logic for tag_exist, part of #822. --- README.md | 8 ++++ config/config.go | 42 ++++++++++++++---- config/config_test.go | 100 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 141 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index b1391925..8bbd7765 100644 --- a/README.md +++ b/README.md @@ -535,6 +535,14 @@ s3: exclude: tag: 'foo' ``` + +You can also exclude a resource by specifying only the tag key. + +```yaml +s3: + exclude: + tag_exist: 'bar' +``` #### Timeout You have the flexibility to set individual timeout options for specific resources. The execution will pause until the designated timeout is reached for each resource. ```yaml diff --git a/config/config.go b/config/config.go index d9f0b0a8..719328ca 100644 --- a/config/config.go +++ b/config/config.go @@ -272,6 +272,7 @@ type FilterRule struct { TimeAfter *time.Time `yaml:"time_after"` TimeBefore *time.Time `yaml:"time_before"` Tag *string `yaml:"tag"` // A tag to filter resources by. (e.g., If set under ExcludedRule, resources with this tag will be excluded). + TagExist *string `yaml:"tag_exist"` } type Expression struct { @@ -401,17 +402,40 @@ func (r ResourceType) ShouldIncludeBasedOnTag(tags map[string]string) bool { } if r.ProtectUntilExpire { - // Check if the tags contain "cloud-nuke-after" and if the date is before today. - if value, ok := tags[CloudNukeAfterExclusionTagKey]; ok { - nukeDate, err := ParseTimestamp(value) - if err == nil { - if !nukeDate.Before(time.Now()) { - logging.Debugf("[Skip] the resource is protected until %v", nukeDate) - return false - } + return nukeAfterTagValue(tags) + } + + return true +} + +func nukeAfterTagValue(tags map[string]string) bool { + // Check if the tags contain "cloud-nuke-after" and if the date is before today. + if value, ok := tags[CloudNukeAfterExclusionTagKey]; ok { + nukeDate, err := ParseTimestamp(value) + if err == nil { + if !nukeDate.Before(time.Now()) { + logging.Debugf("[Skip] the resource is protected until %v", nukeDate) + return false } } } + + return true +} + +func (r ResourceType) ShouldIncludeBasedOnTagName(tags map[string]string) bool { + if r.ExcludeRule.TagExist != nil { + tagName := *r.ExcludeRule.TagExist + + if _, ok := tags[tagName]; ok { + return false + } + } + + if r.ProtectUntilExpire { + return nukeAfterTagValue(tags) + } + return true } @@ -422,6 +446,8 @@ func (r ResourceType) ShouldInclude(value ResourceValue) bool { return false } else if value.Tags != nil && len(value.Tags) != 0 && !r.ShouldIncludeBasedOnTag(value.Tags) { return false + } else if value.Tags != nil && len(value.Tags) != 0 && !r.ShouldIncludeBasedOnTagName(value.Tags) { + return false } return true diff --git a/config/config_test.go b/config/config_test.go index 161969f5..08c9ce0f 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -456,7 +456,105 @@ func TestShouldIncludeWithTags(t *testing.T) { ACM: ResourceType{}, } - assert.Equal(t, tt.want, testConfig.ACM.ShouldInclude(ResourceValue{ + require.Equal(t, tt.want, testConfig.ACM.ShouldInclude(ResourceValue{ + Tags: tt.tags, + })) + }) + } +} + +func TestShouldIncludeBasedOnTagName(t *testing.T) { + timeIn2h := time.Now().Add(2 * time.Hour) + + type arg struct { + ExcludeRule FilterRule + ProtectUntilExpire bool + } + tests := []struct { + name string + fields arg + when map[string]string + expected bool + }{ + { + name: "should include if no tag set", + fields: arg{}, + when: nil, + expected: true, + }, + { + name: "should exclude if tag was found", + fields: arg{ + ExcludeRule: FilterRule{ + TagExist: aws.String("if-this-tag-exist-skip-resource"), + }, + }, + when: map[string]string{"if-this-tag-exist-skip-resource": "true"}, + expected: false, + }, + + { + name: "should skip resource with protect until expire is set", + fields: arg{ + ExcludeRule: FilterRule{}, + ProtectUntilExpire: true, + }, + when: map[string]string{CloudNukeAfterExclusionTagKey: timeIn2h.Format(time.RFC3339)}, + expected: false, + }, + { + name: "should include resource with if protection expire is in the past", + fields: arg{ + ExcludeRule: FilterRule{}, + ProtectUntilExpire: true, + }, + when: map[string]string{CloudNukeAfterExclusionTagKey: time.Now().Format(time.RFC3339)}, + expected: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := ResourceType{ + ExcludeRule: tt.fields.ExcludeRule, + ProtectUntilExpire: tt.fields.ProtectUntilExpire, + } + + require.Equal(t, tt.expected, r.ShouldIncludeBasedOnTagName(tt.when)) + }) + } +} + +func TestResourceType_ShouldInclude(t *testing.T) { + tests := []struct { + name string + excludeRule FilterRule + tags map[string]string + want bool + }{ + { + name: "should include if no config pass", + want: true, + }, + { + name: "skip if tag with given name was found", + excludeRule: FilterRule{ + TagExist: aws.String("if-this-tag-exist-skip-resource"), + }, + tags: map[string]string{"if-this-tag-exist-skip-resource": "true"}, + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testConfig := &Config{ + ACM: ResourceType{ + ExcludeRule: tt.excludeRule, + }, + } + + require.Equal(t, tt.want, testConfig.ACM.ShouldInclude(ResourceValue{ Tags: tt.tags, })) }) From c74dcded0b0cecf468f5d809638f544cb3080152 Mon Sep 17 00:00:00 2001 From: AJ Date: Wed, 25 Dec 2024 09:52:37 +0000 Subject: [PATCH 3/5] Revert "add logic for tag_exist, part of #822." This reverts commit f67b0deea62fddfd93dd801e1a65409d98e7a340. --- README.md | 8 ---- config/config.go | 42 ++++-------------- config/config_test.go | 100 +----------------------------------------- 3 files changed, 9 insertions(+), 141 deletions(-) diff --git a/README.md b/README.md index 8bbd7765..b1391925 100644 --- a/README.md +++ b/README.md @@ -535,14 +535,6 @@ s3: exclude: tag: 'foo' ``` - -You can also exclude a resource by specifying only the tag key. - -```yaml -s3: - exclude: - tag_exist: 'bar' -``` #### Timeout You have the flexibility to set individual timeout options for specific resources. The execution will pause until the designated timeout is reached for each resource. ```yaml diff --git a/config/config.go b/config/config.go index 719328ca..d9f0b0a8 100644 --- a/config/config.go +++ b/config/config.go @@ -272,7 +272,6 @@ type FilterRule struct { TimeAfter *time.Time `yaml:"time_after"` TimeBefore *time.Time `yaml:"time_before"` Tag *string `yaml:"tag"` // A tag to filter resources by. (e.g., If set under ExcludedRule, resources with this tag will be excluded). - TagExist *string `yaml:"tag_exist"` } type Expression struct { @@ -402,40 +401,17 @@ func (r ResourceType) ShouldIncludeBasedOnTag(tags map[string]string) bool { } if r.ProtectUntilExpire { - return nukeAfterTagValue(tags) - } - - return true -} - -func nukeAfterTagValue(tags map[string]string) bool { - // Check if the tags contain "cloud-nuke-after" and if the date is before today. - if value, ok := tags[CloudNukeAfterExclusionTagKey]; ok { - nukeDate, err := ParseTimestamp(value) - if err == nil { - if !nukeDate.Before(time.Now()) { - logging.Debugf("[Skip] the resource is protected until %v", nukeDate) - return false + // Check if the tags contain "cloud-nuke-after" and if the date is before today. + if value, ok := tags[CloudNukeAfterExclusionTagKey]; ok { + nukeDate, err := ParseTimestamp(value) + if err == nil { + if !nukeDate.Before(time.Now()) { + logging.Debugf("[Skip] the resource is protected until %v", nukeDate) + return false + } } } } - - return true -} - -func (r ResourceType) ShouldIncludeBasedOnTagName(tags map[string]string) bool { - if r.ExcludeRule.TagExist != nil { - tagName := *r.ExcludeRule.TagExist - - if _, ok := tags[tagName]; ok { - return false - } - } - - if r.ProtectUntilExpire { - return nukeAfterTagValue(tags) - } - return true } @@ -446,8 +422,6 @@ func (r ResourceType) ShouldInclude(value ResourceValue) bool { return false } else if value.Tags != nil && len(value.Tags) != 0 && !r.ShouldIncludeBasedOnTag(value.Tags) { return false - } else if value.Tags != nil && len(value.Tags) != 0 && !r.ShouldIncludeBasedOnTagName(value.Tags) { - return false } return true diff --git a/config/config_test.go b/config/config_test.go index 08c9ce0f..161969f5 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -456,105 +456,7 @@ func TestShouldIncludeWithTags(t *testing.T) { ACM: ResourceType{}, } - require.Equal(t, tt.want, testConfig.ACM.ShouldInclude(ResourceValue{ - Tags: tt.tags, - })) - }) - } -} - -func TestShouldIncludeBasedOnTagName(t *testing.T) { - timeIn2h := time.Now().Add(2 * time.Hour) - - type arg struct { - ExcludeRule FilterRule - ProtectUntilExpire bool - } - tests := []struct { - name string - fields arg - when map[string]string - expected bool - }{ - { - name: "should include if no tag set", - fields: arg{}, - when: nil, - expected: true, - }, - { - name: "should exclude if tag was found", - fields: arg{ - ExcludeRule: FilterRule{ - TagExist: aws.String("if-this-tag-exist-skip-resource"), - }, - }, - when: map[string]string{"if-this-tag-exist-skip-resource": "true"}, - expected: false, - }, - - { - name: "should skip resource with protect until expire is set", - fields: arg{ - ExcludeRule: FilterRule{}, - ProtectUntilExpire: true, - }, - when: map[string]string{CloudNukeAfterExclusionTagKey: timeIn2h.Format(time.RFC3339)}, - expected: false, - }, - { - name: "should include resource with if protection expire is in the past", - fields: arg{ - ExcludeRule: FilterRule{}, - ProtectUntilExpire: true, - }, - when: map[string]string{CloudNukeAfterExclusionTagKey: time.Now().Format(time.RFC3339)}, - expected: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := ResourceType{ - ExcludeRule: tt.fields.ExcludeRule, - ProtectUntilExpire: tt.fields.ProtectUntilExpire, - } - - require.Equal(t, tt.expected, r.ShouldIncludeBasedOnTagName(tt.when)) - }) - } -} - -func TestResourceType_ShouldInclude(t *testing.T) { - tests := []struct { - name string - excludeRule FilterRule - tags map[string]string - want bool - }{ - { - name: "should include if no config pass", - want: true, - }, - { - name: "skip if tag with given name was found", - excludeRule: FilterRule{ - TagExist: aws.String("if-this-tag-exist-skip-resource"), - }, - tags: map[string]string{"if-this-tag-exist-skip-resource": "true"}, - want: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testConfig := &Config{ - ACM: ResourceType{ - ExcludeRule: tt.excludeRule, - }, - } - - require.Equal(t, tt.want, testConfig.ACM.ShouldInclude(ResourceValue{ + assert.Equal(t, tt.want, testConfig.ACM.ShouldInclude(ResourceValue{ Tags: tt.tags, })) }) From a244989c52e0f3565627cb6504eaab80602126a7 Mon Sep 17 00:00:00 2001 From: AJ Date: Wed, 25 Dec 2024 10:19:22 +0000 Subject: [PATCH 4/5] change the behaviour to allow matching only on the tag name when excluding resources from being nuked, fix #822 --- README.md | 5 +++-- config/config.go | 2 +- config/config_test.go | 37 +++++++++++++++++++++++++++---------- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index b1391925..4d66f609 100644 --- a/README.md +++ b/README.md @@ -528,12 +528,13 @@ s3: #### Tag Filter You can also exclude resources by tags. The following config will exclude all s3 buckets that have a tag with key `foo` -and value `true` (case-insensitive). +if the specified tag has a value, it must be set to `true` (case-insensitive), +if the tag value is not set, cloud-nuke will only check for the tag name. ```yaml s3: exclude: - tag: 'foo' + tag: 'foo' # exclude if tag foo exists with empty value or 'true' ``` #### Timeout You have the flexibility to set individual timeout options for specific resources. The execution will pause until the designated timeout is reached for each resource. diff --git a/config/config.go b/config/config.go index d9f0b0a8..da87cd7a 100644 --- a/config/config.go +++ b/config/config.go @@ -395,7 +395,7 @@ func (r ResourceType) ShouldIncludeBasedOnTag(tags map[string]string) bool { // Handle exclude rule first exclusionTag := r.getExclusionTag() if value, ok := tags[exclusionTag]; ok { - if strings.ToLower(value) == "true" { + if strings.ToLower(value) == "true" || value == "" { return false } } diff --git a/config/config_test.go b/config/config_test.go index 161969f5..76e3c0c8 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -319,11 +319,11 @@ func TestGetExclusionTag(t *testing.T) { ExcludeRule FilterRule }{ { - name: "empty", + name: "empty config returns default tag", want: DefaultAwsResourceExclusionTagKey, }, { - name: "exclude custom tag", + name: "custom exclusion tag is returned", ExcludeRule: FilterRule{ Tag: aws.String("my-custom-tag"), }, @@ -357,13 +357,13 @@ func TestShouldIncludeBasedOnTag(t *testing.T) { expect bool }{ { - name: "should exclude resource, with default tag", + name: "should include resource with default exclude tag", given: arg{}, when: map[string]string{DefaultAwsResourceExclusionTagKey: "true"}, expect: false, }, { - name: "should exclude resource, with custom tag", + name: "should include resource with custom exclude tag", given: arg{ ExcludeRule: FilterRule{ Tag: aws.String("my-custom-skip-tag"), @@ -374,7 +374,24 @@ func TestShouldIncludeBasedOnTag(t *testing.T) { expect: false, }, { - name: "should include resource when not explicitly set to true", + name: "should include resource with custom exclude tag and empty value", + given: arg{ + ExcludeRule: FilterRule{ + Tag: aws.String("my-custom-skip-tag"), + }, + ProtectUntilExpire: false, + }, + when: map[string]string{"my-custom-skip-tag": ""}, + expect: false, + }, + { + name: "should include resource with empty default exclude tag value", + given: arg{}, + when: map[string]string{DefaultAwsResourceExclusionTagKey: ""}, + expect: false, + }, + { + name: "should include resource when exclude tag is not set to true", given: arg{ ExcludeRule: FilterRule{ Tag: aws.String(DefaultAwsResourceExclusionTagKey), @@ -396,7 +413,7 @@ func TestShouldIncludeBasedOnTag(t *testing.T) { expect: true, }, { - name: "should skip resource with protect until expire is set", + name: "should include resource when protection expires in the future", given: arg{ ExcludeRule: FilterRule{}, ProtectUntilExpire: true, @@ -405,7 +422,7 @@ func TestShouldIncludeBasedOnTag(t *testing.T) { expect: false, }, { - name: "should include resource with if protection expire is in the past", + name: "should include resource when protection expired in the past", given: arg{ ExcludeRule: FilterRule{}, ProtectUntilExpire: true, @@ -434,17 +451,17 @@ func TestShouldIncludeWithTags(t *testing.T) { want bool }{ { - name: "should include when there resource has no tags", + name: "should include when resource has no tags", tags: map[string]string{}, want: true, }, { - name: "should include when there resource has tags", + name: "should include when resource has tags", tags: map[string]string{"env": "production"}, want: true, }, { - name: "should exclude when there resource has default skip tag", + name: "should include when resource has default skip tag set", tags: map[string]string{DefaultAwsResourceExclusionTagKey: "true"}, want: false, }, From da874fc2e692152b502e16753e50636ff79c8e6a Mon Sep 17 00:00:00 2001 From: AJ Date: Fri, 27 Dec 2024 06:12:51 +0000 Subject: [PATCH 5/5] change the behaviour of excluding resources to allow specifying the tag value using a regular expression, fix #822 --- README.md | 15 +++++++++++---- config/config.go | 21 ++++++++++++++++----- config/config_test.go | 31 ++++++++++++++++++++++++++++--- 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 4d66f609..1b9868b1 100644 --- a/README.md +++ b/README.md @@ -527,14 +527,21 @@ s3: #### Tag Filter -You can also exclude resources by tags. The following config will exclude all s3 buckets that have a tag with key `foo` -if the specified tag has a value, it must be set to `true` (case-insensitive), -if the tag value is not set, cloud-nuke will only check for the tag name. +You can also exclude resources by tags. The following configuration will exclude all S3 buckets that have a tag with the key `foo`. +By default, we will check if the tag's value is set to `true` (case-insensitive). ```yaml s3: exclude: - tag: 'foo' # exclude if tag foo exists with empty value or 'true' + tag: 'foo' # exclude if tag foo exists with value of 'true' +``` + +You can also overwrite the expected value by specifying `tag_value` (you can use regular expressions). +```yaml +s3: + exclude: + tag: 'foo' + tag_value: 'dev-.*' ``` #### Timeout You have the flexibility to set individual timeout options for specific resources. The execution will pause until the designated timeout is reached for each resource. diff --git a/config/config.go b/config/config.go index da87cd7a..f4ae2aa9 100644 --- a/config/config.go +++ b/config/config.go @@ -13,10 +13,11 @@ import ( ) const ( - DefaultAwsResourceExclusionTagKey = "cloud-nuke-excluded" - CloudNukeAfterExclusionTagKey = "cloud-nuke-after" - CloudNukeAfterTimeFormat = time.RFC3339 - CloudNukeAfterTimeFormatLegacy = time.DateTime + DefaultAwsResourceExclusionTagKey = "cloud-nuke-excluded" + DefaultAwsResourceExclusionTagValue = "true" + CloudNukeAfterExclusionTagKey = "cloud-nuke-after" + CloudNukeAfterTimeFormat = time.RFC3339 + CloudNukeAfterTimeFormatLegacy = time.DateTime ) // Config - the config object we pass around @@ -272,6 +273,7 @@ type FilterRule struct { TimeAfter *time.Time `yaml:"time_after"` TimeBefore *time.Time `yaml:"time_before"` Tag *string `yaml:"tag"` // A tag to filter resources by. (e.g., If set under ExcludedRule, resources with this tag will be excluded). + TagValue *Expression `yaml:"tag_value"` } type Expression struct { @@ -377,6 +379,14 @@ func (r ResourceType) getExclusionTag() string { return DefaultAwsResourceExclusionTagKey } +func (r ResourceType) getExclusionTagValue() *Expression { + if r.ExcludeRule.TagValue != nil { + return r.ExcludeRule.TagValue + } + + return &Expression{RE: *regexp.MustCompile(DefaultAwsResourceExclusionTagValue)} +} + func ParseTimestamp(timestamp string) (*time.Time, error) { parsed, err := time.Parse(CloudNukeAfterTimeFormat, timestamp) if err != nil { @@ -394,8 +404,9 @@ func ParseTimestamp(timestamp string) (*time.Time, error) { func (r ResourceType) ShouldIncludeBasedOnTag(tags map[string]string) bool { // Handle exclude rule first exclusionTag := r.getExclusionTag() + exclusionTagValue := r.getExclusionTagValue() if value, ok := tags[exclusionTag]; ok { - if strings.ToLower(value) == "true" || value == "" { + if matches(strings.ToLower(value), []Expression{*exclusionTagValue}) { return false } } diff --git a/config/config_test.go b/config/config_test.go index 76e3c0c8..4b2a1c42 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -378,6 +378,9 @@ func TestShouldIncludeBasedOnTag(t *testing.T) { given: arg{ ExcludeRule: FilterRule{ Tag: aws.String("my-custom-skip-tag"), + TagValue: &Expression{ + RE: *regexp.MustCompile(""), + }, }, ProtectUntilExpire: false, }, @@ -385,9 +388,31 @@ func TestShouldIncludeBasedOnTag(t *testing.T) { expect: false, }, { - name: "should include resource with empty default exclude tag value", - given: arg{}, - when: map[string]string{DefaultAwsResourceExclusionTagKey: ""}, + name: "should include resource with custom exclude tag and empty value (using regular expression)", + given: arg{ + ExcludeRule: FilterRule{ + Tag: aws.String("my-custom-skip-tag"), + TagValue: &Expression{ + RE: *regexp.MustCompile(".*"), + }, + }, + ProtectUntilExpire: false, + }, + when: map[string]string{"my-custom-skip-tag": ""}, + expect: false, + }, + { + name: "should include resource with custom exclude tag and prefix value (using regular expression)", + given: arg{ + ExcludeRule: FilterRule{ + Tag: aws.String("my-custom-skip-tag"), + TagValue: &Expression{ + RE: *regexp.MustCompile("protected-.*"), + }, + }, + ProtectUntilExpire: false, + }, + when: map[string]string{"my-custom-skip-tag": "protected-database"}, expect: false, }, {