diff options
Diffstat (limited to 'config-application-package/src/main/java/com/yahoo/config/application/OverrideProcessor.java')
-rw-r--r-- | config-application-package/src/main/java/com/yahoo/config/application/OverrideProcessor.java | 76 |
1 files changed, 54 insertions, 22 deletions
diff --git a/config-application-package/src/main/java/com/yahoo/config/application/OverrideProcessor.java b/config-application-package/src/main/java/com/yahoo/config/application/OverrideProcessor.java index d7efca3b723..21bb193ef93 100644 --- a/config-application-package/src/main/java/com/yahoo/config/application/OverrideProcessor.java +++ b/config-application-package/src/main/java/com/yahoo/config/application/OverrideProcessor.java @@ -4,6 +4,7 @@ package com.yahoo.config.application; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.RegionName; +import com.yahoo.config.provision.Tags; import com.yahoo.text.XML; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -23,7 +24,7 @@ import java.util.logging.Logger; import java.util.stream.Collectors; /** - * Handles overrides in a XML document according to the rules defined for multi environment application packages. + * Handles overrides in a XML document according to the rules defined for multi-environment application packages. * * Rules: * @@ -41,16 +42,19 @@ class OverrideProcessor implements PreProcessor { private final InstanceName instance; private final Environment environment; private final RegionName region; + private final Tags tags; private static final String ID_ATTRIBUTE = "id"; private static final String INSTANCE_ATTRIBUTE = "instance"; private static final String ENVIRONMENT_ATTRIBUTE = "environment"; private static final String REGION_ATTRIBUTE = "region"; + private static final String TAGS_ATTRIBUTE = "tags"; - public OverrideProcessor(InstanceName instance, Environment environment, RegionName region) { + public OverrideProcessor(InstanceName instance, Environment environment, RegionName region, Tags tags) { this.instance = instance; this.environment = environment; this.region = region; + this.tags = tags; } public Document process(Document input) throws TransformerException { @@ -80,6 +84,7 @@ class OverrideProcessor implements PreProcessor { child.removeAttributeNS(XmlPreProcessor.deployNamespaceUri, INSTANCE_ATTRIBUTE); child.removeAttributeNS(XmlPreProcessor.deployNamespaceUri, ENVIRONMENT_ATTRIBUTE); child.removeAttributeNS(XmlPreProcessor.deployNamespaceUri, REGION_ATTRIBUTE); + child.removeAttributeNS(XmlPreProcessor.deployNamespaceUri, TAGS_ATTRIBUTE); } } @@ -87,13 +92,16 @@ class OverrideProcessor implements PreProcessor { Set<InstanceName> instances = context.instances; Set<Environment> environments = context.environments; Set<RegionName> regions = context.regions; + Tags tags = context.tags; if (instances.isEmpty()) instances = getInstances(parent); if (environments.isEmpty()) environments = getEnvironments(parent); if (regions.isEmpty()) regions = getRegions(parent); - return Context.create(instances, environments, regions); + if (tags.isEmpty()) + tags = getTags(parent); + return Context.create(instances, environments, regions, tags); } /** @@ -128,17 +136,21 @@ class OverrideProcessor implements PreProcessor { throw new IllegalArgumentException("Regions in child (" + regions + ") are not a subset of those of the parent (" + context.regions + ") at " + child); } + + Tags tags = getTags(child); + if ( ! tags.isEmpty() && ! context.tags.isEmpty() && ! context.tags.containsAll(tags)) { + throw new IllegalArgumentException("Tags in child (" + environments + + ") are not a subset of those of the parent (" + context.tags + ") at " + child); + } } } - /** - * Prune elements that are not matching our environment and region - */ + /** Prune elements that are not matching our environment and region. */ private void pruneNonMatching(Element parent, List<Element> children) { Iterator<Element> elemIt = children.iterator(); while (elemIt.hasNext()) { Element child = elemIt.next(); - if ( ! matches(getInstances(child), getEnvironments(child), getRegions(child))) { + if ( ! matches(getInstances(child), getEnvironments(child), getRegions(child), getTags(child))) { parent.removeChild(child); elemIt.remove(); } @@ -147,7 +159,8 @@ class OverrideProcessor implements PreProcessor { private boolean matches(Set<InstanceName> elementInstances, Set<Environment> elementEnvironments, - Set<RegionName> elementRegions) { + Set<RegionName> elementRegions, + Tags elementTags) { if ( ! elementInstances.isEmpty()) { // match instance if ( ! elementInstances.contains(instance)) return false; } @@ -164,12 +177,14 @@ class OverrideProcessor implements PreProcessor { if ( ! environment.isMultiRegion() && elementEnvironments.isEmpty() ) return false; } + if ( ! elementTags.isEmpty()) { // match tags + if ( ! elementTags.intersects(tags)) return false; + } + return true; } - /** - * Find the most specific element and remove all others. - */ + /** Find the most specific element and remove all others. */ private void retainMostSpecific(Element parent, List<Element> children, Context context) { // Keep track of elements with highest number of matches (might be more than one element with same tag, need a list) List<Element> bestMatches = new ArrayList<>(); @@ -205,12 +220,15 @@ class OverrideProcessor implements PreProcessor { Set<InstanceName> elementInstances = hasInstance(child) ? getInstances(child) : context.instances; Set<Environment> elementEnvironments = hasEnvironment(child) ? getEnvironments(child) : context.environments; Set<RegionName> elementRegions = hasRegion(child) ? getRegions(child) : context.regions; + Tags elementTags = hasTag(child) ? getTags(child) : context.tags; if ( ! elementInstances.isEmpty() && elementInstances.contains(instance)) currentMatch++; if ( ! elementEnvironments.isEmpty() && elementEnvironments.contains(environment)) currentMatch++; if ( ! elementRegions.isEmpty() && elementRegions.contains(region)) currentMatch++; + if ( elementTags.intersects(tags)) + currentMatch++; return currentMatch; } @@ -233,16 +251,14 @@ class OverrideProcessor implements PreProcessor { return false; } - /** - * Retains all elements where at least one element is overridden. Removes non-overridden elements from map. - */ + /** Retains all elements where at least one element is overridden. Removes non-overridden elements from map. */ private void retainOverriddenElements(Map<String, List<Element>> elementsByTagName) { Iterator<Map.Entry<String, List<Element>>> it = elementsByTagName.entrySet().iterator(); while (it.hasNext()) { List<Element> elements = it.next().getValue(); boolean hasOverrides = false; for (Element element : elements) { - if (hasEnvironment(element) || hasRegion(element)) { + if (hasInstance(element) || hasEnvironment(element) || hasRegion(element) || hasTag(element)) { hasOverrides = true; } } @@ -264,24 +280,34 @@ class OverrideProcessor implements PreProcessor { return element.hasAttributeNS(XmlPreProcessor.deployNamespaceUri, ENVIRONMENT_ATTRIBUTE); } + private boolean hasTag(Element element) { + return element.hasAttributeNS(XmlPreProcessor.deployNamespaceUri, TAGS_ATTRIBUTE); + } + private Set<InstanceName> getInstances(Element element) { String instance = element.getAttributeNS(XmlPreProcessor.deployNamespaceUri, INSTANCE_ATTRIBUTE); - if (instance == null || instance.isEmpty()) return Collections.emptySet(); + if (instance == null || instance.isEmpty()) return Set.of(); return Arrays.stream(instance.split(" ")).map(InstanceName::from).collect(Collectors.toSet()); } private Set<Environment> getEnvironments(Element element) { String env = element.getAttributeNS(XmlPreProcessor.deployNamespaceUri, ENVIRONMENT_ATTRIBUTE); - if (env == null || env.isEmpty()) return Collections.emptySet(); + if (env == null || env.isEmpty()) return Set.of(); return Arrays.stream(env.split(" ")).map(Environment::from).collect(Collectors.toSet()); } private Set<RegionName> getRegions(Element element) { String reg = element.getAttributeNS(XmlPreProcessor.deployNamespaceUri, REGION_ATTRIBUTE); - if (reg == null || reg.isEmpty()) return Collections.emptySet(); + if (reg == null || reg.isEmpty()) return Set.of(); return Arrays.stream(reg.split(" ")).map(RegionName::from).collect(Collectors.toSet()); } + private Tags getTags(Element element) { + String env = element.getAttributeNS(XmlPreProcessor.deployNamespaceUri, TAGS_ATTRIBUTE); + if (env == null || env.isEmpty()) return Tags.empty(); + return Tags.fromString(env); + } + private Map<String, List<Element>> elementsByTagNameAndId(List<Element> children) { Map<String, List<Element>> elementsByTagName = new LinkedHashMap<>(); // Index by tag name @@ -336,21 +362,27 @@ class OverrideProcessor implements PreProcessor { final Set<InstanceName> instances; final Set<Environment> environments; final Set<RegionName> regions; + final Tags tags; - private Context(Set<InstanceName> instances, Set<Environment> environments, Set<RegionName> regions) { + private Context(Set<InstanceName> instances, + Set<Environment> environments, + Set<RegionName> regions, + Tags tags) { this.instances = Set.copyOf(instances); this.environments = Set.copyOf(environments); this.regions = Set.copyOf(regions); + this.tags = tags; } static Context empty() { - return new Context(Set.of(), Set.of(), Set.of()); + return new Context(Set.of(), Set.of(), Set.of(), Tags.empty()); } public static Context create(Set<InstanceName> instances, Set<Environment> environments, - Set<RegionName> regions) { - return new Context(instances, environments, regions); + Set<RegionName> regions, + Tags tags) { + return new Context(instances, environments, regions, tags); } } |