Skip to content

Commit

Permalink
tests: add tests for new artifact and dsl features
Browse files Browse the repository at this point in the history
  • Loading branch information
aszs committed Jan 16, 2025
1 parent 24f44d2 commit 2558d39
Show file tree
Hide file tree
Showing 13 changed files with 457 additions and 70 deletions.
22 changes: 22 additions & 0 deletions docs/examples/artifact2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import unfurl
import tosca
from tosca import ToscaOutputs, Attribute, Eval
class MyArtifact(unfurl.artifacts.ShellExecutable):
file: str = "myscript.sh"
contrived_key: str
outputsTemplate = Eval("{{ stdout | from_json | subelements(SELF.contrived_key)}}")

class Outputs(ToscaOutputs):
a_output: str = Attribute()

def execute(self, arg1: str, arg2: int) -> Outputs:
return MyArtifact.Outputs()

class MyNode(tosca.nodes.Root):
prop1: str
prop2: int

def configure(self) -> MyArtifact.Outputs:
return MyArtifact(contrived_key=self.prop1).execute("hello", arg2=self.prop2)

my_node = MyNode(prop1="foo", prop2=1)
67 changes: 67 additions & 0 deletions docs/examples/artifact2.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
artifact_types:
MyArtifact:
derived_from: unfurl.artifacts.ShellExecutable
properties:
file:
type: string
default: myscript.sh
contrived_key:
type: string
outputsTemplate:
type: string
default: '{{ stdout | from_json | subelements(SELF.contrived_key)}}'
interfaces:
Executable:
type: unfurl.interfaces.Executable
operations:
execute:
metadata:
output_key:
- Outputs
inputs:
arg1:
type: string
arg2:
type: integer
outputs:
a_output:
type: string
node_types:
MyNode:
derived_from: tosca.nodes.Root
properties:
prop1:
type: string
prop2:
type: integer
interfaces:
Standard:
operations:
configure:
metadata:
output_key:
- Outputs
arguments:
- arg1
- arg2
inputs:
arg1: hello
arg2:
eval: .::prop2
outputs:
a_output:
type: string
implementation:
primary:
type: MyArtifact
properties:
contrived_key:
eval: .parent::.::prop1
file: myscript.sh
topology_template:
node_templates:
my_node:
type: MyNode
properties:
prop1: foo
prop2: 1
50 changes: 50 additions & 0 deletions tests/examples/artifact1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Generated by tosca.yaml2python from docs/examples/artifact1.yaml at 2025-01-14T17:23:28 overwrite not modified (change to "overwrite ok" to allow)

import unfurl
from typing import List, Dict, Any, Tuple, Union, Sequence
import tosca
from tosca import ArtifactEntity, Eval, Node, operation



@operation(name="execute")
def _terraform_execute(self, bar=1):
...
# terraform_execute = operation(name="execute")

def _make_terraform() -> unfurl.artifacts.TerraformModule:
terraform = unfurl.artifacts.TerraformModule(
"terraform",
resultTemplate=Eval(
{
"attributes": {
"output_attribute": "{{ '.name' | eval }} node "
"{{arguments.foo}}:{{arguments.bar}}"
}
}
),
file="missing.tf",
contents='resource "null_resource" "null" {\n}\n',
)
# manual:
terraform.set_operation(_terraform_execute)
terraform._Executable_default_inputs = dict(foo="hello") # type: ignore[attr-defined]
return terraform


configurator_artifacts: Node = unfurl.nodes.LocalRepository(
"configurator-artifacts",
)
configurator_artifacts.terraform = _make_terraform() # type: ignore[attr-defined]


@operation(name="configure")
def test_configure(self, **kw):
return self.find_artifact("terraform").execute()


test: Node = tosca.nodes.Root(
"test",
)
test.set_operation(test_configure, "configure")

41 changes: 41 additions & 0 deletions tests/examples/artifact1.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
kind: Ensemble
apiVersion: unfurl/v1alpha1
spec:
service_template:
tosca_definitions_version: tosca_simple_unfurl_1_0_0
topology_template:
node_templates:
configurator-artifacts:
type: unfurl.nodes.LocalRepository
artifacts:
terraform:
type: unfurl.artifacts.TerraformModule
properties:
resultTemplate:
attributes:
output_attribute: "{{ '.name' | eval }} node {{arguments.foo}}:{{arguments.bar}}"
interfaces:
Executable:
type: unfurl.interfaces.Executable
inputs:
foo: hello
operations:
execute:
inputs:
bar:
type: integer
default: 1
file: missing.tf
contents: "resource \"null_resource\" \"null\" {\n}\n"
metadata:
module: service_template
test:
type: tosca.nodes.Root
interfaces:
Standard:
operations:
configure:
implementation:
primary: terraform
metadata:
module: service_template
75 changes: 75 additions & 0 deletions tests/examples/dsl_artifacts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from abc import abstractmethod
from typing import Dict
import unfurl
import tosca
from tosca import ToscaInputs, ToscaOutputs, Attribute, placeholder

class KubernetesClusterInputs(ToscaInputs):
do_region: str = "nyc3"
doks_k8s_version: str ="1.30"

class KubernetesClusterOutputs(ToscaOutputs):
do_id: str = Attribute() # needed so can be KubernetesClusterOutputs inherited

class ClusterOp(unfurl.artifacts.Executable):
file: str = "kubernetes"

# inputs defined here are set as the inputs for operations that set this artifact as their implementation
# args, retval set input, output definitions
def execute(self, inputs: KubernetesClusterInputs) -> KubernetesClusterOutputs:
# If an artifact of this type is use an operation's implementation
# the inputs defined here will be set as the inputs definition for the operation
return placeholder(KubernetesClusterOutputs)

class MoreInputs(ToscaInputs):
nodes: int = 4


class CustomClusterOp(ClusterOp):
# customizing an artifact should make sure execute is compatible with base artifact's artifact
# this means you can't change existing parameters, only add new ones with default values

# only add new inputs definitions to this types interface, since the base types inputs will be merged with these
def execute(self, inputs: KubernetesClusterInputs, more_inputs: MoreInputs = MoreInputs()) -> KubernetesClusterOutputs:
self.set_inputs(inputs, more_inputs)
return placeholder(KubernetesClusterOutputs)

class ClusterTerraform(unfurl.artifacts.TerraformModule, ClusterOp):
# need to merge properties with inputs for configurator
file: str = "kubernetes"

# def execute(self, inputs: KubernetesClusterInputs) -> KubernetesClusterOutputs:
# ToscaInputs._get_inputs(self) # type: ignore
# return None # type: ignore
# # print("Cluster.execute!!", inputs)
# # return configurator(self.className)(self).execute(inputs)

class DOCluster(tosca.nodes.Root, KubernetesClusterInputs, KubernetesClusterOutputs):
clusterconfig: "ClusterOp" = ClusterTerraform()

my_property: str = "default"

def configure(self, **kw) -> KubernetesClusterOutputs:
return self.clusterconfig.execute(self)


class ExtraClusterOp(ClusterOp):
# properties are merged with configurator inputs at runtime
extra: str = tosca.Property(options=tosca.InputOption)

def execute(self, inputs: KubernetesClusterInputs, extra: str = tosca.CONSTRAINED) -> KubernetesClusterOutputs:
# self.set_inputs(inputs, extra=self.extra)
return KubernetesClusterOutputs()

class CustomClusterTerraform(unfurl.artifacts.TerraformModule, ExtraClusterOp):
file: str = "my_custom_kubernetes_tf_module"

mycluster = DOCluster(clusterconfig=CustomClusterTerraform(extra="extra",
contents = """resource "null_resource" "null" {}
output "do_id" {
value = "ABC"
}
"""))



2 changes: 1 addition & 1 deletion tests/examples/dsl_configurator.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def create(self, **kw: Any) -> Callable[[Any], Any]:

@operation(outputs=dict(test_output="computed"))
def delete(self, **kw: Any) -> TemplateConfigurator:
render = self._context # type: ignore
render = self._context # type: ignore # raise error to force render
done = DoneDict(outputs=dict(test_output=Eval("{{'set output'}}")))
return TemplateConfigurator(TemplateInputs(run="test me", done=done))

Expand Down
2 changes: 1 addition & 1 deletion tests/examples/dsl_relationships.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class Volume(tosca.nodes.Root):
disk_label: str
disk_size: tosca.Size = 100 * tosca.GB

class VolumeAttachment(tosca.relationships.AttachesTo):
class VolumeAttachment(tosca.relationships.AttachesTo): # type: ignore[override]
_target: Volume

class VolumeMountArtifact(tosca.artifacts.Root):
Expand Down
Loading

0 comments on commit 2558d39

Please sign in to comment.