From 93631adf94b875108731d68dac9b420fae696fb4 Mon Sep 17 00:00:00 2001 From: kenjones Date: Tue, 25 Oct 2016 14:08:53 -0400 Subject: [PATCH] Feature: Apply Inherited data Add the application of inherited data to each component to provide a simple view of each entity that is consumable without the need to continuously traverse up and down the hierarchy. --- README.md | 3 + TODO.md | 46 +++++++ attributes.go | 4 - capabilities.go | 20 ++- flattener.go | 168 +++++++++++++++++++++++-- interfaces.go | 85 +++++++++---- node_template.go | 163 ++++++++++-------------- node_type.go | 13 +- policies.go | 19 +++ relationships.go | 43 +++++-- requirements.go | 18 +++ service_template.go | 28 +---- topology.go | 23 ++++ tosca_functions.go | 6 - tosca_namespace_alias.go | 13 +- tosca_reusable_modeling_definitions.go | 6 +- 16 files changed, 466 insertions(+), 192 deletions(-) create mode 100644 TODO.md diff --git a/README.md b/README.md index 5485bba..47c60aa 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,9 @@ This library is an implementation of the TOSCA definition as described in the do [5]: http://gocover.io/_badge/github.com/CiscoCloud/toscalib [6]: http://gocover.io/github.com/CiscoCloud/toscalib +## Plans + +[ToDo Tasks](TODO.md) ## Normative Types The normative types definitions are included de facto. The files are embeded using go-bindata. diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..9a8ab35 --- /dev/null +++ b/TODO.md @@ -0,0 +1,46 @@ +## To Do +### ``assignment.go`` +(line 225) (kenjones): Add support for the evaluation of ConstraintClause + + +### ``flattener.go`` +(line 14) (kenjones): Switch to reflect if possible in the future (if simple) + + +### ``parser.go`` +(line 84) (kenjones): Add hooks as method parameter + +(line 139) (kenjones): Does dropping the Imports list really have any impact? + + +### ``service_template.go`` +(line 21) (kenjones): Implement ImportDefinition as it is not always going to be a simple + +(line 187) (kenjones): assume the requirement has a node specified, otherwise need to use the + + +### ``topology.go`` +(line 62) (kenjones): Add support for Groups + + +### ``tosca_namespace_alias.go`` +(line 43) (kenjones): Leverage https://github.com/blang/semver to provide Version implementation details. + +(line 45) Version.GetMajor + +(line 52) Version.GetMinor + +(line 59) Version.GetFixVersion + +(line 66) Version.GetQualifier + +(line 73) Version.GetBuildVersion + + +### ``tosca_reusable_modeling_definitions.go`` +(line 19) : Implement ArtifactDefinition struct + +(line 22) : Implement NodeFilter struct + +(line 70) : Implement ArtifactType struct + diff --git a/attributes.go b/attributes.go index c46310b..03b9fc7 100644 --- a/attributes.go +++ b/attributes.go @@ -36,7 +36,3 @@ func newAAValue(val interface{}) *AttributeAssignment { v.Value = val return v } - -func newAA(def AttributeDefinition) *AttributeAssignment { - return newAAValue(def.Default) -} diff --git a/capabilities.go b/capabilities.go index 06b0ce2..208fbde 100644 --- a/capabilities.go +++ b/capabilities.go @@ -16,7 +16,7 @@ limitations under the License. package toscalib -// CapabilityDefinition TODO: Appendix 6.1 +// CapabilityDefinition Appendix 6.1 type CapabilityDefinition struct { Type string `yaml:"type" json:"type"` // The required name of the Capability Type the capability definition is based upon. Description string `yaml:"description,omitempty" jsson:"description,omitempty"` // The optional description of the Capability definition. @@ -113,9 +113,7 @@ func (c *CapabilityDefinition) extendFrom(capType CapabilityType) { } } - // handle reflecting any new properties as attributes - tmp := reflectDefinitionProps(c.Properties, c.Attributes) - c.Attributes = *tmp + c.reflectProperties() } // CapabilityType as described in appendix 6.6 @@ -146,3 +144,17 @@ func (c *CapabilityAssignment) reflectProperties() { tmp := reflectAssignmentProps(c.Properties, c.Attributes) c.Attributes = *tmp } + +func (c *CapabilityAssignment) extendFrom(cd CapabilityDefinition) { + for k, v := range cd.Properties { + if len(c.Properties) == 0 { + c.Properties = make(map[string]PropertyAssignment) + } + if _, ok := c.Properties[k]; !ok { + tmp := newPA(v) + c.Properties[k] = *tmp + } + } + + c.reflectProperties() +} diff --git a/flattener.go b/flattener.go index 3e84a76..99df81a 100644 --- a/flattener.go +++ b/flattener.go @@ -2,11 +2,32 @@ package toscalib import "github.com/kenjones-cisco/mergo" -func (s *ServiceTemplateDefinition) flattenCapType(name string) CapabilityType { +type flatTypes struct { + Capabilities map[string]CapabilityType + Interfaces map[string]InterfaceType + Relationships map[string]RelationshipType + Nodes map[string]NodeType + Groups map[string]GroupType + Policies map[string]PolicyType +} + +// TODO(kenjones): Switch to reflect if possible in the future (if simple) + +func flattenCapType(name string, s ServiceTemplateDefinition) CapabilityType { if ct, ok := s.CapabilityTypes[name]; ok { if ct.DerivedFrom != "" { - parent := s.flattenCapType(ct.DerivedFrom) + parent := flattenCapType(ct.DerivedFrom, s) + // mergo does not handle merging Slices so the rt items + // will wipe away, capture the values here. + sources := parent.ValidSources + _ = mergo.MergeWithOverwrite(&parent, ct) + + // now copy them back in using append, if the child type had + // any previously, otherwise it will duplicate the parents. + if len(ct.ValidSources) > 0 { + parent.ValidSources = append(parent.ValidSources, sources...) + } return parent } return ct @@ -14,14 +35,47 @@ func (s *ServiceTemplateDefinition) flattenCapType(name string) CapabilityType { return CapabilityType{} } -func (s *ServiceTemplateDefinition) flattenNodeType(name string) NodeType { +func flattenIntfType(name string, s ServiceTemplateDefinition) InterfaceType { + if it, ok := s.InterfaceTypes[name]; ok { + if it.DerivedFrom != "" { + parent := flattenIntfType(it.DerivedFrom, s) + _ = mergo.MergeWithOverwrite(&parent, it) + return parent + } + return it + } + return InterfaceType{} +} + +func flattenRelType(name string, s ServiceTemplateDefinition) RelationshipType { + if rt, ok := s.RelationshipTypes[name]; ok { + if rt.DerivedFrom != "" { + parent := flattenRelType(rt.DerivedFrom, s) + // mergo does not handle merging Slices so the rt items + // will wipe away, capture the values here. + targets := parent.ValidTarget + + _ = mergo.MergeWithOverwrite(&parent, rt) + + // now copy them back in using append, if the child type had + // any previously, otherwise it will duplicate the parents. + if len(rt.ValidTarget) > 0 { + parent.ValidTarget = append(parent.ValidTarget, targets...) + } + return parent + } + return rt + } + return RelationshipType{} +} + +func flattenNodeType(name string, s ServiceTemplateDefinition) NodeType { if nt, ok := s.NodeTypes[name]; ok { if nt.DerivedFrom != "" { - parent := s.flattenNodeType(nt.DerivedFrom) + parent := flattenNodeType(nt.DerivedFrom, s) // mergo does not handle merging Slices so the nt items // will wipe away, capture the values here. reqs := parent.Requirements - arts := parent.Artifacts _ = mergo.MergeWithOverwrite(&parent, nt) @@ -30,12 +84,110 @@ func (s *ServiceTemplateDefinition) flattenNodeType(name string) NodeType { if len(nt.Requirements) > 0 { parent.Requirements = append(parent.Requirements, reqs...) } - if len(nt.Artifacts) > 0 { - parent.Artifacts = append(parent.Artifacts, arts...) - } return parent } return nt } return NodeType{} } + +func flattenGroupType(name string, s ServiceTemplateDefinition) GroupType { + if gt, ok := s.GroupTypes[name]; ok { + if gt.DerivedFrom != "" { + parent := flattenGroupType(gt.DerivedFrom, s) + // mergo does not handle merging Slices so the nt items + // will wipe away, capture the values here. + reqs := parent.Requirements + members := parent.Members + + _ = mergo.MergeWithOverwrite(&parent, gt) + + // now copy them back in using append, if the child type had + // any previously, otherwise it will duplicate the parents. + if len(gt.Requirements) > 0 { + parent.Requirements = append(parent.Requirements, reqs...) + } + if len(gt.Members) > 0 { + parent.Members = append(parent.Members, members...) + } + return parent + } + return gt + } + return GroupType{} +} + +func flattenPolicyType(name string, s ServiceTemplateDefinition) PolicyType { + if pt, ok := s.PolicyTypes[name]; ok { + if pt.DerivedFrom != "" { + parent := flattenPolicyType(pt.DerivedFrom, s) + // mergo does not handle merging Slices so the items + // will wipe away, capture the values here. + targets := parent.Targets + + _ = mergo.MergeWithOverwrite(&parent, pt) + + // now copy them back in using append, if the child type had + // any previously, otherwise it will duplicate the parents. + if len(pt.Targets) > 0 { + parent.Targets = append(parent.Targets, targets...) + } + return parent + } + return pt + } + return PolicyType{} +} + +func flattenHierarchy(s ServiceTemplateDefinition) flatTypes { + var flats flatTypes + + flats.Capabilities = make(map[string]CapabilityType) + for name := range s.CapabilityTypes { + flats.Capabilities[name] = flattenCapType(name, s) + } + + flats.Interfaces = make(map[string]InterfaceType) + for name := range s.InterfaceTypes { + flats.Interfaces[name] = flattenIntfType(name, s) + } + + flats.Relationships = make(map[string]RelationshipType) + for name := range s.RelationshipTypes { + flats.Relationships[name] = flattenRelType(name, s) + } + + flats.Nodes = make(map[string]NodeType) + for name := range s.NodeTypes { + flats.Nodes[name] = flattenNodeType(name, s) + } + + for k, v := range flats.Nodes { + for name, capDef := range v.Capabilities { + capDef.extendFrom(flats.Capabilities[capDef.Type]) + v.Capabilities[name] = capDef + } + for name, iDef := range v.Interfaces { + // during merge the Type is not always properly inherited, so set it + // from the parent. + if iDef.Type == "" { + iDef.Type = flats.Nodes[v.DerivedFrom].Interfaces[name].Type + } + iDef.extendFrom(flats.Interfaces[iDef.Type]) + v.Interfaces[name] = iDef + } + flats.Nodes[k] = v + } + + flats.Groups = make(map[string]GroupType) + for name := range s.GroupTypes { + flats.Groups[name] = flattenGroupType(name, s) + } + + flats.Policies = make(map[string]PolicyType) + for name := range s.PolicyTypes { + flats.Policies[name] = flattenPolicyType(name, s) + } + + return flats +} diff --git a/interfaces.go b/interfaces.go index 353dd4c..09f95a5 100644 --- a/interfaces.go +++ b/interfaces.go @@ -16,6 +16,8 @@ limitations under the License. package toscalib +import "github.com/kenjones-cisco/mergo" + // InterfaceType as described in Appendix A 6.4 // An Interface Type is a reusable entity that describes a set of operations that can be used to interact with or manage a node or relationship in a TOSCA topology. type InterfaceType struct { @@ -23,8 +25,8 @@ type InterfaceType struct { Version Version `yaml:"version,omitempty"` Metadata Metadata `yaml:"metadata,omitempty" json:"metadata"` Description string `yaml:"description,omitempty"` - Operations map[string]OperationDefinition `yaml:"operations,inline"` Inputs map[string]PropertyDefinition `yaml:"inputs,omitempty" json:"inputs"` // The optional list of input parameter definitions. + Operations map[string]OperationDefinition `yaml:"operations,inline"` } // OperationDefinition defines a named function or procedure that can be bound to an implementation artifact (e.g., a script). @@ -57,34 +59,65 @@ func (i *OperationDefinition) UnmarshalYAML(unmarshal func(interface{}) error) e // InterfaceDefinition is related to a node type type InterfaceDefinition struct { - Type string `yaml:"type" json:"type"` - Inputs map[string]PropertyAssignment `yaml:"inputs,omitempty"` - Description string `yaml:"description,omitempty"` - Implementation string `yaml:"implementation,omitempty"` - Operations map[string]OperationDefinition `yaml:"operations,inline"` + Type string `yaml:"type" json:"type"` + Inputs map[string]PropertyAssignment `yaml:"inputs,omitempty"` + Operations map[string]OperationDefinition `yaml:"operations,inline"` } -// UnmarshalYAML converts YAML text to a type -func (i *InterfaceDefinition) UnmarshalYAML(unmarshal func(interface{}) error) error { - var s string - if err := unmarshal(&s); err == nil { - i.Implementation = s - return nil +func (i *InterfaceDefinition) extendFrom(intfType InterfaceType) { + + base := intfType.Operations + _ = mergo.MergeWithOverwrite(&base, i.Operations) + i.Operations = base + + for k, v := range intfType.Inputs { + if len(i.Inputs) == 0 { + i.Inputs = make(map[string]PropertyAssignment) + } + if _, ok := i.Inputs[k]; !ok { + tmp := newPA(v) + i.Inputs[k] = *tmp + } } - var str struct { - Type string `yaml:"type" json:"type"` - Inputs map[string]PropertyAssignment `yaml:"inputs,omitempty"` - Description string `yaml:"description,omitempty"` - Implementation string `yaml:"implementation,omitempty"` - Operations map[string]OperationDefinition `yaml:"operations,inline"` +} + +func (i *InterfaceDefinition) merge(other InterfaceDefinition) { + if i.Type == "" { + i.Type = other.Type } - if err := unmarshal(&str); err != nil { - return err + + for k, v := range other.Inputs { + if len(i.Inputs) == 0 { + i.Inputs = make(map[string]PropertyAssignment) + } + if _, ok := i.Inputs[k]; !ok { + i.Inputs[k] = v + } + } + + for k, v := range other.Operations { + if len(i.Operations) == 0 { + i.Operations = make(map[string]OperationDefinition) + } + if op, ok := i.Operations[k]; ok { + if op.Description == "" { + op.Description = v.Description + } + if op.Implementation == "" { + op.Implementation = v.Implementation + } + if len(op.Inputs) == 0 { + op.Inputs = v.Inputs + } else { + for pn, pv := range v.Inputs { + if _, ok := op.Inputs[pn]; !ok { + op.Inputs[pn] = pv + } + } + } + i.Operations[k] = op + } else { + i.Operations[k] = v + } } - i.Type = str.Type - i.Inputs = str.Inputs - i.Implementation = str.Implementation - i.Description = str.Description - i.Operations = str.Operations - return nil } diff --git a/node_template.go b/node_template.go index bad54c8..0b4eb5c 100644 --- a/node_template.go +++ b/node_template.go @@ -16,10 +16,7 @@ limitations under the License. package toscalib -import ( - "fmt" - "regexp" -) +import "github.com/kenjones-cisco/mergo" // NodeTemplate as described in Appendix 7.3 // A Node Template specifies the occurrence of a manageable software component @@ -37,13 +34,12 @@ type NodeTemplate struct { Attributes map[string]AttributeAssignment `yaml:"attributes,omitempty" json:"-" json:"attributes,omitempty"` // An optional list of attribute value assignments for the Node Template. Requirements []map[string]RequirementAssignment `yaml:"requirements,omitempty" json:"-" json:"requirements,omitempty"` // An optional sequenced list of requirement assignments for the Node Template. Capabilities map[string]CapabilityAssignment `yaml:"capabilities,omitempty" json:"-" json:"capabilities,omitempty"` // An optional list of capability assignments for the Node Template. - Interfaces map[string]InterfaceType `yaml:"interfaces,omitempty" json:"-" json:"interfaces,omitempty"` // An optional list of named interface definitions for the Node Template. + Interfaces map[string]InterfaceDefinition `yaml:"interfaces,omitempty" json:"-" json:"interfaces,omitempty"` // An optional list of named interface definitions for the Node Template. Artifacts map[string]ArtifactDefinition `yaml:"artifacts,omitempty" json:"-" json:"artifacts,omitempty"` // An optional list of named artifact definitions for the Node Template. NodeFilter map[string]NodeFilter `yaml:"node_filter,omitempty" json:"-" json:"node_filter,omitempty"` // The optional filter definition that TOSCA orchestrators would use to select the correct target node. This keyname is only valid if the directive has the value of “selectable” set. Copy string `yaml:"copy,omitempty" json:"copy,omitempty"` // The optional (symbolic) name of another node template to copy into (all keynames and values) and use as a basis for this node template. Refs struct { - Type NodeType `yaml:"-",json:"-"` - Interfaces []InterfaceType `yaml:"-",json:"-"` + Type NodeType `yaml:"-",json:"-"` } `yaml:"-",json:"-"` } @@ -80,29 +76,10 @@ func (n *NodeTemplate) GetRelationshipTarget(relationshipName string) string { func (n *NodeTemplate) getRequirementByRelationship(relationshipName string) *RequirementAssignment { for _, req := range n.Requirements { - for name, r := range req { + for _, r := range req { if r.Relationship.Type == relationshipName { return &r } - if r.Relationship.Type == "" { - if rd := n.getRequirementRelationshipType(name, relationshipName); rd != nil { - r.Capability = rd.Capability - r.Relationship.Type = rd.Relationship.Type - // TODO(kenjones): Set Node attribute from node type when Node Filters implemented - return &r - } - } - } - } - return nil -} - -func (n *NodeTemplate) getRequirementRelationshipType(name, relationshipName string) *RequirementDefinition { - for _, req := range n.Refs.Type.Requirements { - if rd, ok := req[name]; ok { - if rd.Relationship.Type == relationshipName { - return &rd - } } } return nil @@ -126,16 +103,10 @@ func (n *NodeTemplate) findProperty(key, capname string) *PropertyAssignment { if prop, ok := n.Capabilities[capname].Properties[key]; ok { return &prop } - if prop, ok := n.Refs.Type.Capabilities[capname].Properties[key]; ok { - return newPA(prop) - } } if prop, ok := n.Properties[key]; ok { return &prop } - if prop, ok := n.Refs.Type.Properties[key]; ok { - return newPA(prop) - } return nil } @@ -144,16 +115,10 @@ func (n *NodeTemplate) findAttribute(key, capname string) *AttributeAssignment { if attr, ok := n.Capabilities[capname].Attributes[key]; ok { return &attr } - if attr, ok := n.Refs.Type.Capabilities[capname].Attributes[key]; ok { - return newAA(attr) - } } if attr, ok := n.Attributes[key]; ok { return &attr } - if attr, ok := n.Refs.Type.Attributes[key]; ok { - return newAA(attr) - } return nil } @@ -168,80 +133,82 @@ func (n *NodeTemplate) reflectProperties() { } } -// setRefs fills in the references of the node -func (n *NodeTemplate) setRefs(s ServiceTemplateDefinition, nt map[string]NodeType) { - for name := range n.Interfaces { - re := regexp.MustCompile(fmt.Sprintf("%v$", name)) - for na, v := range s.InterfaceTypes { - if re.MatchString(na) { - n.Refs.Interfaces = append(n.Refs.Interfaces, v) - } +func (n *NodeTemplate) _extendCaps(nt NodeType) { + // make sure each inherited Capability is added + for k := range nt.Capabilities { + if len(n.Capabilities) == 0 { + n.Capabilities = make(map[string]CapabilityAssignment) + } + if _, ok := n.Capabilities[k]; !ok { + n.Capabilities[k] = CapabilityAssignment{} } } - n.Refs.Type = nt[n.Type] -} -// fillInterface Completes the interface of the node with any values found in its type -// All the Operations will be filled -func (n *NodeTemplate) fillInterface(s ServiceTemplateDefinition) { - nt := s.NodeTypes[n.Type] - if len(n.Interfaces) == 0 { - // If no interface is found, take the one from the node type - myInterfaces := make(map[string]InterfaceType, 1) + // then make sure the values are extended from the inherited + for k, v := range n.Capabilities { + v.extendFrom(nt.Capabilities[k]) + n.Capabilities[k] = v + } +} - for intfname, intftype := range nt.Interfaces { - operations := make(map[string]OperationDefinition, 0) - for opname, interfacedef := range intftype.Operations { - operations[opname] = OperationDefinition{ - Description: interfacedef.Description, - Implementation: interfacedef.Implementation, - } +func (n *NodeTemplate) _extendReqs(nt NodeType) { + // make sure each inherited Requirement is added + for _, reqs := range nt.Requirements { + if len(n.Requirements) == 0 { + n.Requirements = make([]map[string]RequirementAssignment, 0) + } + for k := range reqs { + if r := n.GetRequirement(k); r == nil { + tmp := make(map[string]RequirementAssignment) + tmp[k] = RequirementAssignment{} + n.Requirements = append(n.Requirements, tmp) } - myInterfaces[intfname] = InterfaceType{Operations: operations} } - - n.Interfaces = myInterfaces - return } - for name, intf := range n.Interfaces { - intf2, ok := nt.Interfaces[name] - if !ok { - continue + // then make sure the values are extended from the inherited + for i, reqs := range n.Requirements { + for k, v := range reqs { + v.extendFrom(nt.getRequirement(k)) + reqs[k] = v } - re := regexp.MustCompile(fmt.Sprintf("%v$", name)) - for ifacename, iface := range s.InterfaceTypes { - if re.MatchString(ifacename) { - operations := make(map[string]OperationDefinition, 0) + n.Requirements[i] = reqs + } +} - for op := range iface.Operations { - v, ok := intf.Operations[op] - v2, ok2 := intf2.Operations[op] +func (n *NodeTemplate) extendFrom(nt NodeType) { + n.Refs.Type = nt - switch { - case !ok && ok2: - operations[op] = OperationDefinition{ - Description: v2.Description, - Implementation: v2.Implementation, - } - case ok: - if ok2 && v.Implementation == "" { - v.Implementation = v2.Implementation - } - operations[op] = v - } - } + for k, v := range nt.Interfaces { + if len(n.Interfaces) == 0 { + n.Interfaces = make(map[string]InterfaceDefinition) + } + if intf, ok := n.Interfaces[k]; ok { + intf.merge(v) + n.Interfaces[k] = intf + } else { + n.Interfaces[k] = v + } + } - n.Interfaces[name] = InterfaceType{ - Description: n.Interfaces[name].Description, - Version: n.Interfaces[name].Version, - Operations: operations, - Inputs: n.Interfaces[name].Inputs, - } + abase := nt.Artifacts + _ = mergo.MergeWithOverwrite(&abase, n.Artifacts) + n.Artifacts = abase - } + n._extendCaps(nt) + n._extendReqs(nt) + + for k, v := range nt.Properties { + if len(n.Properties) == 0 { + n.Properties = make(map[string]PropertyAssignment) + } + if _, ok := n.Properties[k]; !ok { + tmp := newPA(v) + n.Properties[k] = *tmp } } + + n.reflectProperties() } func (n *NodeTemplate) setName(name string) { diff --git a/node_type.go b/node_type.go index 4c585b5..05cf2c7 100644 --- a/node_type.go +++ b/node_type.go @@ -27,7 +27,7 @@ type NodeType struct { Requirements []map[string]RequirementDefinition `yaml:"requirements,omitempty" json:"requirements,omitempty"` // An optional sequenced list of requirement definitions for the Node Type Capabilities map[string]CapabilityDefinition `yaml:"capabilities,omitempty" json:"capabilities,omitempty"` // An optional list of capability definitions for the Node Type Interfaces map[string]InterfaceDefinition `yaml:"interfaces,omitempty" json:"interfaces,omitempty"` // An optional list of interface definitions supported by the Node Type - Artifacts []ArtifactDefinition `yaml:"artifacts,omitempty" json:"artifacts,omitempty"` // An optional list of named artifact definitions for the Node Type + Artifacts map[string]ArtifactDefinition `yaml:"artifacts,omitempty" json:"artifacts,omitempty"` // An optional list of named artifact definitions for the Node Type } func (n *NodeType) reflectProperties() { @@ -40,3 +40,14 @@ func (n *NodeType) reflectProperties() { n.Capabilities[capname] = c } } + +func (n *NodeType) getRequirement(name string) RequirementDefinition { + for _, reqs := range n.Requirements { + for k, v := range reqs { + if k == name { + return v + } + } + } + return RequirementDefinition{} +} diff --git a/policies.go b/policies.go index 4a757f1..c3dffc3 100644 --- a/policies.go +++ b/policies.go @@ -1,5 +1,7 @@ package toscalib +import "github.com/kenjones-cisco/mergo" + // EventFilterDefinition provides structure for event_filter of a Trigger type EventFilterDefinition struct { Node string `yaml:"node" json:"node"` @@ -83,3 +85,20 @@ func (pd *PolicyDefinition) IsValidTarget(name string) bool { } return false } + +func (pd *PolicyDefinition) extendFrom(pt PolicyType) { + + base := pt.Triggers + _ = mergo.MergeWithOverwrite(&base, pd.Triggers) + pd.Triggers = base + + for k, v := range pt.Properties { + if len(pd.Properties) == 0 { + pd.Properties = make(map[string]PropertyAssignment) + } + if _, ok := pd.Properties[k]; !ok { + tmp := newPA(v) + pd.Properties[k] = *tmp + } + } +} diff --git a/relationships.go b/relationships.go index e14e9f7..376ea15 100644 --- a/relationships.go +++ b/relationships.go @@ -35,6 +35,22 @@ func (r *RelationshipType) reflectProperties() { r.Attributes = *tmp } +// IsValidTarget checks to see if a specified type is in the list of valid targets +// and returns true/false. If there are no defined valid targets then it will +// always be true. +func (r *RelationshipType) IsValidTarget(typeName string) bool { + if len(r.ValidTarget) == 0 { + return true + } + + for _, t := range r.ValidTarget { + if t == typeName { + return true + } + } + return false +} + // RelationshipTemplate specifies the occurrence of a manageable relationship between node templates // as part of an application’s topology model that is defined in a TOSCA Service Template. // A Relationship template is an instance of a specified Relationship Type and can provide customized @@ -55,18 +71,25 @@ func (r *RelationshipTemplate) reflectProperties() { r.Attributes = *tmp } -// IsValidTarget checks to see if a specified type is in the list of valid targets -// and returns true/false. If there are no defined valid targets then it will -// always be true. -func (r *RelationshipType) IsValidTarget(typeName string) bool { - if len(r.ValidTarget) == 0 { - return true +func (r *RelationshipTemplate) extendFrom(relType RelationshipType) { + for k, v := range relType.Interfaces { + if len(r.Interfaces) == 0 { + r.Interfaces = make(map[string]InterfaceDefinition) + } + if _, ok := r.Interfaces[k]; !ok { + r.Interfaces[k] = v + } } - for _, t := range r.ValidTarget { - if t == typeName { - return true + for k, v := range relType.Properties { + if len(r.Properties) == 0 { + r.Properties = make(map[string]PropertyAssignment) + } + if _, ok := r.Properties[k]; !ok { + tmp := newPA(v) + r.Properties[k] = *tmp } } - return false + + r.reflectProperties() } diff --git a/requirements.go b/requirements.go index 656ca40..4e8bddd 100644 --- a/requirements.go +++ b/requirements.go @@ -16,6 +16,8 @@ limitations under the License. package toscalib +import "github.com/kenjones-cisco/mergo" + // RequirementRelationshipType defines the Relationship type of a Requirement Definition type RequirementRelationshipType struct { Type string `yaml:"type" json:"type"` @@ -151,3 +153,19 @@ func (r *RequirementAssignment) UnmarshalYAML(unmarshal func(interface{}) error) r.Relationship = test2.Relationship return nil } + +func (r *RequirementAssignment) extendFrom(rd RequirementDefinition) { + if r.Capability == "" { + r.Capability = rd.Capability + } + if r.Node == "" { + r.Node = rd.Node + } + if r.Relationship.Type == "" { + r.Relationship.Type = rd.Relationship.Type + } + + base := rd.Relationship.Interfaces + _ = mergo.MergeWithOverwrite(&base, r.Relationship.Interfaces) + r.Relationship.Interfaces = base +} diff --git a/service_template.go b/service_template.go index ad89d10..07566f5 100644 --- a/service_template.go +++ b/service_template.go @@ -45,31 +45,9 @@ func (s *ServiceTemplateDefinition) resolve() { // reflect properties to attributes s.reflectProperties() - flatCaps := make(map[string]CapabilityType) - for name := range s.CapabilityTypes { - flatCaps[name] = s.flattenCapType(name) - } - - flatNodes := make(map[string]NodeType) - for name := range s.NodeTypes { - flatNodes[name] = s.flattenNodeType(name) - } - - for k, v := range flatNodes { - for name, capDef := range v.Capabilities { - capDef.extendFrom(flatCaps[capDef.Type]) - v.Capabilities[name] = capDef - } - flatNodes[k] = v - } - - // make sure any references are fulfilled - for name, node := range s.TopologyTemplate.NodeTemplates { - node.fillInterface(*s) - node.setRefs(*s, flatNodes) - node.setName(name) - s.TopologyTemplate.NodeTemplates[name] = node - } + // resolve inherited data + ft := flattenHierarchy(*s) + s.TopologyTemplate.extendFrom(ft) } func (s *ServiceTemplateDefinition) reflectProperties() { diff --git a/topology.go b/topology.go index efdf00a..a350957 100644 --- a/topology.go +++ b/topology.go @@ -46,3 +46,26 @@ func (t *TopologyTemplateType) reflectProperties() { t.RelationshipTemplates[k] = v } } + +func (t *TopologyTemplateType) extendFrom(ft flatTypes) { + for k, v := range t.NodeTemplates { + v.extendFrom(ft.Nodes[v.Type]) + v.setName(k) + t.NodeTemplates[k] = v + } + + for k, v := range t.RelationshipTemplates { + v.extendFrom(ft.Relationships[v.Type]) + t.RelationshipTemplates[k] = v + } + + // TODO(kenjones): Add support for Groups + + for i, policies := range t.Policies { + for k, v := range policies { + v.extendFrom(ft.Policies[v.Type]) + policies[k] = v + } + t.Policies[i] = policies + } +} diff --git a/tosca_functions.go b/tosca_functions.go index f6eaecd..6b4958d 100644 --- a/tosca_functions.go +++ b/tosca_functions.go @@ -50,9 +50,3 @@ func isFunction(f string) bool { } return false } - -// ToscaFunction defines the interface for implementing a pre-defined -// function from tosca. -type ToscaFunction interface { - Evaluate(std *ServiceTemplateDefinition, ctx string) interface{} -} diff --git a/tosca_namespace_alias.go b/tosca_namespace_alias.go index f014d9f..83861f0 100644 --- a/tosca_namespace_alias.go +++ b/tosca_namespace_alias.go @@ -40,38 +40,37 @@ import ( // that has the same qualifer_string type Version string -// TODO(kenjones): Leverage https://github.com/blang/semver -// to provide implementation details. +// TODO(kenjones): Leverage https://github.com/blang/semver to provide Version implementation details. -/*TODO +/*TODO Version.GetMajor // GetMajor returns the major_version number func (toscaVersion *Version) GetMajor() int { return 0 } */ -/*TODO +/*TODO Version.GetMinor // GetMinor returns the minor_version number func (toscaVersion *Version) GetMinor() int { return 0 } */ -/*TODO +/*TODO Version.GetFixVersion // GetFixVersion returns the fix_version integer value func (toscaVersion *Version) GetFixVersion() int { return 0 } */ -/*TODO +/*TODO Version.GetQualifier // GetQualifier returns the named, pre-release version of the associated code that has been derived from the version of the code identified by the combination major_version, minor_version and fix_version numbers func (toscaVersion *Version) GetQualifier() string { return nil } */ -/*TODO +/*TODO Version.GetBuildVersion // GetBuildVersion returns an integer value greater than or equal to 0 (zero) that can be used to further qualify different build versions of the code that has the same qualifer_string func (toscaVersion *Version) GetBuildVersion() int { return 0 diff --git a/tosca_reusable_modeling_definitions.go b/tosca_reusable_modeling_definitions.go index fee6b94..b689779 100644 --- a/tosca_reusable_modeling_definitions.go +++ b/tosca_reusable_modeling_definitions.go @@ -16,10 +16,10 @@ limitations under the License. package toscalib -// ArtifactDefinition TODO: Appendix 5.5 +// ArtifactDefinition Appendix 5.5 TODO: Implement ArtifactDefinition struct type ArtifactDefinition map[string]interface{} -// NodeFilter TODO Appendix 5.4 +// NodeFilter Appendix 5.4 TODO: Implement NodeFilter struct // A node filter definition defines criteria for selection of a TOSCA Node Template based upon the template’s property values, capabilities and capability properties. type NodeFilter interface{} @@ -67,7 +67,7 @@ func (r *RepositoryDefinition) UnmarshalYAML(unmarshal func(interface{}) error) // ArtifactType as described in appendix 6.3 // An Artifact Type is a reusable entity that defines the type of one or more files which Node Types or Node Templates can have dependent relationships and used during operations such as during installation or deployment. -// TODO +// TODO: Implement ArtifactType struct type ArtifactType interface{} // Metadata is provides support for attaching provider specific attributes