Skip to content

Commit

Permalink
Improve Clout normalization and copy
Browse files Browse the repository at this point in the history
* Also various API changes in Kutil
  • Loading branch information
tliron committed Jun 9, 2022
1 parent 95af4ac commit ac66999
Show file tree
Hide file tree
Showing 34 changed files with 689 additions and 688 deletions.
20 changes: 10 additions & 10 deletions assets/tosca/profiles/common/1.0/js/lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,28 +105,28 @@ exports.getNestedValue = function(singular, plural, args) {
let value = nodeTemplate[plural];
if (arg in nodeTemplate.capabilities) {
value = nodeTemplate.capabilities[arg][plural];
singular = puccini.sprintf('capability "%s" %s', arg, singular);
singular = puccini.sprintf('capability %q %s', arg, singular);
arg = args[++a];
} else for (let r = 0, l = nodeTemplate.requirements.length; r < l; r++) {
let requirement = nodeTemplate.requirements[r];
if ((requirement.name === arg) && requirement.relationship) {
value = requirement.relationship[plural];
singular = puccini.sprintf('relationship "%s" %s', arg, singular);
singular = puccini.sprintf('relationship %q %s', arg, singular);
arg = args[++a];
break;
}
}
if ((typeof value === 'object') && (value !== null) && (arg in value))
value = value[arg];
else
throw puccini.sprintf('%s "%s" not found in "%s"', singular, arg, nodeTemplate.name);
throw puccini.sprintf('%s %q not found in %q', singular, arg, nodeTemplate.name);
value = clout.coerce(value);
for (let i = a + 1; i < length; i++) {
arg = args[i];
if ((typeof value === 'object') && (value !== null) && (arg in value))
value = value[arg];
else
throw puccini.sprintf('nested %s "%s" not found in "%s"', singular, args.slice(a, i+1).join('.'), nodeTemplate.name);
throw puccini.sprintf('nested %s %q not found in %q', singular, args.slice(a, i+1).join('.'), nodeTemplate.name);
}
return value;
};
Expand All @@ -136,22 +136,22 @@ exports.getModelableEntity = function(entity) {
switch (entity) {
case 'SELF':
if (!this || !this.site)
throw puccini.sprintf('"%s" cannot be used in this context', entity);
throw puccini.sprintf('%q cannot be used in this context', entity);
vertex = this.site;
break;
case 'SOURCE':
if (!this || !this.source)
throw puccini.sprintf('"%s" cannot be used in this context', entity);
throw puccini.sprintf('%q cannot be used in this context', entity);
vertex = this.source;
break;
case 'TARGET':
if (!this || !this.target)
throw puccini.sprintf('"%s" cannot be used in this context', entity);
throw puccini.sprintf('%q cannot be used in this context', entity);
vertex = this.target;
break;
case 'HOST':
if (!this || !this.site)
throw puccini.sprintf('"%s" cannot be used in this context', entity);
throw puccini.sprintf('%q cannot be used in this context', entity);
vertex = exports.getHost(this.site);
break;
default:
Expand All @@ -165,7 +165,7 @@ exports.getModelableEntity = function(entity) {
if (exports.isNodeTemplate(vertex))
return vertex.properties;
else
throw puccini.sprintf('node template "%s" not found', entity);
throw puccini.sprintf('node template %q not found', entity);
};

exports.getHost = function(vertex) {
Expand All @@ -180,7 +180,7 @@ exports.getHost = function(vertex) {
}
}
if (exports.isNodeTemplate(vertex))
throw puccini.sprintf('"HOST" not found for node template "%s"', vertex.properties.name);
throw puccini.sprintf('"HOST" not found for node template %q', vertex.properties.name);
else
throw '"HOST" not found';
};
Expand Down
22 changes: 11 additions & 11 deletions assets/tosca/profiles/common/1.0/js/resolve.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ function resolve(sourceVertex, sourceNodeTemplate, requirement) {
chosen = candidate;
}

puccini.log.debugf('%s: satisfied "%s" with capability "%s" in node template "%s"', location.path, name, chosen.capabilityName, chosen.nodeTemplateName);
puccini.log.debugf('%s: satisfied %q with capability %q in node template %q', location.path, name, chosen.capabilityName, chosen.nodeTemplateName);
addRelationship(sourceVertex, requirement, chosen.vertex, chosen.capabilityName);
}

Expand All @@ -125,18 +125,18 @@ function gatherCandidateNodeTemplates(sourceVertex, requirement) {
let candidateNodeTemplateName = candidateNodeTemplate.name;

if ((nodeTemplateName !== '') && (nodeTemplateName !== candidateNodeTemplateName)) {
puccini.log.debugf('%s: node template "%s" is not named "%s"', path, candidateNodeTemplateName, nodeTemplateName);
puccini.log.debugf('%s: node template %q is not named %q', path, candidateNodeTemplateName, nodeTemplateName);
continue;
}

if ((nodeTypeName !== '') && !(nodeTypeName in candidateNodeTemplate.types)) {
puccini.log.debugf('%s: node template "%s" is not of type "%s"', path, candidateNodeTemplateName, nodeTypeName);
puccini.log.debugf('%s: node template %q is not of type %q', path, candidateNodeTemplateName, nodeTypeName);
continue;
}

// Node filter
if ((nodeTemplatePropertyConstraints.length !== 0) && !arePropertiesValid(path, sourceVertex, 'node template', candidateNodeTemplateName, candidateNodeTemplate, nodeTemplatePropertyConstraints)) {
puccini.log.debugf('%s: properties of node template "%s" do not match constraints', path, candidateNodeTemplateName);
puccini.log.debugf('%s: properties of node template %q do not match constraints', path, candidateNodeTemplateName);
continue;
}

Expand All @@ -159,7 +159,7 @@ function gatherCandidateNodeTemplates(sourceVertex, requirement) {
}

if ((capabilityPropertyConstraints !== undefined) && (capabilityPropertyConstraints.length !== 0) && !arePropertiesValid(path, sourceVertex, 'capability', candidateCapabilityName, candidateCapability, capabilityPropertyConstraints)) {
puccini.log.debugf('%s: properties of capability "%s" in node template "%s" do not match constraints', path, candidateCapabilityName, candidateNodeTemplateName);
puccini.log.debugf('%s: properties of capability %q in node template %q do not match constraints', path, candidateCapabilityName, candidateNodeTemplateName);
valid = false;
break;
}
Expand Down Expand Up @@ -206,21 +206,21 @@ function gatherCandidateCapabilities(requirement, candidateNodeTemplates) {
let candidateCapabilityName = candidateCapabilities[cc].name;

if ((capabilityName !== '') && (capabilityName !== candidateCapabilityName)) {
puccini.log.debugf('%s: capability "%s" in node template "%s" is not named "%s"', path, candidateCapabilityName, candidateNodeTemplateName, capabilityName);
puccini.log.debugf('%s: capability %q in node template %q is not named %q', path, candidateCapabilityName, candidateNodeTemplateName, capabilityName);
continue;
}

let candidateCapability = candidateCapabilities[cc].capability;

if ((capabilityTypeName !== '') && !(capabilityTypeName in candidateCapability.types)) {
puccini.log.debugf('%s: capability "%s" in node template "%s" is not of type "%s"', path, candidateCapabilityName, candidateNodeTemplateName, capabilityTypeName);
puccini.log.debugf('%s: capability %q in node template %q is not of type %q', path, candidateCapabilityName, candidateNodeTemplateName, capabilityTypeName);
continue;
}

if (enforceCapabilityOccurrences) {
let maxRelationshipCount = candidateCapability.maxRelationshipCount;
if ((maxRelationshipCount !== -1) && (countRelationships(candidateVertex, candidateCapabilityName) === maxRelationshipCount)) {
puccini.log.debugf('%s: capability "%s" in node template "%s" already has %d relationships, the maximum allowed', path, candidateCapabilityName, candidateNodeTemplateName, maxRelationshipCount);
puccini.log.debugf('%s: capability %q in node template %q already has %d relationships, the maximum allowed', path, candidateCapabilityName, candidateNodeTemplateName, maxRelationshipCount);
continue;
}
}
Expand Down Expand Up @@ -283,7 +283,7 @@ function arePropertiesValid(path, sourceVertex, kind, name, entity, constraintsM

let properties = entity.properties;
for (let propertyName in constraintsMap) {
puccini.log.debugf('%s: applying constraints to property "%s" of %s "%s"', path, propertyName, kind, name);
puccini.log.debugf('%s: applying constraints to property %q of %s %q', path, propertyName, kind, name);

let property = properties[propertyName];
if (property === undefined) {
Expand Down Expand Up @@ -335,9 +335,9 @@ function isMaxCountGreater(a, b) {

function unsatisfied(location, name, message) {
if (typeof problems === 'undefined')
throw puccini.sprintf('%s: could not satisfy "%s" because %s', location.path, name, message);
throw puccini.sprintf('%s: could not satisfy %q because %s', location.path, name, message);
else
problems.reportFull(11, 'Resolution', location.path, puccini.sprintf('could not satisfy "%s" because %s', name, message), location.row, location.column);
problems.reportFull(11, 'Resolution', location.path, puccini.sprintf('could not satisfy %q because %s', name, message), location.row, location.column);
}

function notEnoughRelationships(location, relationshipCount, minRelationshipCount) {
Expand Down
2 changes: 1 addition & 1 deletion assets/tosca/profiles/hot/1.0/js/functions/get_param.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ exports.evaluate = function(input) {
throw 'Clout is not TOSCA';
let inputs = clout.properties.tosca.inputs;
if (!(input in inputs))
throw puccini.sprintf('parameter "%s" not found', input);
throw puccini.sprintf('parameter %q not found', input);
let r = inputs[input];
r = clout.coerce(r);
return r;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ exports.evaluate = function(entity, artifactName, location, remove) {
throw 'must have at least 2 arguments';
let nodeTemplate = tosca.getModelableEntity.call(this, entity);
if (!nodeTemplate.artifacts || !(artifactName in nodeTemplate.artifacts))
throw puccini.sprintf('artifact "%s" not found in "%s"', artifactName, nodeTemplate.name);
throw puccini.sprintf('artifact %q not found in %q', artifactName, nodeTemplate.name);
let artifact = nodeTemplate.artifacts[artifactName];
if (artifact.$artifact === undefined)
return artifact.sourcePath;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ exports.evaluate = function(input) {
throw 'Clout is not TOSCA';
let inputs = clout.properties.tosca.inputs;
if (!(input in inputs))
throw puccini.sprintf('input "%s" not found', input);
throw puccini.sprintf('input %q not found', input);
let r = inputs[input];
r = clout.coerce(r);
return r;
Expand Down
75 changes: 58 additions & 17 deletions clout/clout.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,32 +57,73 @@ func (self *Clout) Resolve() error {
return fmt.Errorf("unsupported Clout version: %q", self.Version)
}

for key, v := range self.Vertexes {
v.Clout = self
v.ID = key

for _, e := range v.EdgesOut {
var ok bool
if e.Target, ok = self.Vertexes[e.TargetID]; !ok {
return fmt.Errorf("could not resolve Clout, bad TargetID: %q", e.TargetID)
}
return self.ResolveTopology()
}

func (self *Clout) ResolveTopology() error {
for id, vertex := range self.Vertexes {
vertex.Clout = self
vertex.ID = id

e.Source = v
e.Target.EdgesIn = append(e.Target.EdgesIn, e)
for _, edge := range vertex.EdgesOut {
if edge.Target == nil {
var ok bool
if edge.Target, ok = self.Vertexes[edge.TargetID]; !ok {
return fmt.Errorf("could not resolve Clout, bad TargetID: %q", edge.TargetID)
}

edge.Source = vertex
edge.Target.EdgesIn = append(edge.Target.EdgesIn, edge)
}
}
}
return nil
}

func (self *Clout) Copy() (*Clout, error) {
// TODO: not very efficient
if code, err := cbor.Marshal(self); err == nil {
return Read(bytes.NewReader(code), "cbor")
func (self *Clout) AgnosticCopy() (*Clout, error) {
return self.copy(true)
}

func (self *Clout) SimpleCopy() *Clout {
clout, _ := self.copy(false)
return clout
}

func (self *Clout) copy(agnostic bool) (*Clout, error) {
clout := Clout{
Version: Version,
}
var err error
if clout.Vertexes, err = self.Vertexes.copy(agnostic); err == nil {
if agnostic {
if metadata, err := ard.NormalizeStringMapsAgnosticCopy(self.Metadata); err == nil {
if properties, err := ard.NormalizeStringMapsAgnosticCopy(self.Properties); err == nil {
clout.Metadata = metadata.(ard.StringMap)
clout.Properties = properties.(ard.StringMap)
} else {
return nil, err
}
} else {
return nil, err
}
} else {
clout.Metadata = ard.SimpleCopy(self.Metadata).(ard.StringMap)
clout.Properties = ard.SimpleCopy(self.Properties).(ard.StringMap)
}
} else {
return nil, err
}
if err := clout.ResolveTopology(); err == nil {
return &clout, nil
} else {
return nil, err
}
}

func (self *Clout) Normalize() (*Clout, error) {
return self.Copy()
func (self *Clout) copyOld(agnostic bool) (*Clout, error) {
if code, err := cbor.Marshal(self); err == nil {
return Read(bytes.NewReader(code), "cbor")
} else {
return nil, err
}
}
41 changes: 38 additions & 3 deletions clout/edge.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func (self *Vertex) NewEdgeTo(target *Vertex) *Edge {
Metadata: make(ard.StringMap),
Properties: make(ard.StringMap),
Source: self,
TargetID: target.ID,
Target: target,
}
self.EdgesOut = append(self.EdgesOut, edge)
Expand All @@ -43,8 +44,8 @@ func (self *Vertex) NewEdgeToID(targetId string) *Edge {
}

func (self *Edge) Remove() {
self.Source.EdgesOut = self.Source.EdgesOut.Remove(self)
self.Target.EdgesIn = self.Target.EdgesIn.Remove(self)
self.Source.EdgesOut = self.Source.EdgesOut.remove(self)
self.Target.EdgesIn = self.Target.EdgesIn.remove(self)
}

// Entity interface
Expand Down Expand Up @@ -138,13 +139,47 @@ func (self *Edge) UnmarshalCBOR(data []byte) error {
})
}

func (self *Edge) copy(agnostic bool) (*Edge, error) {
edge := Edge{
TargetID: self.TargetID,
}
if agnostic {
if metadata, err := ard.NormalizeStringMapsAgnosticCopy(self.Metadata); err == nil {
if properties, err := ard.NormalizeStringMapsAgnosticCopy(self.Properties); err == nil {
edge.Metadata = metadata.(ard.StringMap)
edge.Properties = properties.(ard.StringMap)
} else {
return nil, err
}
} else {
return nil, err
}
} else {
edge.Metadata = ard.SimpleCopy(self.Metadata).(ard.StringMap)
edge.Properties = ard.SimpleCopy(self.Properties).(ard.StringMap)
}
return &edge, nil
}

//
// Edges
//

// Warning: Adding public methods will break it in JavaScript
type Edges []*Edge

func (self Edges) Remove(edge *Edge) Edges {
func (self Edges) copy(agnostic bool) (Edges, error) {
edges := make(Edges, len(self))
var err error
for index, edge := range self {
if edges[index], err = edge.copy(agnostic); err != nil {
return nil, err
}
}
return edges, nil
}

func (self Edges) remove(edge *Edge) Edges {
for index, e := range self {
if e == edge {
return append(self[:index], self[index+1:]...)
Expand Down
4 changes: 2 additions & 2 deletions clout/js/coerce.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import (
cloutpkg "github.com/tliron/puccini/clout"
)

func Coerce(clout *cloutpkg.Clout, problems *problemspkg.Problems, urlContext *urlpkg.Context, history bool, format string, strict bool, allowTimestamps bool, pretty bool) {
func Coerce(clout *cloutpkg.Clout, problems *problemspkg.Problems, urlContext *urlpkg.Context, history bool, format string, strict bool, pretty bool) {
var arguments map[string]string
if !history {
arguments = make(map[string]string)
arguments["history"] = "false"
}
context := NewContext("tosca.coerce", log, arguments, true, format, strict, allowTimestamps, pretty, "", urlContext)
context := NewContext("tosca.coerce", log, arguments, true, format, strict, pretty, "", urlContext)
if _, err := context.Require(clout, "tosca.coerce", map[string]any{"problems": problems}); err != nil {
problems.ReportError(err)
}
Expand Down
Loading

0 comments on commit ac66999

Please sign in to comment.