From 15c9e3ec811fe1ec1b8830b0ea87f5bf3892938b Mon Sep 17 00:00:00 2001 From: Velizar Kalapov Date: Tue, 4 Mar 2025 17:42:15 +0200 Subject: [PATCH] Add warning message for unknown parameters LMCROSSITXSADEPLOY-3137 --- .../multiapps/mta/model/Resource.java | 2 +- .../resolvers/CustomParameterContainer.java | 30 +++ .../mta/resolvers/ParameterChecker.java | 104 +++++++++++ .../mta/resolvers/ReferenceContainer.java | 23 +++ .../mta/resolvers/ReferencePattern.java | 8 +- .../mta/resolvers/ReferencesFinder.java | 133 ++++++++++++++ .../mta/resolvers/ParameterCheckerTest.java | 135 ++++++++++++++ .../mta/resolvers/ReferencesFinderTest.java | 173 ++++++++++++++++++ .../multiapps/mta/resolvers/mtad-empty.yaml | 3 + .../mta/resolvers/mtad-match-complex.yaml | 40 ++++ .../mta/resolvers/mtad-match-hooks.yaml | 15 ++ .../mtad-match-parameter-resource.yaml | 12 ++ .../mta/resolvers/mtad-match-parameter.yaml | 11 ++ .../mtad-match-provides-requires.yaml | 26 +++ .../mta/resolvers/mtad-reference-complex.yaml | 45 +++++ .../mta/resolvers/mtad-reference-hooks.yaml | 16 ++ .../resolvers/mtad-reference-parameter.yaml | 19 ++ .../resolvers/mtad-reference-property.yaml | 16 ++ .../mtad-reference-provides-requires.yaml | 26 +++ .../multiapps/mta/resolvers/mtad-valid.yaml | 36 ++++ 20 files changed, 869 insertions(+), 4 deletions(-) create mode 100644 multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/resolvers/CustomParameterContainer.java create mode 100644 multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/resolvers/ParameterChecker.java create mode 100644 multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/resolvers/ReferenceContainer.java create mode 100644 multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/resolvers/ReferencesFinder.java create mode 100644 multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/resolvers/ParameterCheckerTest.java create mode 100644 multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/resolvers/ReferencesFinderTest.java create mode 100644 multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-empty.yaml create mode 100644 multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-match-complex.yaml create mode 100644 multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-match-hooks.yaml create mode 100644 multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-match-parameter-resource.yaml create mode 100644 multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-match-parameter.yaml create mode 100644 multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-match-provides-requires.yaml create mode 100644 multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-reference-complex.yaml create mode 100644 multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-reference-hooks.yaml create mode 100644 multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-reference-parameter.yaml create mode 100644 multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-reference-property.yaml create mode 100644 multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-reference-provides-requires.yaml create mode 100644 multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-valid.yaml diff --git a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/model/Resource.java b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/model/Resource.java index 10c945b4..dbe5a47f 100644 --- a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/model/Resource.java +++ b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/model/Resource.java @@ -194,7 +194,7 @@ public Resource setRequiredDependencies(List requiredDepende @Override public void accept(ElementContext context, Visitor visitor) { visitor.visit(context, this); - if (majorSchemaVersion > 3) { + if (majorSchemaVersion > 2) { for (RequiredDependency requiredDependency : requiredDependencies) { visitor.visit(new ElementContext(this, context), requiredDependency); } diff --git a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/resolvers/CustomParameterContainer.java b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/resolvers/CustomParameterContainer.java new file mode 100644 index 00000000..68cc944e --- /dev/null +++ b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/resolvers/CustomParameterContainer.java @@ -0,0 +1,30 @@ +package org.cloudfoundry.multiapps.mta.resolvers; + +import java.util.List; + +public class CustomParameterContainer { + + private final String parameterOwner; + private final List customParameters; + + private final String prefixedName; + + public CustomParameterContainer(String parameterOwner, List parameters, String prefixedName) { + this.parameterOwner = parameterOwner; + this.customParameters = parameters; + this.prefixedName = prefixedName; + } + + public String getParameterOwner() { + return parameterOwner; + } + + public String getPrefixedName() { + return prefixedName; + } + + public List getParameters() { + return customParameters; + } + +} diff --git a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/resolvers/ParameterChecker.java b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/resolvers/ParameterChecker.java new file mode 100644 index 00000000..bbdb425a --- /dev/null +++ b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/resolvers/ParameterChecker.java @@ -0,0 +1,104 @@ +package org.cloudfoundry.multiapps.mta.resolvers; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import org.cloudfoundry.multiapps.mta.helpers.SimplePropertyVisitor; +import org.cloudfoundry.multiapps.mta.helpers.VisitableObject; +import org.cloudfoundry.multiapps.mta.model.DeploymentDescriptor; +import org.cloudfoundry.multiapps.mta.model.ElementContext; +import org.cloudfoundry.multiapps.mta.model.Hook; +import org.cloudfoundry.multiapps.mta.model.Module; +import org.cloudfoundry.multiapps.mta.model.ParametersContainer; +import org.cloudfoundry.multiapps.mta.model.ProvidedDependency; +import org.cloudfoundry.multiapps.mta.model.RequiredDependency; +import org.cloudfoundry.multiapps.mta.model.Resource; +import org.cloudfoundry.multiapps.mta.model.Visitor; + +public abstract class ParameterChecker extends Visitor implements SimplePropertyVisitor { + private List unsupportedParameters; + + private Set predefinedParametersForContainer; + + protected abstract Set getModuleParametersToMatch(); + + protected abstract Set getModuleHookParametersToMatch(); + + protected abstract Set getResourceParametersToMatch(); + + protected abstract Set getGlobalParametersToMatch(); + + protected abstract Set getDependencyParametersToMatch(); + + public List getCustomParameters(DeploymentDescriptor descriptor) { + this.unsupportedParameters = new ArrayList<>(); + descriptor.accept(this); + return this.unsupportedParameters; + } + + @Override + public void visit(ElementContext context, DeploymentDescriptor descriptor) { + matchAndStore(null, getGlobalParametersToMatch(), descriptor, null); + } + + @Override + public void visit(ElementContext context, Module module) { + matchAndStore(module.getName(), getModuleParametersToMatch(), module, context.getPrefixedName()); + } + + @Override + public void visit(ElementContext context, ProvidedDependency providedDependency) { + matchAndStore(providedDependency.getName(), getDependencyParametersToMatch(), providedDependency, context.getPrefixedName()); + } + + @Override + public void visit(ElementContext context, RequiredDependency requiredDependency) { + matchAndStore(requiredDependency.getName(), getDependencyParametersToMatch(), requiredDependency, context.getPrefixedName()); + } + + @Override + public void visit(ElementContext context, Resource resource) { + matchAndStore(resource.getName(), getResourceParametersToMatch(), resource, context.getPrefixedName()); + } + + @Override + public void visit(ElementContext context, Hook hook) { + matchAndStore(hook.getName(), getModuleHookParametersToMatch(), hook, context.getPrefixedName()); + } + + private void matchAndStore(String name, Set parametersToMatch, ParametersContainer parametersContainer, String prefixedName) { + this.predefinedParametersForContainer = parametersToMatch; + List unmatched = findMatchesInParameters(parametersContainer); + if (!unmatched.isEmpty()) { + unsupportedParameters.add(new CustomParameterContainer(name, unmatched, prefixedName)); + } + } + + private List findMatchesInParameters(ParametersContainer parametersContainer) { + List matchedKeys = (List) findMatchesInKeySet(parametersContainer.getParameters() + .keySet()); + return matchedKeys.stream() + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + private Object findMatchesInKeySet(Set keys) { + return new VisitableObject(keys).accept(this); + } + + @Override + public Object visit(String key, String value) { + return parameterKeyMatches(value); + } + + private String parameterKeyMatches(String valueToMatch) { + if (predefinedParametersForContainer != null && !predefinedParametersForContainer.contains(valueToMatch)) { + return valueToMatch; + } + return null; + } + +} diff --git a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/resolvers/ReferenceContainer.java b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/resolvers/ReferenceContainer.java new file mode 100644 index 00000000..e6dbd814 --- /dev/null +++ b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/resolvers/ReferenceContainer.java @@ -0,0 +1,23 @@ +package org.cloudfoundry.multiapps.mta.resolvers; + +import java.util.List; + +public class ReferenceContainer { + + private final String referenceOwner; + private final List references; + + public ReferenceContainer(String referenceOwner, List references) { + this.referenceOwner = referenceOwner; + this.references = references; + } + + public String getReferenceOwner() { + return referenceOwner; + } + + public List getReferences() { + return references; + } + +} diff --git a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/resolvers/ReferencePattern.java b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/resolvers/ReferencePattern.java index 4b1887fe..857183d7 100644 --- a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/resolvers/ReferencePattern.java +++ b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/resolvers/ReferencePattern.java @@ -8,14 +8,17 @@ public enum ReferencePattern implements ValueMatcher { PLACEHOLDER("(? match(String line) { - Matcher matcher = Pattern.compile(this.pattern) - .matcher(line); + Matcher matcher = this.compiledPattern.matcher(line); List references = new ArrayList<>(); while (matcher.find()) { String matchedValue = matcher.group(0); diff --git a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/resolvers/ReferencesFinder.java b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/resolvers/ReferencesFinder.java new file mode 100644 index 00000000..bd6d7d28 --- /dev/null +++ b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/resolvers/ReferencesFinder.java @@ -0,0 +1,133 @@ +package org.cloudfoundry.multiapps.mta.resolvers; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +import org.cloudfoundry.multiapps.mta.helpers.SimplePropertyVisitor; +import org.cloudfoundry.multiapps.mta.helpers.VisitableObject; +import org.cloudfoundry.multiapps.mta.model.DeploymentDescriptor; +import org.cloudfoundry.multiapps.mta.model.ElementContext; +import org.cloudfoundry.multiapps.mta.model.Hook; +import org.cloudfoundry.multiapps.mta.model.Module; +import org.cloudfoundry.multiapps.mta.model.ProvidedDependency; +import org.cloudfoundry.multiapps.mta.model.RequiredDependency; +import org.cloudfoundry.multiapps.mta.model.Resource; +import org.cloudfoundry.multiapps.mta.model.Visitor; + +public class ReferencesFinder extends Visitor implements SimplePropertyVisitor { + + private List foundReferences; + + public List getAllReferences(DeploymentDescriptor descriptor) { + this.foundReferences = new ArrayList<>(); + descriptor.accept(this); + return this.foundReferences; + } + + @Override + public void visit(ElementContext context, DeploymentDescriptor descriptor) { + collectReferences(null, descriptor.getParameters()); + } + + @Override + public void visit(ElementContext context, Module module) { + collectReferences(module.getName(), module.getParameters(), module.getProperties()); + } + + @Override + public void visit(ElementContext context, ProvidedDependency providedDependency) { + collectReferences(context.getPrefixedName(), providedDependency.getParameters(), providedDependency.getProperties()); + } + + @Override + public void visit(ElementContext context, RequiredDependency requiredDependency) { + collectReferences(context.getPrefixedName(), requiredDependency.getParameters(), requiredDependency.getProperties()); + } + + @Override + public void visit(ElementContext context, Resource resource) { + collectReferences(resource.getName(), resource.getParameters(), resource.getProperties()); + } + + @Override + public void visit(ElementContext context, Hook hook) { + collectReferences(context.getPrefixedName(), hook.getParameters()); + } + + private void collectReferences(String name, Map... maps) { + List merged = Arrays.stream(maps) + .flatMap(map -> findReferencesInParameters(map).stream()) + .collect(Collectors.toList()); + + if (!merged.isEmpty()) { + foundReferences.add(new ReferenceContainer(name, merged)); + } + } + + private List findReferencesInParameters(Map map) { + if (map == null) { + return Collections.emptyList(); + } + + List matchedKeys = (List) findReferencesInList(new ArrayList(map.values())); + return matchedKeys.stream() + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + private Object findReferencesInList(List keys) { + Object result = new VisitableObject(keys).accept(this); + return extractReferences(result); + } + + private List extractReferences(Object input) { + if (input == null) { + return Collections.emptyList(); + } + + if (input instanceof Reference) { + return List.of((Reference) input); + } + + if (input instanceof List) { + return ((List) input).stream() + .flatMap(item -> extractReferences(item).stream()) + .collect(Collectors.toList()); + } + + if (input instanceof Map) { + return ((Map) input).values() + .stream() + .flatMap(value -> extractReferences(value).stream()) + .collect(Collectors.toList()); + } + + return Collections.emptyList(); + } + + @Override + public Object visit(String key, String value) { + return referencePatternMatches(value); + } + + private List referencePatternMatches(String valueToMatch) { + //Not using ReferencePattern.values() in order to have the more specific FULLY_QUALIFIED pattern before SHORT + for (ReferencePattern pattern : List.of( + ReferencePattern.PLACEHOLDER, + ReferencePattern.FULLY_QUALIFIED, + ReferencePattern.SHORT)) { + + List references = pattern.match(valueToMatch); + if (!references.isEmpty()) { + return references; + } + } + return null; + } + +} diff --git a/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/resolvers/ParameterCheckerTest.java b/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/resolvers/ParameterCheckerTest.java new file mode 100644 index 00000000..8327709c --- /dev/null +++ b/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/resolvers/ParameterCheckerTest.java @@ -0,0 +1,135 @@ +package org.cloudfoundry.multiapps.mta.resolvers; + +import java.io.InputStream; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.cloudfoundry.multiapps.mta.handlers.DescriptorParserFacade; +import org.cloudfoundry.multiapps.mta.model.DeploymentDescriptor; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ParameterCheckerTest { + static Stream testGetMatches() { + return Stream.of( + Arguments.of("mtad-match-parameter.yaml", + List.of(new CustomParameterContainer("foo", List.of("other-parameter"), "foo"))), + Arguments.of("mtad-match-parameter-resource.yaml", + List.of(new CustomParameterContainer("baz", List.of("other-parameter"), "baz"))), + Arguments.of("mtad-match-provides-requires.yaml", + List.of(new CustomParameterContainer("bar", List.of("other-required-dependency-parameter"), "ztana#bar"), + new CustomParameterContainer("backend-live", List.of("other-provided-dependency-parameter"), + "ztana#backend-live"), + new CustomParameterContainer("bar", List.of("other-resource-dependency-parameter"), "baz#baz"))), + Arguments.of("mtad-match-hooks.yaml", + List.of(new CustomParameterContainer("test-hook1", List.of("other-hook-parameter"), "foo#test-hook1"))), + Arguments.of("mtad-match-complex.yaml", + List.of(new CustomParameterContainer("ztana", List.of("other-parameter"), "ztana"), + new CustomParameterContainer("backend-live", List.of("other-provided-dependency-parameter"), + "ztana#backend-live"), + new CustomParameterContainer("bar", List.of("other-required-dependency-parameter"), "ztana#bar"), + new CustomParameterContainer("test-hook1", List.of("other-hook-parameter"), "ztana#test-hook1"), + new CustomParameterContainer("baz", List.of("other-resource-parameter"), "baz"), + new CustomParameterContainer("bar", List.of("other-resource-dependency-parameter"), "baz#baz")))); + } + + @ParameterizedTest + @MethodSource + void testGetMatches(String descriptorLocation, List expectedResult) { + ParameterChecker parameterChecker = new ParameterCheckerTestImpl(); + DeploymentDescriptor descriptor = parseDeploymentDescriptor(descriptorLocation); + List actualResult = parameterChecker.getCustomParameters(descriptor); + assertContainsAllCustomParameters(actualResult, expectedResult); + } + + private DeploymentDescriptor parseDeploymentDescriptor(String deploymentDescriptorLocation) { + InputStream deploymentDescriptorYaml = getClass().getResourceAsStream(deploymentDescriptorLocation); + return new DescriptorParserFacade().parseDeploymentDescriptor(deploymentDescriptorYaml); + } + + public static void assertContainsAllCustomParameters(List actual, List expected) { + for (CustomParameterContainer exp : expected) { + boolean matchFound = containsParameter(actual, exp.getParameterOwner(), exp.getParameters(), exp.getPrefixedName()); + if (!matchFound) { + throw new AssertionError( + "Expected parameter not found: " + formatCustomParameterContainer(exp) + "\nActual: " + formatCustomParameterContainers( + actual)); + } + } + + if (actual.size() != expected.size()) { + throw new AssertionError("Expected size: " + expected.size() + ", Actual size: " + actual.size() + + "\nExpected:\n" + formatCustomParameterContainers(expected) + "\n\n" + + "\nActual:\n" + formatCustomParameterContainers(actual)); + } + } + + private static String formatCustomParameterContainer(CustomParameterContainer container) { + return String.format( + "parameterOwner: \"%s\", prefixedName: \"%s\", parameters: %s", + container.getParameterOwner(), + container.getPrefixedName(), + container.getParameters() + ); + } + + private static String formatCustomParameterContainers(List containers) { + return containers.stream() + .map(ParameterCheckerTest::formatCustomParameterContainer) + .collect(Collectors.joining("\n")); + } + + public static boolean containsParameter(List parameters, String owner, List customParameters, + String prefixedName) { + return parameters.stream() + .anyMatch(param -> + Objects.equals(param.getParameterOwner(), owner) && + Objects.equals(param.getParameters(), customParameters) && + Objects.equals(param.getPrefixedName(), prefixedName) + ); + } + + @ParameterizedTest + @ValueSource(strings = { "mtad-valid.yaml", "mtad-empty.yaml" }) + void testGetMatches(String descriptorLocation) { + ParameterChecker checker = new ParameterCheckerTestImpl(); + DeploymentDescriptor descriptor = parseDeploymentDescriptor(descriptorLocation); + List result = checker.getCustomParameters(descriptor); + assertTrue(result.isEmpty(), "Expected no custom parameters but found: " + result); + } + + private class ParameterCheckerTestImpl extends ParameterChecker { + + @Override + protected Set getModuleParametersToMatch() { + return Set.of("module-parameter"); + } + + @Override + protected Set getModuleHookParametersToMatch() { + return Set.of("hook-parameter"); + } + + @Override + protected Set getResourceParametersToMatch() { + return Set.of("resource-parameter"); + } + + @Override + protected Set getGlobalParametersToMatch() { + return Set.of("global-parameter"); + } + + @Override + protected Set getDependencyParametersToMatch() { + return Set.of("dependency-parameter"); + } + } +} diff --git a/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/resolvers/ReferencesFinderTest.java b/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/resolvers/ReferencesFinderTest.java new file mode 100644 index 00000000..e65da0f2 --- /dev/null +++ b/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/resolvers/ReferencesFinderTest.java @@ -0,0 +1,173 @@ +package org.cloudfoundry.multiapps.mta.resolvers; + +import java.io.InputStream; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.cloudfoundry.multiapps.mta.handlers.DescriptorParserFacade; +import org.cloudfoundry.multiapps.mta.model.DeploymentDescriptor; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static java.util.List.of; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +public class ReferencesFinderTest { + + static Stream testGetReferences() { + return Stream.of( + arguments("mtad-reference-parameter.yaml", + of( + container(null, of(ref("${a}", "a", null))), + container("foo", of(ref("${b}", "b", null))), + container("baz", of( + ref("~{c/d}", "d", "c"), + ref("~{e/f}", "f", "e"), + ref("~{bar/non-existing}", "non-existing", "bar") + )) + ) + ), + arguments("mtad-reference-property.yaml", + of( + container("foo", of(ref("${b}", "b", null))), + container("baz", of( + ref("~{bar/non-existing}", "non-existing", "bar"), + ref("~{e/f}", "f", "e"), + ref("~{c/d}", "d", "c") + )) + ) + ), + arguments("mtad-reference-provides-requires.yaml", + of( + container("ztanaa#backend-live", of(ref("${ref-url}", "ref-url", null))), + container("ztanaa#bar", of(ref("${non-existing}", "non-existing", null))), + container("ztanaa#backend-idle", of(ref("${ref-url-2}", "ref-url-2", null))), + container("baz#baz", of(ref("${non-existing-resource}", "non-existing-resource", null))) + ) + ), + arguments("mtad-reference-hooks.yaml", + of( + container("foo#test-hook1", of(ref("${default-time}", "default-time", null))) + ) + ), + arguments("mtad-reference-complex.yaml", + of( + container(null, of(ref("${a}", "a", null))), + container("foo", of(ref("${b}", "b", null), ref("${c}", "c", null))), + container("foo#backend-live", of(ref("${ref-url}", "ref-url", null))), + container("foo#backend-idle", of(ref("${ref-url-2}", "ref-url-2", null))), + container("foo#bar", of(ref("${d}", "d", null))), + container("foo#test-hook1", of(ref("${task-ref}", "task-ref", null))), + container("baz", of(ref("~{e}", "e", null), ref("~{f}", "f", null))), + container("baz#baz", of(ref("${non-existing-resource}", "non-existing-resource", null))) + ) + ), + arguments("mtad-empty.yaml", Collections.emptyList()) + ); + } + + @ParameterizedTest + @MethodSource + void testGetReferences(String descriptorLocation, List expectedResult) { + ReferencesFinder finder = new ReferencesFinder(); + DeploymentDescriptor descriptor = parseDeploymentDescriptor(descriptorLocation); + List actualReferences = finder.getAllReferences(descriptor); + assertContainsAllReferences(actualReferences, expectedResult); + } + + private DeploymentDescriptor parseDeploymentDescriptor(String deploymentDescriptorLocation) { + InputStream deploymentDescriptorYaml = getClass().getResourceAsStream(deploymentDescriptorLocation); + return new DescriptorParserFacade().parseDeploymentDescriptor(deploymentDescriptorYaml); + } + + private static ReferenceContainer container(String containerName, List references) { + return new ReferenceContainer(containerName, references); + } + + private static Reference ref(String pattern, String key, String dep) { + return new Reference(pattern, key, dep); + } + + public static void assertContainsAllReferences(List actual, List expected) { + if (actual.size() != expected.size()) { + throw new AssertionError("Expected size " + expected.size() + " but got " + actual.size()); + } + + for (ReferenceContainer expectedContainer : expected) { + ReferenceContainer actualContainer = findMatchingActualContainer(actual, expectedContainer); + assertAllReferencesMatch(expectedContainer, actualContainer); + } + } + + private static ReferenceContainer findMatchingActualContainer(List actual, ReferenceContainer expected) { + return actual.stream() + .filter(container -> Objects.equals(expected.getReferenceOwner(), container.getReferenceOwner())) + .findFirst() + .orElseThrow(() -> new AssertionError( + "Expected owner: " + formatReferenceContainer(expected) + + ", but none matched.\n" + + "Actual reference mappings:\n" + getOwnerToReferencesMap(actual) + )); + } + + private static String getOwnerToReferencesMap(List containers) { + return containers.stream() + .map(ReferencesFinderTest::formatReferenceContainer) + .collect(Collectors.joining("\n")); + } + + private static String formatReferenceContainer(ReferenceContainer container) { + return container.getReferenceOwner() + " -> " + + container.getReferences() + .stream() + .map(ReferencesFinderTest::formatReference) + .collect(Collectors.joining(", ")); + } + + private static String formatReference(Reference ref) { + return ref.getKey() + + " (pattern: " + ref.getMatchedPattern() + + ", dep: " + (ref.getDependencyName() != null ? ref.getDependencyName() : "null") + ")"; + } + + private static void assertAllReferencesMatch(ReferenceContainer expectedContainer, ReferenceContainer actualContainer) { + String owner = expectedContainer.getReferenceOwner(); + + expectedContainer.getReferences() + .forEach(expectedReference -> + assertReferenceExists(actualContainer, expectedReference, owner) + ); + } + + private static void assertReferenceExists(ReferenceContainer actualContainer, Reference expectedReference, String owner) { + boolean found = actualContainer.getReferences() + .stream() + .anyMatch(actualReference -> containsReference( + actualContainer.getReferences(), + expectedReference.getMatchedPattern(), + expectedReference.getKey(), + expectedReference.getDependencyName() + )); + + if (!found) { + throw new AssertionError( + "Expected reference " + formatReference(expectedReference) + ", not found for owner: " + owner + + "\nActual reference mappings:\n" + + formatReferenceContainer(actualContainer)); + } + } + + public static boolean containsReference(List set, String pattern, String key, String dependencyName) { + return set.stream() + .anyMatch(ref -> + Objects.equals(ref.getMatchedPattern(), pattern) && + Objects.equals(ref.getKey(), key) && + Objects.equals(ref.getDependencyName(), dependencyName) + ); + } + +} diff --git a/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-empty.yaml b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-empty.yaml new file mode 100644 index 00000000..19fef55a --- /dev/null +++ b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-empty.yaml @@ -0,0 +1,3 @@ +_schema-version: 3 +ID: reference-test +version: 1.0.0 diff --git a/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-match-complex.yaml b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-match-complex.yaml new file mode 100644 index 00000000..638b5549 --- /dev/null +++ b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-match-complex.yaml @@ -0,0 +1,40 @@ +_schema-version: 3 +ID: reference-test +version: 1.0.0 + +modules: + - name: ztana + type: java.tomcat + path: backend.war + parameters: + module-parameter: test + other-parameter: test + hooks: + - name: test-hook1 + type: task + phases: + - application.before-stop.live + parameters: + hook-parameter: test + other-hook-parameter: test + provides: + - name: backend-live + parameters: + dependency-parameter: test + other-provided-dependency-parameter: test + requires: + - name: bar + parameters: + dependency-parameter: test + other-required-dependency-parameter: test + +resources: + - name: baz + parameters: + other-resource-parameter: a + resource-parameter: b + requires: + - name: bar + parameters: + dependency-parameter: test + other-resource-dependency-parameter: test \ No newline at end of file diff --git a/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-match-hooks.yaml b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-match-hooks.yaml new file mode 100644 index 00000000..0d454b54 --- /dev/null +++ b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-match-hooks.yaml @@ -0,0 +1,15 @@ +_schema-version: "3.1.0" +ID: reference-test +version: 1.0.0 + +modules: + - name: foo + type: application + hooks: + - name: test-hook1 + type: task + phases: + - application.before-stop.live + parameters: + hook-parameter: test + other-hook-parameter: test \ No newline at end of file diff --git a/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-match-parameter-resource.yaml b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-match-parameter-resource.yaml new file mode 100644 index 00000000..66451d12 --- /dev/null +++ b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-match-parameter-resource.yaml @@ -0,0 +1,12 @@ +_schema-version: 3 +ID: reference-test +version: 1.0.0 + + +resources: + - name: baz + parameters: + other-parameter: a + resource-parameter: b + properties: + some-property: c \ No newline at end of file diff --git a/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-match-parameter.yaml b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-match-parameter.yaml new file mode 100644 index 00000000..3d91c03b --- /dev/null +++ b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-match-parameter.yaml @@ -0,0 +1,11 @@ +_schema-version: 3 +ID: reference-test +version: 1.0.0 + + +modules: + - name: foo + type: application + parameters: + module-parameter: a + other-parameter: b \ No newline at end of file diff --git a/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-match-provides-requires.yaml b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-match-provides-requires.yaml new file mode 100644 index 00000000..dc391874 --- /dev/null +++ b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-match-provides-requires.yaml @@ -0,0 +1,26 @@ +_schema-version: 3 +ID: reference-test +version: 1.0.0 + +modules: + - name: ztana + type: java.tomcat + path: backend.war + provides: + - name: backend-live + parameters: + dependency-parameter: test + other-provided-dependency-parameter: test + requires: + - name: bar + parameters: + dependency-parameter: test + other-required-dependency-parameter: test + +resources: + - name: baz + requires: + - name: bar + parameters: + dependency-parameter: test + other-resource-dependency-parameter: test diff --git a/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-reference-complex.yaml b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-reference-complex.yaml new file mode 100644 index 00000000..0b5c38dc --- /dev/null +++ b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-reference-complex.yaml @@ -0,0 +1,45 @@ +_schema-version: 3 +ID: reference-test +version: 1.0.0 + +parameters: + a: ${a} + +modules: + - name: foo + type: application + parameters: + b: bbb + b-ref: ${b} + properties: + c: ${c} + provides: + - name: backend-live + parameters: + url: ${ref-url} + - name: backend-idle + properties: + url: ${ref-url-2} + requires: + - name: bar + properties: + d: ${d} + hooks: + - name: test-hook1 + type: task + phases: + - application.before-stop.live + parameters: + name: before-stop-task + command: "sleep(${task-ref})" + +resources: + - name: baz + parameters: + e: ~{e} + properties: + f: ~{f} + requires: + - name: bar + properties: + reference: ${non-existing-resource} \ No newline at end of file diff --git a/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-reference-hooks.yaml b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-reference-hooks.yaml new file mode 100644 index 00000000..4b7b5847 --- /dev/null +++ b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-reference-hooks.yaml @@ -0,0 +1,16 @@ +_schema-version: "3.1.0" +ID: reference-test +version: 1.0.0 + +modules: + - name: foo + type: application + hooks: + - name: test-hook1 + type: task + phases: + - application.before-stop.live + parameters: + name: before-stop-task + command: ${default-time} + foo: true \ No newline at end of file diff --git a/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-reference-parameter.yaml b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-reference-parameter.yaml new file mode 100644 index 00000000..836d6ed5 --- /dev/null +++ b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-reference-parameter.yaml @@ -0,0 +1,19 @@ +_schema-version: 3 +ID: reference-test +version: 1.0.0 + +parameters: + a: ${a} + +modules: + - name: foo + type: application + parameters: + b: bbb + b2: ${b} + +resources: + - name: baz + parameters: + parameters1: ~{c/d}.foo-test2-value + baz-test2: ~{e/f}.bar-test1-value.nonexisting.~{bar/non-existing} \ No newline at end of file diff --git a/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-reference-property.yaml b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-reference-property.yaml new file mode 100644 index 00000000..06b8ef38 --- /dev/null +++ b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-reference-property.yaml @@ -0,0 +1,16 @@ +_schema-version: 3 +ID: reference-test +version: 1.0.0 + +modules: + - name: foo + type: application + properties: + b: bbb + b2: ${b} + +resources: + - name: baz + properties: + parameters1: ~{c/d}.foo-test2-value + baz-test2: ~{e/f}.bar-test1-value.nonexisting.~{bar/non-existing} \ No newline at end of file diff --git a/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-reference-provides-requires.yaml b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-reference-provides-requires.yaml new file mode 100644 index 00000000..3d605203 --- /dev/null +++ b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-reference-provides-requires.yaml @@ -0,0 +1,26 @@ +_schema-version: 3 +ID: reference-test +version: 1.0.0 + +modules: + - name: ztanaa + type: java.tomcat + path: backend.war + provides: + - name: backend-live + parameters: + url: ${ref-url} + - name: backend-idle + properties: + url: ${ref-url-2} + requires: + - name: bar + properties: + b: ${non-existing} + +resources: + - name: baz + requires: + - name: bar + properties: + c: ${non-existing-resource} diff --git a/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-valid.yaml b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-valid.yaml new file mode 100644 index 00000000..fc864cdf --- /dev/null +++ b/multiapps-mta/src/test/resources/org/cloudfoundry/multiapps/mta/resolvers/mtad-valid.yaml @@ -0,0 +1,36 @@ +_schema-version: 3 +ID: reference-test +version: 1.0.0 + +parameters: + global-parameter: ${a} + +modules: + - name: foo + type: application + parameters: + module-parameter: bbb + provides: + - name: backend-live + parameters: + dependency-parameter: ${ref-url} + requires: + - name: bar + parameters: + dependency-parameter: ${ref-url} + hooks: + - name: test-hook1 + type: task + phases: + - application.before-stop.live + parameters: + hook-parameter: before-stop-task + +resources: + - name: baz + parameters: + resource-parameter: ~{e} + requires: + - name: bar + parameters: + dependency-parameter: ${ref-url} \ No newline at end of file