Skip to content

Commit

Permalink
Bugfix: Map and Map of Map properties
Browse files Browse the repository at this point in the history
Fixes simple properties where the value is a simple map but during
parsing the value is lost as it was assumed to be part of a function.

Fixes get_property/attribute of named map key. The map was returned
and not the specified key within the map. When a key is specified,
the key will be looked up instead of just returning the map.
  • Loading branch information
kenjones-cisco committed Dec 13, 2016
1 parent 299946c commit 2da4008
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 0 deletions.
31 changes: 31 additions & 0 deletions assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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))
}
}
Expand All @@ -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))
}
}
Expand Down
50 changes: 50 additions & 0 deletions assignment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 27 additions & 0 deletions service_template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 14 additions & 0 deletions tests/custom_types/db_with_list_param.yaml
Original file line number Diff line number Diff line change
@@ -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
50 changes: 50 additions & 0 deletions tests/tosca_nested_property_names_indexes.yaml
Original file line number Diff line number Diff line change
@@ -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
7 changes: 7 additions & 0 deletions utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:]
}

0 comments on commit 2da4008

Please sign in to comment.