Skip to content

Commit

Permalink
managed node template
Browse files Browse the repository at this point in the history
  • Loading branch information
milesstoetzner committed Nov 9, 2024
1 parent 0400cae commit c87449a
Show file tree
Hide file tree
Showing 27 changed files with 82 additions and 111 deletions.
11 changes: 1 addition & 10 deletions docs/docs/variability4tosca/specification/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,14 +259,6 @@ The following options are used to configure the solver.
| optimization_technologies_unique | false | Boolean | false | Enable check for unique results considering technologies. |
| optimization_technologies_mode | false | count | weight | weight-count | count | Configure optimization mode considering technologies. |

### Normalization Options

The following options are used to configure the normalizer.

| Keyname | Mandatory | Type | Default | Description |
|----------------------|-----------|-------------------------------|---------|-----------------------------------------------------------|
| technology_required | false | Boolean | false | Enable if a technology is required by default for a node. |


### Enricher Options

Expand Down Expand Up @@ -329,7 +321,6 @@ optimization_technologies_mode: weight-count
technology_constraint: true
hosting_stack_constraint: true
relation_default_implied: true
technology_required: false
unconsumed_input_check: false
unproduced_output_check: false
enrich_technologies: true
Expand Down Expand Up @@ -357,7 +348,6 @@ unique_input_constraint: true
unique_output_constraint: true
required_artifact_constraint: true
relation_default_implied: true
technology_required: true
checks: false
enrich_technologies: true
enrich_implementations: true
Expand Down Expand Up @@ -561,6 +551,7 @@ A node template can also hold conditional types, artifact, and properties.
| weight | false | Boolean | Non-Negative Number | Configure the weight of this element used during optimization (default is 1). |
| implies | false | List(Tuple(Target: VariabilityCondition, Condition?: VariabilityCondition)) | An optional list of implications following the pattern `element implies target` or `(element and condition) implies target`. |
| technology | false | String | List(Map(String, TechnologyTemplate){single}) | An optional conditional assignment of deployment technologies. |
| managed | false | Boolean | Enable if node is managed (default is true). |

For example, the following node template has a variability condition assigned.

Expand Down
2 changes: 1 addition & 1 deletion src/controller/utils/scenarios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export default async function (options: ScenariosOptions) {
rules = rules.filter(rule => {
assert.isDefined(rule.hosting)

if (check.isTrue(options.hosting)) return !utils.isEmpty(rule.hosting)
if (check.isTrue(options.hosting)) return utils.isPopulated(rule.hosting)
if (check.isFalse(options.hosting)) return utils.isEmpty(rule.hosting)

assert.isArray(options.hosting)
Expand Down
2 changes: 1 addition & 1 deletion src/enricher/conditions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export class ConditionEnricher {
return false
})

if (!utils.isEmpty(selected)) {
if (utils.isPopulated(selected)) {
conditions.unshift(
generatify(
simplify(
Expand Down
3 changes: 2 additions & 1 deletion src/enricher/constraints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,9 @@ export class ConstraintEnricher {
/**
* Ensure that technology exists (required and single)
*/
// .filter(it => utils.isPopulated(it.technologies)
if (this.graph.options.constraints.technology) {
for (const node of this.graph.nodes.filter(it => !utils.isEmpty(it.technologies))) {
for (const node of this.graph.nodes.filter(it => it.managed)) {
const consequence =
node.technologies.length === 1 ? node.technologies[0].id : {exo: node.technologies.map(it => it.id)}
this.graph.addConstraint({implies: [node.id, consequence]})
Expand Down
66 changes: 31 additions & 35 deletions src/enricher/elements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,55 +41,51 @@ export class ElementEnricher {
// for backwards compatibility and testing purposed, continue if, e.g., no rules at all exists
if (utils.isEmpty(this.graph.plugins.technology)) return

for (const node of this.graph.nodes) {
if (!utils.isEmpty(node.technologies)) {
// Do not override manual assigned technologies but enrich them with an implementation
const enriched: TechnologyTemplateMap[] = []
for (const node of this.graph.nodes.filter(it => it.managed).filter(it => utils.isPopulated(it.technologies))) {
// Do not override manual assigned technologies but enrich them with an implementation
const enriched: TechnologyTemplateMap[] = []

node.technologies.forEach(technology => {
// TODO: super inefficient but we need copies for each since the same technology might be defined multiple times
const candidates = this.getTechnologyCandidates(node)
const selected = candidates.filter(map => utils.firstKey(map) === technology.name)
node.technologies.forEach(technology => {
// TODO: super inefficient but we need copies for each since the same technology might be defined multiple times
const candidates = this.getTechnologyCandidates(node)
const selected = candidates.filter(map => utils.firstKey(map) === technology.name)

for (const map of selected) {
const template = utils.firstValue(map)
if (!utils.isEmpty(technology.conditions)) {
// TODO: improve plugin typing!
// assert.isArray(template.conditions)
assert.isDefined(template.conditions)
template.conditions = [
...technology.conditions,
...(check.isArray(template.conditions) ? template.conditions : [template.conditions]),
]
}
for (const map of selected) {
const template = utils.firstValue(map)
if (utils.isPopulated(technology.conditions)) {
// TODO: improve plugin typing!
// assert.isArray(template.conditions)
assert.isDefined(template.conditions)
template.conditions = [
...technology.conditions,
...(check.isArray(template.conditions) ? template.conditions : [template.conditions]),
]
}
}

if (utils.isEmpty(selected))
throw new Error(`${node.Display} has no implementation for ${technology.display}`)
if (utils.isEmpty(selected))
throw new Error(`${node.Display} has no implementation for ${technology.display}`)

enriched.push(...selected)
})
this.graph.replaceTechnologies(node, enriched)
}
enriched.push(...selected)
})
this.graph.replaceTechnologies(node, enriched)
}
}

private enrichTechnologies() {
for (const node of this.graph.nodes) {
for (const node of this.graph.nodes.filter(it => it.managed).filter(it => utils.isEmpty(it.technologies))) {
// Get all possible technology assignments
const candidates = this.getTechnologyCandidates(node)

if (utils.isEmpty(node.technologies)) {
// Throw if technology is required but no candidate has been found
if (this.graph.options.checks.requiredTechnology) {
if (node.technologyRequired && utils.isEmpty(candidates)) {
throw new Error(`${node.Display} has no technology candidates`)
}
// Throw if technology is required but no candidate has been found
if (this.graph.options.checks.requiredTechnology) {
if (utils.isEmpty(candidates)) {
throw new Error(`${node.Display} has no technology candidates`)
}

// Assign each possible technology assignment
candidates.forEach(it => this.graph.addTechnology(node, it))
}

// Assign each possible technology assignment
candidates.forEach(it => this.graph.addTechnology(node, it))
}
}
}
4 changes: 2 additions & 2 deletions src/graph/artifact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,11 @@ export default class Artifact extends Element {

const wrappers: ConditionsWrapper[] = []

if (!utils.isEmpty(consistencies)) {
if (utils.isPopulated(consistencies)) {
wrappers.push({conditions: andify(consistencies), consistency: true, semantic: false})
}

if (!utils.isEmpty(semantics)) {
if (utils.isPopulated(semantics)) {
wrappers.push({conditions: andify(semantics), consistency: false, semantic: true})
}

Expand Down
29 changes: 21 additions & 8 deletions src/graph/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,27 @@ export default class Node extends Element {
return this.name
}

// TODO: NEXT: adapt this to external component
/**
* Technology is required if type is not abstract
* Checks if component is managed, i.e., requires a technology.
*/
get technologyRequired() {
// TODO: write input validations in populator that ensures that raw.manged does not conflict eg with modeled technologies?
get managed() {
// Component is (un)managed if manually specified
if (check.isDefined(this.raw.managed)) {
assert.isBoolean(this.raw.managed)
return this.raw.managed
}

// Component is managed, if technologies are assigned (e.g., manually)
if (utils.isPopulated(this.technologies)) return true

// Component is unmanaged if type is abstract
const type = this.graph.inheritance.getNodeType(this.getType().name)
assert.isDefined(type, `Node type "${this.getType().name}" does not exist`)
return !isAbstract(type)
if (isAbstract(type)) return false

// Component is managed by default
return true
}

get persistent() {
Expand Down Expand Up @@ -105,19 +118,19 @@ export default class Node extends Element {
}

get hasHost() {
return !utils.isEmpty(this.hosts)
return utils.isPopulated(this.hosts)
}

get isTarget() {
return !utils.isEmpty(this.ingoing)
return utils.isPopulated(this.ingoing)
}

get isSource() {
return !utils.isEmpty(this.outgoing)
return utils.isPopulated(this.outgoing)
}

get hasArtifact() {
return !utils.isEmpty(this.artifacts)
return utils.isPopulated(this.artifacts)
}

getTypeSilent() {
Expand Down
31 changes: 2 additions & 29 deletions src/graph/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -650,19 +650,8 @@ class ChecksOptions extends BaseOptions {
this.ambiguousTechnology = this.raw.ambiguous_technology_check ?? this.consistency
assert.isBoolean(this.ambiguousTechnology)

if (this.v1 || this.v2) {
/**
* Case: tosca_simple_yaml_1_3, tosca_variability_1_0, tosca_variability_1_0_rc_1, tosca_variability_1_0_rc_2
*/
this.requiredTechnology = this.raw.required_technology_check ?? this.semantic
assert.isBoolean(this.expectedTechnology)
} else {
/**
* Case: tosca_variability_1_0_rc_3
*/
this.requiredTechnology = this.raw.required_technology_check ?? true
assert.isBoolean(this.expectedTechnology)
}
this.requiredTechnology = this.raw.required_technology_check ?? this.semantic
assert.isBoolean(this.expectedTechnology)

this.ambiguousRelation = this.raw.ambiguous_relation_check ?? this.consistency
assert.isBoolean(this.ambiguousRelation)
Expand Down Expand Up @@ -940,24 +929,8 @@ class ConstraintsOptions extends BaseOptions {
}

export class NormalizationOptions extends BaseOptions {
readonly technologyRequired: boolean

constructor(serviceTemplate: ServiceTemplate) {
super(serviceTemplate)

if (this.v1 || this.v2) {
/**
* Case: tosca_simple_yaml_1_3, tosca_variability_1_0, tosca_variability_1_0_rc_1, tosca_variability_1_0_rc_2
*/
this.technologyRequired = this.raw.technology_required ?? false
assert.isBoolean(this.technologyRequired)
} else {
/**
* Case: tosca_variability_1_0_rc_3
*/
this.technologyRequired = this.raw.technology_required ?? true
assert.isBoolean(this.technologyRequired)
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/graph/populator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -574,14 +574,14 @@ export class Populator {

// GUESS: relation is referenced
const relations = node.outgoing.filter(it => it.name === parsed.propertyContainer).map(it => it.target)
if (!utils.isEmpty(relations)) {
if (utils.isPopulated(relations)) {
property.consuming.push(...relations)
continue
}

// GUESS: artifact is referenced
const artifacts = node.artifactsMap.get(parsed.propertyContainer) ?? []
if (!utils.isEmpty(artifacts)) {
if (utils.isPopulated(artifacts)) {
property.consuming.push(...artifacts)
continue
}
Expand Down
1 change: 0 additions & 1 deletion src/normalizer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,6 @@ export default class Normalizer {
assert.isString(rule.component)

if (check.isUndefined(rule.hosting)) rule.hosting = []
// TODO: dont allow short form
assert.isArray(rule.hosting)
rule.hosting.forEach(it => assert.isString(it))

Expand Down
1 change: 1 addition & 0 deletions src/resolver/transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export default class Transformer {
delete raw.weight
delete raw.implies
delete raw.technology
delete raw.managed
}

private transformNodes() {
Expand Down
1 change: 1 addition & 0 deletions src/specification/node-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export type NodeTemplate = {
weight?: number | boolean
persistent?: boolean
technology?: TechnologyAssignment
managed?: boolean
} & VariabilityAlternative & {
default_condition_mode?: NodeDefaultConditionMode
}
Expand Down
2 changes: 1 addition & 1 deletion src/technologies/plugins/rules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export class TechnologyRulePlugin implements TechnologyPlugin {
assert.isDefined(rule.artifact)
return it.getType().isA(rule.artifact)
})
const hasArtifactInTemplate = !utils.isEmpty(artifactsByTemplate)
const hasArtifactInTemplate = utils.isPopulated(artifactsByTemplate)

// Check for artifact in type
const hasArtifactInType = this.graph.inheritance.hasArtifactDefinition(
Expand Down
2 changes: 1 addition & 1 deletion src/technologies/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export function constructRuleName(data: TechnologyRule, options: {technology?: b
output += '::' + data.technology
}

if (check.isDefined(data.hosting) && !utils.isEmpty(data.hosting)) {
if (check.isDefined(data.hosting) && utils.isPopulated(data.hosting)) {
output += '@' + data.hosting.join('->')
}

Expand Down
4 changes: 4 additions & 0 deletions src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ export function isEmpty(obj: any) {
throw new Error(`Cannot check if object ${pretty(obj)} is empty`)
}

export function isPopulated(obj: any) {
return !isEmpty(obj)
}

export function first<T>(array: T[]) {
return array[0]
}
Expand Down
2 changes: 1 addition & 1 deletion tasks/docs/generate/variability/test.template.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The variability of the following variable service template shall be resolved.
{% endraw %}
```

<% if (!utils.isEmpty(test.inputs)) { -%>
<% if (utils.isPopulated(test.inputs)) { -%>
## Variability Inputs
When resolving variability, the following variability inputs shall be used.
Expand Down
1 change: 1 addition & 0 deletions tests/conformance/implied-default/fixed-left/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ topology_template:
expected_incoming_relation_check: false
required_incoming_relation_constraint: false
enrich_technologies: false
technology_constraint: false

node_templates:
application:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ topology_template:
expected_incoming_relation_check: false
required_incoming_relation_constraint: false
enrich_technologies: false
technology_constraint: false

node_templates:
application:
Expand Down
1 change: 1 addition & 0 deletions tests/conformance/implied-host/broken/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ topology_template:

options:
enrich_technologies: false
technology_constraint: false

node_templates:
application:
Expand Down
1 change: 1 addition & 0 deletions tests/conformance/implied-host/fixed-left/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ topology_template:

options:
enrich_technologies: false
technology_constraint: false

node_templates:
application:
Expand Down
1 change: 1 addition & 0 deletions tests/conformance/implied-host/manual-left/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ topology_template:

options:
enrich_technologies: false
technology_constraint: false

node_templates:
application:
Expand Down
Loading

0 comments on commit c87449a

Please sign in to comment.