Skip to content

Commit

Permalink
refactor(aws): Refactor AmazonCachingAgentFilter for better reuse (#5094
Browse files Browse the repository at this point in the history
)
  • Loading branch information
gavinbunney committed Nov 12, 2020
1 parent c0c90db commit eb9fb4b
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 94 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package com.netflix.spinnaker.clouddriver.aws.provider.agent;

import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties("aws.caching.filter")
public class AmazonCachingAgentFilter {

List<TagFilterOption> includeTags;
List<TagFilterOption> excludeTags;

public static class TagFilterOption {
String name;
String value;

public TagFilterOption() {}

public TagFilterOption(String name, String value) {
this.name = name;
this.value = value;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}
}

public List<TagFilterOption> getIncludeTags() {
return includeTags;
}

public void setIncludeTags(List<TagFilterOption> includeTags) {
this.includeTags = includeTags;
}

public List<TagFilterOption> getExcludeTags() {
return excludeTags;
}

public void setExcludeTags(List<TagFilterOption> excludeTags) {
this.excludeTags = excludeTags;
}

public boolean hasTagFilter() {
return this.hasIncludeTagFilter() || this.hasExcludeTagFilter();
}

public boolean hasIncludeTagFilter() {
return this.includeTags != null && this.includeTags.size() > 0;
}

public boolean hasExcludeTagFilter() {
return this.excludeTags != null && this.excludeTags.size() > 0;
}

/**
* ResourceTag is a helper wrapper for AWS resources which are taggable, to convert their specific
* types from the AWS SDK into a generic type for comparison by this agent filter.
*/
public static class ResourceTag {
final String key;
final String value;

public ResourceTag(String key, String value) {
this.key = key;
this.value = value;
}
}

/**
* Determine if the resource with the given set of tags should be retained, that is, if the caller
* should discard or keep it based on the include/exclude filters configured.
*/
public boolean shouldRetainResource(List<ResourceTag> tags) {

// retain the resource by default if there isn't an include filter setup
boolean retainResource = !this.hasIncludeTagFilter();

if (this.hasIncludeTagFilter()) {
retainResource =
this.includeTags.stream()
.anyMatch(
filter ->
tags.stream()
.anyMatch(tag -> tagFilterOptionMatchesResourceTag(filter, tag)));
}

// exclude takes precedence over include so runs second if the resource is still being retained
if (retainResource && this.hasExcludeTagFilter()) {
retainResource =
this.excludeTags.stream()
.noneMatch(
filter ->
tags.stream()
.anyMatch(tag -> tagFilterOptionMatchesResourceTag(filter, tag)));
}

return retainResource;
}

private boolean tagFilterOptionMatchesResourceTag(
TagFilterOption tagFilterOption, ResourceTag resourceTag) {
if (resourceTag.key == null || !resourceTag.key.matches(tagFilterOption.name)) {
return false;
}

return StringUtils.isEmpty(tagFilterOption.getValue())
|| (resourceTag.value != null && resourceTag.value.matches(tagFilterOption.value));
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class ClusterCachingAgent implements CachingAgent, OnDemandAgent, AccountAware,
final ObjectMapper objectMapper
final Registry registry
final EddaTimeoutConfig eddaTimeoutConfig
final AmazonCachingAgentFilterConfiguration amazonCachingAgentFilterConfiguration
final AmazonCachingAgentFilter amazonCachingAgentFilter

final OnDemandMetricsSupport metricsSupport

Expand All @@ -97,7 +97,7 @@ class ClusterCachingAgent implements CachingAgent, OnDemandAgent, AccountAware,
ObjectMapper objectMapper,
Registry registry,
EddaTimeoutConfig eddaTimeoutConfig,
AmazonCachingAgentFilterConfiguration amazonCachingAgentFilterConfiguration) {
AmazonCachingAgentFilter amazonCachingAgentFilter) {
this.amazonCloudProvider = amazonCloudProvider
this.amazonClientProvider = amazonClientProvider
this.account = account
Expand All @@ -106,7 +106,7 @@ class ClusterCachingAgent implements CachingAgent, OnDemandAgent, AccountAware,
this.registry = registry
this.eddaTimeoutConfig = eddaTimeoutConfig
this.metricsSupport = new OnDemandMetricsSupport(registry, this, "${amazonCloudProvider.id}:${OnDemandType.ServerGroup}")
this.amazonCachingAgentFilterConfiguration = amazonCachingAgentFilterConfiguration
this.amazonCachingAgentFilter = amazonCachingAgentFilter
}

@Override
Expand Down Expand Up @@ -310,30 +310,13 @@ class ClusterCachingAgent implements CachingAgent, OnDemandAgent, AccountAware,
asgs = asgs.findAll { it.status == null }

// filter asg if there is any filter configuration established
if (amazonCachingAgentFilterConfiguration.includeTags?.size() > 0 || amazonCachingAgentFilterConfiguration.excludeTags?.size() > 0) {
if (amazonCachingAgentFilter.hasTagFilter()) {
asgs = asgs.findAll { asg ->

// retain the asg by default if there isn't an include filter setup
def retainASG = amazonCachingAgentFilterConfiguration.includeTags?.size() <= 0

asg.tags?.each { tag ->
amazonCachingAgentFilterConfiguration.includeTags?.each {includeTag ->
if (tag.getKey()?.matches(includeTag.name) && (!includeTag?.getValue() || tag?.getValue() == includeTag?.value || tag?.getValue()?.matches(includeTag?.value))) {
retainASG = true
return
}
}

// exclude takes precedence over include so runs second
amazonCachingAgentFilterConfiguration.excludeTags?.each {excludeTag ->
if (tag.getKey()?.matches(excludeTag.name) && (!excludeTag?.getValue() || tag?.getValue() == excludeTag?.value || tag?.getValue()?.matches(excludeTag?.value))) {
retainASG = false
return
}
}
def asgTags = asg.tags?.collect {
new AmazonCachingAgentFilter.ResourceTag(it.key, it.value)
}

return retainASG
return amazonCachingAgentFilter.shouldRetainResource(asgTags)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
import com.netflix.spinnaker.clouddriver.aws.provider.AwsInfrastructureProvider;
import com.netflix.spinnaker.clouddriver.aws.provider.AwsProvider;
import com.netflix.spinnaker.clouddriver.aws.provider.agent.AmazonApplicationLoadBalancerCachingAgent;
import com.netflix.spinnaker.clouddriver.aws.provider.agent.AmazonCachingAgentFilterConfiguration;
import com.netflix.spinnaker.clouddriver.aws.provider.agent.AmazonCachingAgentFilter;
import com.netflix.spinnaker.clouddriver.aws.provider.agent.AmazonCertificateCachingAgent;
import com.netflix.spinnaker.clouddriver.aws.provider.agent.AmazonCloudFormationCachingAgent;
import com.netflix.spinnaker.clouddriver.aws.provider.agent.AmazonElasticIpCachingAgent;
Expand Down Expand Up @@ -123,7 +123,7 @@ public static BuildResult buildAwsProviderAgents(
ObjectMapper objectMapper,
Registry registry,
EddaTimeoutConfig eddaTimeoutConfig,
AmazonCachingAgentFilterConfiguration amazonCachingAgentFilterConfiguration,
AmazonCachingAgentFilter amazonCachingAgentFilter,
AwsProvider awsProvider,
AmazonCloudProvider amazonCloudProvider,
DynamicConfigService dynamicConfigService,
Expand All @@ -146,7 +146,7 @@ public static BuildResult buildAwsProviderAgents(
objectMapper,
registry,
eddaTimeoutConfig,
amazonCachingAgentFilterConfiguration));
amazonCachingAgentFilter));
newlyAddedAgents.add(
new LaunchConfigCachingAgent(
amazonClientProvider, credentials, region.getName(), objectMapper, registry));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import com.netflix.spinnaker.clouddriver.aws.provider.AwsCleanupProvider;
import com.netflix.spinnaker.clouddriver.aws.provider.AwsInfrastructureProvider;
import com.netflix.spinnaker.clouddriver.aws.provider.AwsProvider;
import com.netflix.spinnaker.clouddriver.aws.provider.agent.AmazonCachingAgentFilterConfiguration;
import com.netflix.spinnaker.clouddriver.aws.provider.agent.AmazonCachingAgentFilter;
import com.netflix.spinnaker.clouddriver.aws.provider.agent.ImageCachingAgent;
import com.netflix.spinnaker.clouddriver.aws.provider.agent.ReservationReportCachingAgent;
import com.netflix.spinnaker.clouddriver.aws.provider.config.ProviderHelpers;
Expand Down Expand Up @@ -74,7 +74,7 @@ public class AmazonCredentialsLifecycleHandler
private final Optional<ExecutorService> reservationReportPool;
private final Optional<Collection<AgentProvider>> agentProviders;
private final EddaTimeoutConfig eddaTimeoutConfig;
private final AmazonCachingAgentFilterConfiguration amazonCachingAgentFilterConfiguration;
private final AmazonCachingAgentFilter amazonCachingAgentFilter;
private final DynamicConfigService dynamicConfigService;
private final DeployDefaults deployDefaults;
private final CredentialsRepository<NetflixAmazonCredentials>
Expand Down Expand Up @@ -177,7 +177,7 @@ private void scheduleAWSProviderAgents(NetflixAmazonCredentials credentials) {
objectMapper,
registry,
eddaTimeoutConfig,
amazonCachingAgentFilterConfiguration,
amazonCachingAgentFilter,
awsProvider,
amazonCloudProvider,
dynamicConfigService,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.netflix.spinnaker.clouddriver.aws.provider.agent

import spock.lang.Shared
import spock.lang.Specification

class AmazonCachingAgentFilterSpec extends Specification {

@Shared
AmazonCachingAgentFilter filter = new AmazonCachingAgentFilter()

void "should retain based on tag criteria"() {
given:
filter.includeTags = includeTags
filter.excludeTags = excludeTags

when:
def result = filter.shouldRetainResource(resourceTags)

then:
result == expected

where:
resourceTags | includeTags | excludeTags | expected
[resourceTag("hello", "goodbye")] | null | null | true
[resourceTag("hello", "goodbye")] | [filterTag("hello")] | null | true
[resourceTag("hello", "goodbye")] | [filterTag("hello", "goodbye")] | null | true
[resourceTag("hello", "goodbye")] | [filterTag("hello", "goo")] | null | false
[resourceTag("hello", "goodbye")] | [filterTag("hello", ".*bye")] | null | true
[resourceTag("hello", "goodbye")] | [filterTag(".*a.*")] | null | false
[resourceTag("hello", "goodbye")] | null | [filterTag("hello")] | false
[resourceTag("hello", "goodbye")] | null | [filterTag("hello", "goodbye")] | false
[resourceTag("hello", "goodbye")] | null | [filterTag(".*a.*")] | true
[resourceTag("hello", "goodbye")] | [filterTag("hello", "goodbye")] | [filterTag("Name")] | true
[resourceTag("hello", "goodbye")] | [filterTag("hello", "goodbye")] | [filterTag("hello")] | false
[resourceTag("hello", "goodbye")] | [filterTag(".*", "ciao")] | [filterTag("hello", ".*")] | false
[resourceTag("hello", "goodbye"),
resourceTag("Name", "primary"),] | [filterTag("hello")] | null | true
[resourceTag("hello", "goodbye"),
resourceTag("Name", "primary"),] | null | [filterTag("hello")] | false
}

private static def resourceTag(String name = null, String value = null) {
return new AmazonCachingAgentFilter.ResourceTag(name, value)
}

private static def filterTag(String name = null, String value = null) {
return new AmazonCachingAgentFilter.TagFilterOption(name, value)
}
}
Loading

0 comments on commit eb9fb4b

Please sign in to comment.