diff --git a/assignment.go b/assignment.go index 9e23a13..50754e8 100644 --- a/assignment.go +++ b/assignment.go @@ -24,24 +24,32 @@ func (p *Assignment) UnmarshalYAML(unmarshal func(interface{}) error) error { var m map[string]string if err := unmarshal(&m); err == nil { + processed := false for k, v := range m { if isFunction(k) { + processed = true p.Function = k args := make([]interface{}, 1) args[0] = v p.Args = args } if isOperator(k) { + processed = true p.Expression = ConstraintClause{Operator: k, Values: v} } } + if !processed { + p.Value = m + } return nil } var mm map[string][]string if err := unmarshal(&mm); err == nil { + processed := false for k, v := range mm { if isFunction(k) { + processed = true p.Function = k args := make([]interface{}, len(v)) for i, a := range v { @@ -50,21 +58,30 @@ func (p *Assignment) UnmarshalYAML(unmarshal func(interface{}) error) error { p.Args = args } if isOperator(k) { + processed = true p.Expression = ConstraintClause{Operator: k, Values: v} } } + if !processed { + p.Value = mm + } return nil } // ex. concat function with another function embedded inside var mmm map[string][]interface{} if err := unmarshal(&mmm); err == nil { + processed := false for k, v := range mmm { if isFunction(k) { + processed = true p.Function = k p.Args = v } } + if !processed { + p.Value = mmm + } return nil } @@ -130,6 +147,14 @@ func (p *Assignment) evaluate(std *ServiceTemplateDefinition, ctx, arg string) i if pa := newAssignmentFunc(val); pa != nil { return pa.Evaluate(std, ctx) } + + if len(p.Args) != 0 { + tmp := clone(*p) + pa, _ := tmp.(Assignment) + pa.Value = val + return pa.lookupValueArg(p.Args[0].(string)) + } + return val } return p.Evaluate(std, ctx) @@ -180,13 +205,16 @@ func (p *Assignment) evalProperty(std *ServiceTemplateDefinition, ctx string) in if len(p.Args) >= 3 { if rnt != nil { if prop := rnt.findProperty(p.Args[2].(string), p.Args[1].(string)); prop != nil { + prop.Args = remainder(3, p.Args) return prop.evaluate(std, rnt.Name, get(3, p.Args)) } } if prop := nt.findProperty(p.Args[2].(string), p.Args[1].(string)); prop != nil { + prop.Args = remainder(3, p.Args) return prop.evaluate(std, nt.Name, get(3, p.Args)) } if prop := nt.findProperty(p.Args[1].(string), ""); prop != nil { + prop.Args = remainder(2, p.Args) return prop.evaluate(std, nt.Name, get(2, p.Args)) } } @@ -207,13 +235,16 @@ func (p *Assignment) evalAttribute(std *ServiceTemplateDefinition, ctx string) i if len(p.Args) >= 3 { if rnt != nil { if attr := rnt.findAttribute(p.Args[2].(string), p.Args[1].(string)); attr != nil { + attr.Args = remainder(3, p.Args) return attr.evaluate(std, rnt.Name, get(3, p.Args)) } } if attr := nt.findAttribute(p.Args[2].(string), p.Args[1].(string)); attr != nil { + attr.Args = remainder(3, p.Args) return attr.evaluate(std, nt.Name, get(3, p.Args)) } if attr := nt.findAttribute(p.Args[1].(string), ""); attr != nil { + attr.Args = remainder(2, p.Args) return attr.evaluate(std, nt.Name, get(2, p.Args)) } } diff --git a/assignment_test.go b/assignment_test.go index c6e5c27..ee50008 100644 --- a/assignment_test.go +++ b/assignment_test.go @@ -411,6 +411,56 @@ func TestEvaluateGetAttributeFuncWithIndex(t *testing.T) { } } +func TestEvaluateGetAttributeFuncWithNamedIndex(t *testing.T) { + fname := "./tests/tosca_nested_property_names_indexes.yaml" + var s ServiceTemplateDefinition + o, err := os.Open(fname) + if err != nil { + t.Fatal(err) + } + err = s.Parse(o) + if err != nil { + t.Log("Error in processing", fname) + t.Fatal(err) + } + + nt := s.GetNodeTemplate("wordpress") + if nt == nil { + t.Log(fname, "missing NodeTemplate `wordpress`") + t.Fail() + } + + intf, ok := nt.Interfaces["Standard"] + if !ok { + t.Log(fname, "missing Interface `Standard`") + t.Fail() + } + + op, ok := intf.Operations["configure"] + if !ok { + t.Log(fname, "missing Operation `configure`") + t.Fail() + } + + pa, ok := op.Inputs["wp_endpoint_protocol"] + if !ok { + t.Log(fname, "missing Operation Input `wp_endpoint_protocol`") + t.Fail() + } + + v := pa.Evaluate(&s, "wordpress") + if vstr, ok := v.(string); ok { + if vstr != "tcp" { + t.Log(fname, "property evaluation failed to get value for `wp_endpoint_protocol`", vstr) + t.Fail() + } + } else { + t.Log("property value returned not the correct type", v) + t.Fail() + } + +} + func TestEvaluateGetPropertyFuncWithCapInherit(t *testing.T) { fname := "./tests/get_property_capabilties_inheritance.yaml" var s ServiceTemplateDefinition diff --git a/service_template_test.go b/service_template_test.go index fd9308e..36a3583 100644 --- a/service_template_test.go +++ b/service_template_test.go @@ -234,6 +234,33 @@ func TestParseVerifyPropertyExpression(t *testing.T) { } } +func TestParseVerifyMapProperty(t *testing.T) { + fname := "./tests/tosca_nested_property_names_indexes.yaml" + var s ServiceTemplateDefinition + o, err := os.Open(fname) + if err != nil { + t.Fatal(err) + } + err = s.Parse(o) + if err != nil { + t.Log("Error in processing", fname) + t.Fatal(err) + } + + prop, ok := s.TopologyTemplate.NodeTemplates["mysql_database"].Properties["map_prop"] + if !ok { + t.Log(fname, "missing NodeTemplate `mysql_database` Property `map_prop`") + t.Fail() + } + + data := map[string]string{"test": "dev", "other": "task"} + + if ok := reflect.DeepEqual(prop.Value, data); !ok { + t.Log(fname, "missing or invalid value found for Property `map_prop`", prop.Value, "wanted:", data) + t.Fail() + } +} + func TestParseVerifyNTInterfaces(t *testing.T) { fname := "./tests/tosca_interface_inheritance.yaml" var s ServiceTemplateDefinition diff --git a/tests/custom_types/db_with_list_param.yaml b/tests/custom_types/db_with_list_param.yaml new file mode 100644 index 0000000..cc1a044 --- /dev/null +++ b/tests/custom_types/db_with_list_param.yaml @@ -0,0 +1,14 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +node_types: + tosca.nodes.DatabaseWithListParam: + derived_from: tosca.nodes.Database + properties: + list_prop: + type: list + entry_schema: + type: integer + map_prop: + type: map + entry_schema: + type: string diff --git a/tests/tosca_nested_property_names_indexes.yaml b/tests/tosca_nested_property_names_indexes.yaml new file mode 100644 index 0000000..fa436ca --- /dev/null +++ b/tests/tosca_nested_property_names_indexes.yaml @@ -0,0 +1,50 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: TOSCA simple profile with nested property names or indexes. + +imports: + - tests/custom_types/wordpress.yaml + - tests/custom_types/db_with_list_param.yaml + +topology_template: + + node_templates: + + wordpress: + type: tosca.nodes.WebApplication.WordPress + requirements: + - host: server + - database_endpoint: mysql_database + interfaces: + Standard: + configure: + implementation: wordpress/wordpress_configure.sh + inputs: + wp_endpoint_protocol: { get_property: [ SELF, database_endpoint, ports, user_port, protocol ] } + wp_list_prop: { get_property: [ mysql_database, list_prop, 2 ] } + + mysql_database: + type: tosca.nodes.DatabaseWithListParam + properties: + list_prop: [1,2,3] + map_prop: + test: dev + other: task + capabilities: + database_endpoint: + properties: + ports: + user_port: + protocol: tcp + target: 50000 + source: 9000 + requirements: + - host: mysql_dbms + + mysql_dbms: + type: tosca.nodes.DBMS + requirements: + - host: server + + server: + type: tosca.nodes.Compute diff --git a/utils.go b/utils.go index ccbea8b..95ac020 100644 --- a/utils.go +++ b/utils.go @@ -99,3 +99,10 @@ func get(k int, list []interface{}) string { } return "" } + +func remainder(k int, list []interface{}) []interface{} { + if len(list) <= k { + return make([]interface{}, 0) + } + return list[k+1:] +}