3232import org .apache .cloudstack .tosca .model .ToscaPropertyDefinition ;
3333import org .apache .cloudstack .tosca .model .ToscaServiceTemplate ;
3434import org .apache .cloudstack .tosca .model .ToscaTypeDefinition ;
35+ import org .apache .commons .collections .CollectionUtils ;
3536import org .apache .commons .collections .MapUtils ;
3637import org .apache .commons .lang3 .EnumUtils ;
3738import org .apache .logging .log4j .LogManager ;
@@ -56,6 +57,7 @@ protected enum TypeOfToscaField {
5657
5758 private static final String NODE_TYPES_ATTRIBUTES_KEY = "attributes" ;
5859 private static final String PROPERTIES_KEY = "properties" ;
60+ private static final String DEPENDENCY_KEY = "properties" ;
5961 private static final String NODE_TEMPLATES_REQUIREMENTS_KEY = "requirements" ;
6062
6163 private static final String FIELDS_TYPE_KEY = "type" ;
@@ -216,12 +218,12 @@ public ToscaServiceTemplate parseServiceTemplate(String content, Map<String, Tos
216218 ToscaServiceTemplateParsingContext context = new ToscaServiceTemplateParsingContext (toscaProfile );
217219
218220 checkRootServiceTemplateYamlKeys (rawToscaTemplate , context );
219-
220221 Map <String , Object > rawServiceTemplate = ToscaYamlHelper .asMap (rawToscaTemplate .get (SERVICE_TEMPLATE_SERVICE_TEMPLATE_KEY ));
221222 Map <String , ToscaInputDefinition > inputs = parseInputs (ToscaYamlHelper .asMap (rawServiceTemplate .get (SERVICE_TEMPLATE_INPUTS_KEY )), context );
222223 Map <String , ToscaNodeTemplate > nodeTemplates = parseNodeTemplates (ToscaYamlHelper .asMap (rawServiceTemplate .get (SERVICE_TEMPLATE_INPUTS_KEY )), context );
223224
224- checkUnresolvedPropertiesByGetProperty (nodeTemplates , context );
225+ checkUnresolvedProperties (nodeTemplates , "$get_property" , context );
226+ checkUnresolvedProperties (nodeTemplates , "$get_attribute" , context );
225227 Map <String , Set <ToscaNodeTemplate >> serviceTemplateDependencies = buildGraphDependencies (nodeTemplates , context );
226228
227229 if (context .hasErrors ()) {
@@ -231,34 +233,48 @@ public ToscaServiceTemplate parseServiceTemplate(String content, Map<String, Tos
231233 return new ToscaServiceTemplate ();
232234 }
233235
234- private void checkUnresolvedPropertiesByGetProperty (Map <String , ToscaNodeTemplate > nodeTemplates , ToscaServiceTemplateParsingContext context ) {
235- for (Map .Entry <String , Set <ToscaProperty >> entry : context .getUnresolvedByGetProperty ().entrySet ()) {
236- String node = entry .getKey ();
237- Set <ToscaProperty > unresolvedProperties = entry .getValue ();
238-
239- unresolvedProperties .forEach (property -> {
240- Map <String , Object > functionCall = ToscaYamlHelper .asMap (property .getRawValue ());
241- List <?> arguments = (List <?>) functionCall .get ("$get_property" );
242- String targetNode = ToscaYamlHelper .asString (arguments .get (0 ));
243- String targetProperty = ToscaYamlHelper .asString (arguments .get (1 ));
244- if (!nodeTemplates .containsKey (targetNode )) {
245- return ;
246- }
236+ private void checkUnresolvedProperties (Map <String , ToscaNodeTemplate > nodeTemplates , String function , ToscaServiceTemplateParsingContext context ) {
237+ Map <String , Set <ToscaProperty >> unresolvedProperties = "$get_property" .equals (function ) ? context .getUnresolvedByGetProperty () : context .getUnresolvedByGetAttribute ();
238+ for (Map .Entry <String , Set <ToscaProperty >> entry : unresolvedProperties .entrySet ()) {
239+ Set <ToscaProperty > properties = entry .getValue ();
240+ if (!CollectionUtils .isEmpty (properties )) {
241+ checkUnresolvedPropertiesOfANode (entry .getKey (), properties , nodeTemplates , function , context );
242+ }
243+ }
244+ }
247245
248- ToscaNodeTemplate targetNodeTemplate = nodeTemplates .get (targetNode );
249- if (!targetNodeTemplate .hasProperty (targetProperty )) {
250- context .addError (String .format ("The node [%s] references an invalid property of the node [%s]." , node , targetNode ), "node templates section" );
251- return ;
252- }
246+ private void checkUnresolvedPropertiesOfANode (String node , Set <ToscaProperty > unresolvedProperties , Map <String , ToscaNodeTemplate > nodeTemplates , String function , ToscaServiceTemplateParsingContext context ) {
247+ unresolvedProperties .forEach (property -> {
248+ Map <String , Object > functionCall = ToscaYamlHelper .asMap (property .getRawValue ());
249+ List <?> arguments = (List <?>) functionCall .get (function );
250+ String targetNode = ToscaYamlHelper .asString (arguments .get (0 ));
251+ String targetField = ToscaYamlHelper .asString (arguments .get (1 ));
253252
254- ToscaPropertyDefinition targetPropertyDefinition = targetNodeTemplate . getProperty ( targetProperty ). getDefinition ();
255- if (! property . getDefinition (). getType (). isAssignableFrom ( targetPropertyDefinition . getType ())) {
256- context . addError ( String . format ( "Unmatching types between [node: %s, property: %s] and [node: %s, property: %s]." , node , property . getDefinition (). getName (), targetNode , targetProperty ), "node templates section" ) ;
257- }
253+ if (! nodeTemplates . containsKey ( targetNode )) {
254+ context . addError ( String . format ( "The node [%s] has a dependency to a non-existent node [%s]." , node , targetNode ), "node templates section" );
255+ return ;
256+ }
258257
259- context .addNodeDependency (node , targetNode );
260- });
261- }
258+ ToscaNodeTemplate targetNodeTemplate = nodeTemplates .get (targetNode );
259+ if ("$get_property" .equals (function ) && !targetNodeTemplate .hasProperty (targetField )) {
260+ context .addError (String .format ("The node [%s] references an invalid property of the node [%s]." , node , targetNode ), "node templates section" );
261+ return ;
262+ }
263+
264+ if ("$get_attribute" .equals (function ) && !targetNodeTemplate .getType ().hasAttribute (targetField )) {
265+ context .addError (String .format ("The node [%s] references an invalid attribute of the node [%s]." , node , targetNode ), "node templates section" );
266+ return ;
267+ }
268+
269+ ToscaFieldDefinition targetFieldDefinition = "$get_property" .equals (function ) ?
270+ targetNodeTemplate .getProperty (targetField ).getDefinition () : targetNodeTemplate .getType ().getAttributeDefinition (targetField );
271+ if (!property .getDefinition ().getType ().isAssignableFrom (targetFieldDefinition .getType ())) {
272+ context .addError (String .format ("Unmatching types between [node: %s, field: %s] and [node: %s, field: %s]." , node , property .getDefinition ().getName (), targetNode , targetField ), "node templates section" );
273+ return ;
274+ }
275+
276+ context .addNodeDependency (node , targetNode );
277+ });
262278 }
263279
264280 private Map <String , Set <ToscaNodeTemplate >> buildGraphDependencies (Map <String , ToscaNodeTemplate > nodeTemplates , ToscaServiceTemplateParsingContext context ) {
@@ -275,10 +291,10 @@ private Map<String, Set<ToscaNodeTemplate>> buildGraphDependencies(Map<String, T
275291
276292 if (context .getNodeDependencies ().getOrDefault (dependency , new HashSet <>()).contains (node )) {
277293 context .addError (String .format ("The nodes [%s] and [%s] have a circular dependency with each other." , node , dependency ), "node templates section" );
294+ return ;
278295 }
279296
280- serviceTemplateDependencies .computeIfAbsent (node , (n ) -> new HashSet <>())
281- .add (nodeTemplates .get (dependency ));
297+ serviceTemplateDependencies .computeIfAbsent (node , (n ) -> new HashSet <>()).add (nodeTemplates .get (dependency ));
282298 });
283299 }
284300 return serviceTemplateDependencies ;
@@ -309,7 +325,7 @@ private Map<String, ToscaNodeTemplate> parseNodeTemplates(Map<String, Object> no
309325 return nodeTemplateDefinitions ;
310326 }
311327
312- private ToscaNodeTemplate parseNodeTemplate (String name , Map <String , Object > body , ToscaServiceTemplateParsingContext context ) {
328+ private ToscaNodeTemplate parseNodeTemplate (String nodeTemplateName , Map <String , Object > body , ToscaServiceTemplateParsingContext context ) {
313329 String nodeTypeName = ToscaYamlHelper .asString (body .get (FIELDS_TYPE_KEY ));
314330 if (nodeTypeName == null || !context .getProfile ().containsKey (nodeTypeName )) {
315331 context .addError (String .format ("The node type [%s] is not valid." , nodeTypeName ), "node templates section" );
@@ -318,8 +334,29 @@ private ToscaNodeTemplate parseNodeTemplate(String name, Map<String, Object> bod
318334
319335 ToscaNodeType nodeType = context .getProfile ().get (nodeTypeName );
320336 validateKnownToscaKeys (body , Set .of (PROPERTIES_KEY , NODE_TEMPLATES_REQUIREMENTS_KEY ), "node templates section" , context );
321- Map <String , ToscaProperty > properties = parseNodeTemplateProperties (name , ToscaYamlHelper .asMap (body .get (PROPERTIES_KEY )), nodeType , context );
322- return new ToscaNodeTemplate (name , nodeType , properties );
337+ validateRequiredToscaKeys (body , Set .of (PROPERTIES_KEY ), "node templates section" , context );
338+
339+ Map <String , ToscaProperty > properties = parseNodeTemplateProperties (nodeTemplateName , ToscaYamlHelper .asMap (body .get (PROPERTIES_KEY )), nodeType , context );
340+ parseNodeTemplateRequirements (nodeTemplateName , ToscaYamlHelper .asMap (body .get (NODE_TEMPLATES_REQUIREMENTS_KEY )), context );
341+ return new ToscaNodeTemplate (nodeTemplateName , nodeType , properties );
342+ }
343+
344+ private void parseNodeTemplateRequirements (String nodeTemplateName , Map <String , Object > requirements , ToscaServiceTemplateParsingContext context ) {
345+ if (MapUtils .isEmpty (requirements )) {
346+ return ;
347+ }
348+
349+ validateKnownToscaKeys (requirements , Set .of (DEPENDENCY_KEY ), "node templates section" , context );
350+ validateRequiredToscaKeys (requirements , Set .of (DEPENDENCY_KEY ), "node templates section" , context );
351+ List <?> dependencies = ToscaYamlHelper .asList (requirements .get (DEPENDENCY_KEY ));
352+ if (dependencies == null ) {
353+ // no error message will be returned here?
354+ return ;
355+ }
356+
357+ dependencies .forEach (dependency -> {
358+ context .addNodeDependency (nodeTemplateName , ToscaYamlHelper .asString (dependency ));
359+ });
323360 }
324361
325362// what about duplicate node templates?
0 commit comments