Skip to content

Commit 41665e5

Browse files
fix bugs -> still required to resolve get inputs
1 parent 09a43c6 commit 41665e5

4 files changed

Lines changed: 82 additions & 32 deletions

File tree

plugins/iac/nimble/src/main/java/org/apache/cloudstack/tosca/model/ToscaNodeTemplate.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ public class ToscaNodeTemplate {
2727
private final String name;
2828
private final ToscaNodeType type;
2929
private final Map<String, ToscaProperty> properties;
30-
private Set<ToscaGetterFunctionCallContext> getPropertyFunctionCalls = new HashSet<>();
31-
private Set<ToscaGetterFunctionCallContext> getAttributeFunctionCalls = new HashSet<>();
30+
private final Set<ToscaProperty> unresolvedPropertiesByGetProperty = new HashSet<>();
31+
private final Set<ToscaProperty> unresolvedPropertiesByGetAttribute = new HashSet<>();
3232
private final Map<String, Object> attributes = new HashMap<>();
3333

3434
public ToscaNodeTemplate(String name, ToscaNodeType type, Map<String, ToscaProperty> properties) {
@@ -57,20 +57,20 @@ public Map<String, ToscaProperty> getProperties() {
5757
return properties;
5858
}
5959

60-
public void setGetPropertyFunctionCalls(Set<ToscaGetterFunctionCallContext> getPropertyFunctionCalls) {
61-
this.getPropertyFunctionCalls = getPropertyFunctionCalls;
60+
public void addUnresolvedPropertyByGetProperty(ToscaProperty unresolvedPropertyByGetProperty) {
61+
this.unresolvedPropertiesByGetProperty.add(unresolvedPropertyByGetProperty);
6262
}
6363

64-
public Set<ToscaGetterFunctionCallContext> getGetPropertyFunctionCalls() {
65-
return getPropertyFunctionCalls;
64+
public Set<ToscaProperty> getUnresolvedPropertiesByGetProperty() {
65+
return unresolvedPropertiesByGetProperty;
6666
}
6767

68-
public void setGetAttributeFunctionCalls(Set<ToscaGetterFunctionCallContext> getAttributeFunctionCalls) {
69-
this.getAttributeFunctionCalls = getAttributeFunctionCalls;
68+
public void addUnresolvedPropertyByGetAttribute(ToscaProperty unresolvedPropertyByGetAttribute) {
69+
this.unresolvedPropertiesByGetAttribute.add(unresolvedPropertyByGetAttribute);
7070
}
7171

72-
public Set<ToscaGetterFunctionCallContext> getGetAttributeFunctionCalls() {
73-
return getAttributeFunctionCalls;
72+
public Set<ToscaProperty> getUnresolvedPropertiesByGetAttribute() {
73+
return unresolvedPropertiesByGetAttribute;
7474
}
7575

7676
public Map<String, String> getApiParams() {

plugins/iac/nimble/src/main/java/org/apache/cloudstack/tosca/orchestrator/ToscaOrchestrator.java

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757

5858
import javax.inject.Inject;
5959
import javax.inject.Named;
60+
import java.util.ArrayList;
6061
import java.util.Collections;
6162
import java.util.HashMap;
6263
import java.util.HashSet;
@@ -367,32 +368,56 @@ protected void populateNodeTemplateAttributes(ToscaNodeTemplate nodeTemplate, Ma
367368
* or when the return value does not pass the validation function.
368369
*/
369370
protected void executeGetAttributeAndGetPropertyFunctionCalls(ToscaNodeTemplate nodeTemplate, ToscaServiceTemplate serviceTemplate) {
370-
Set<ToscaGetterFunctionCallContext> functionCalls = new HashSet<>(nodeTemplate.getGetPropertyFunctionCalls());
371-
functionCalls.addAll(nodeTemplate.getGetAttributeFunctionCalls());
372-
if (CollectionUtils.isEmpty(functionCalls)) {
373-
logger.debug("Node template [{}] has no function calls to the [{}] and [{}] TOSCA function.", nodeTemplate.getName(), ToscaConstants.GET_ATTRIBUTE_FUNCTION, ToscaConstants.GET_PROPERTY_FUNCTION);
371+
Set<ToscaProperty> unresolvedProperties = new HashSet<>(nodeTemplate.getUnresolvedPropertiesByGetProperty());
372+
unresolvedProperties.addAll(nodeTemplate.getUnresolvedPropertiesByGetAttribute());
373+
if (CollectionUtils.isEmpty(unresolvedProperties)) {
374+
logger.debug("Node template [{}] has no unresolved properties by the [{}] and [{}] TOSCA functions.", nodeTemplate.getName(), ToscaConstants.GET_ATTRIBUTE_FUNCTION, ToscaConstants.GET_PROPERTY_FUNCTION);
374375
return;
375376
}
376377

377378
logger.info("Handling the [{}] and [{}] TOSCA function calls performed by the [{}] node template.", ToscaConstants.GET_ATTRIBUTE_FUNCTION, ToscaConstants.GET_PROPERTY_FUNCTION, nodeTemplate.getName());
378-
for (ToscaGetterFunctionCallContext functionCall : functionCalls) {
379-
Object valueToBeResolved = resolveGetAttributeAndGetPropertyFunctionCall(functionCall.getFunctionCall(), serviceTemplate, nodeTemplate);
380-
ToscaProperty callerProperty = functionCall.getProperty();
379+
for (ToscaProperty property : unresolvedProperties) {
380+
Object evaluatedValue = getPropertyEvaluatedValue(property.getRawValue(), serviceTemplate, nodeTemplate);
381381

382-
if (callerProperty.getDefinition().getValidation() != null && callerProperty.getDefinition().getType().getKind() == ToscaTypeDefinition.Kind.PRIMITIVE) {
383-
logger.debug("The property [{}] has a validation clause. Executing it.", callerProperty.getDefinition().getName());
384-
boolean validationResult = callerProperty.getDefinition().getValidation().evaluate(valueToBeResolved);
382+
if (property.getDefinition().getValidation() != null && property.getDefinition().getType().getKind() == ToscaTypeDefinition.Kind.PRIMITIVE) {
383+
logger.debug("The property [{}] has a validation clause. Executing it.", property.getDefinition().getName());
384+
boolean validationResult = property.getDefinition().getValidation().evaluate(evaluatedValue);
385385
if (!validationResult) {
386-
logger.error("The value of the property [{}] of node template [{}] is not valid. Aborting IaC template deployment.", callerProperty.getDefinition().getName(), nodeTemplate.getName());
387-
throw new InvalidParameterValueException(String.format("The value of the property [%s] of node template [%s] is not valid. Please, check the value and try again.", callerProperty.getDefinition().getName(), nodeTemplate.getName()));
386+
logger.error("The value of the property [{}] of node template [{}] is not valid. Aborting IaC template deployment.", property.getDefinition().getName(), nodeTemplate.getName());
387+
throw new InvalidParameterValueException(String.format("The value of the property [%s] of node template [%s] is not valid. Please, check the value and try again.", property.getDefinition().getName(), nodeTemplate.getName()));
388388
}
389389
}
390390

391-
logger.debug("The unresolved property [{}] of node template [{}] will be resolved to value [{}].", callerProperty.getDefinition().getName(), nodeTemplate.getName(), valueToBeResolved);
392-
callerProperty.setEvaluatedValue(valueToBeResolved);
391+
logger.debug("Property [{}] of node template [{}] resolved to [{}].", property.getDefinition().getName(), nodeTemplate.getName(), evaluatedValue);
392+
property.setEvaluatedValue(evaluatedValue);
393393
}
394394
}
395395

396+
private Object getPropertyEvaluatedValue(Object rawValue, ToscaServiceTemplate serviceTemplate, ToscaNodeTemplate nodeTemplate) {
397+
if (rawValue instanceof List) {
398+
List<Object> evaluatedValue = new ArrayList<>();
399+
for (Object item : (List<?>) rawValue) {
400+
evaluatedValue.add(getPropertyEvaluatedValue(item, serviceTemplate, nodeTemplate));
401+
}
402+
return evaluatedValue;
403+
}
404+
405+
if (rawValue instanceof Map) {
406+
Map<String, Object> rawValueAsMap = ToscaYamlHelper.asMap(rawValue);
407+
boolean isToscaFunction = rawValueAsMap.size() == 1 && ToscaConstants.GETTER_FUNCTION_KEYS.contains(rawValueAsMap.keySet().iterator().next());
408+
if (isToscaFunction) {
409+
return resolveGetAttributeAndGetPropertyFunctionCall(rawValueAsMap, serviceTemplate, nodeTemplate);
410+
}
411+
Map<String, Object> evaluatedValue = new LinkedHashMap<>();
412+
for (Map.Entry<String, Object> entry : rawValueAsMap.entrySet()) {
413+
evaluatedValue.put(entry.getKey(), getPropertyEvaluatedValue(entry.getValue(), serviceTemplate, nodeTemplate));
414+
}
415+
return evaluatedValue;
416+
}
417+
418+
return rawValue;
419+
}
420+
396421
private Object resolveGetAttributeAndGetPropertyFunctionCall(Map<String, Object> functionCall, ToscaServiceTemplate serviceTemplate, ToscaNodeTemplate nodeTemplate) {
397422
String toscaFunction = functionCall.keySet().iterator().next();
398423
List<?> args = ToscaYamlHelper.asList(functionCall.get(toscaFunction));

plugins/iac/nimble/src/main/java/org/apache/cloudstack/tosca/parser/ToscaServiceTemplateParser.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -305,18 +305,20 @@ private void getAllToscaFunctionCallsRecursive(ToscaProperty property, Object pr
305305
String function = valueAsMap.keySet().iterator().next();
306306
if (function.startsWith(ToscaConstants.FUNCTION_PREFIX) && !ToscaConstants.GETTER_FUNCTION_KEYS.contains(function)) {
307307
context.addError(String.format("Function call [%s] is not supported in property definitions.", function), function);
308+
return;
308309
} else if (ToscaConstants.GETTER_FUNCTION_KEYS.contains(function)) {
309310
if (validateToscaGetterFunctionCall(valueAsMap, type, context)) {
310311
functionCalls.first().computeIfAbsent(function, k -> new HashSet<>()).add(new ToscaGetterFunctionCallContext(property, valueAsMap, type));
311312
} else {
312313
functionCalls.second(false);
313-
return;
314314
}
315+
return;
315316
}
316317
}
317318

318319
if (type.getKind() != ToscaTypeDefinition.Kind.DATA_TYPE) {
319-
valueAsMap.values().forEach(value -> getAllToscaFunctionCallsRecursive(property, value, type, functionCalls, context));
320+
ToscaTypeDefinition entrySchema = ObjectUtils.defaultIfNull(type.getEntrySchema(), type);
321+
valueAsMap.values().forEach(value -> getAllToscaFunctionCallsRecursive(property, value, entrySchema, functionCalls, context));
320322
return;
321323
}
322324

@@ -399,12 +401,15 @@ private void verifyToscaGetterFunctionCalls(Map<String, ToscaNodeTemplate> nodeT
399401
Set<ToscaGetterFunctionCallContext> functionCallContexts = entry.getValue();
400402

401403
if (!CollectionUtils.isEmpty(functionCallContexts)) {
402-
verifyToscaGetterFunctionCall(nodeTemplate.getName(), functionCallContexts, nodeTemplates, functionName, context);
403-
if (isGetPropertyFunctionCall) {
404-
nodeTemplate.setGetPropertyFunctionCalls(functionCallContexts);
405-
} else {
406-
nodeTemplate.setGetAttributeFunctionCalls(functionCallContexts);
407-
}
404+
functionCallContexts.forEach((functionCall) -> {
405+
ToscaProperty unresolvedProperty = functionCall.getProperty();
406+
verifyToscaGetterFunctionCall(nodeTemplate.getName(), functionCallContexts, nodeTemplates, functionName, context);
407+
if (isGetPropertyFunctionCall) {
408+
nodeTemplate.addUnresolvedPropertyByGetProperty(unresolvedProperty);
409+
} else {
410+
nodeTemplate.addUnresolvedPropertyByGetAttribute(unresolvedProperty);
411+
}
412+
});
408413
}
409414
}
410415
}

plugins/iac/nimble/src/test/java/org/apache/cloudstack/tosca/orchestrator/ToscaOrchestratorTest.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.apache.cloudstack.tosca.parser.ToscaNodeTypeParser;
2828
import org.apache.cloudstack.tosca.parser.ToscaParser;
2929
import org.apache.cloudstack.tosca.parser.ToscaServiceTemplateParser;
30+
import org.apache.cloudstack.tosca.parser.ToscaYamlHelper;
3031
import org.junit.Assert;
3132
import org.junit.Before;
3233
import org.junit.Test;
@@ -171,6 +172,25 @@ public void executeGetAttributeAndGetPropertyFunctionCallsTestSuccessfullyExecut
171172
Assert.assertEquals(serviceTemplate.getNodeTemplates().get("pair").getAttribute("uuid"), serviceTemplate.getNodeTemplates().get("other-instance").getProperty("ssh-key-pair-name").getEvaluatedValue());
172173
}
173174

175+
@Test
176+
public void executeGetAttributeAndGetPropertyFunctionCallsTestSuccessfullyExecuteFunctionCallsContainedInsideOfCollections() {
177+
String serviceTemplateYaml = "{service_template: {node_templates: {instance: {type: Vm, properties: {type: VR, vcpus: 2, ip-addresses: [{$get_property: [other-instance, type]}, {$get_attribute: [other-instance, uuid]}]}}, other-instance: {type: Vm, properties: {type: {$get_property: [pair, name]}, vcpus: 1, offering-details: {detail1: {$get_attribute: [pair, uuid]}, detail2: {$get_property: [pair, public-key]}}}}, pair: {type: SshPair, properties: {name: SSVM, public-key: Public Key}}}}}";
178+
ToscaServiceTemplate serviceTemplate = toscaParser.parseServiceTemplate(serviceTemplateYaml, ToscaFixtures.getToscaProfileForTests(), null);
179+
serviceTemplate.getNodeTemplates().get("other-instance").addAttribute("uuid", "otherinstanceuuid");
180+
serviceTemplate.getNodeTemplates().get("pair").addAttribute("uuid", "pairuuid");
181+
182+
toscaOrchestratorSpy.executeGetAttributeAndGetPropertyFunctionCalls(serviceTemplate.getNodeTemplates().get("other-instance"), serviceTemplate);
183+
Map<String, Object> offeringDetails = ToscaYamlHelper.asMap(serviceTemplate.getNodeTemplates().get("other-instance").getProperty("offering-details").getEvaluatedValue());
184+
Assert.assertEquals(2, offeringDetails.size());
185+
Assert.assertEquals(serviceTemplate.getNodeTemplates().get("pair").getAttribute("uuid"), offeringDetails.get("detail1"));
186+
Assert.assertEquals(serviceTemplate.getNodeTemplates().get("pair").getProperty("public-key").getEvaluatedValue(), offeringDetails.get("detail2"));
187+
188+
toscaOrchestratorSpy.executeGetAttributeAndGetPropertyFunctionCalls(serviceTemplate.getNodeTemplates().get("instance"), serviceTemplate);
189+
List<?> instanceIpAddresses = ToscaYamlHelper.asList(serviceTemplate.getNodeTemplates().get("instance").getProperty("ip-addresses").getEvaluatedValue());
190+
Assert.assertEquals(serviceTemplate.getNodeTemplates().get("other-instance").getProperty("type").getEvaluatedValue(), instanceIpAddresses.get(0));
191+
Assert.assertEquals(serviceTemplate.getNodeTemplates().get("other-instance").getAttribute("uuid"), instanceIpAddresses.get(1));
192+
}
193+
174194
@Test(expected = InvalidParameterValueException.class)
175195
public void executeGetAttributeAndGetPropertyFunctionCallsThrowExceptionWhenTargetAttributeIsNotAvailable() {
176196
String serviceTemplateYaml = "{service_template: {node_templates: {instance: {type: Vm, properties: {type: {$get_attribute: [pair, uuid]}, ssh-key-pair-name: Name, vcpus: 2, ip-addresses: [10.0.0.1, 10.0.0.2]}}, pair: {type: SshPair, properties: {name: SSVM, public-key: Public Key}}}}}";

0 commit comments

Comments
 (0)