cloudsoft.io

The First Five Minutes: Run Stuff

A Compute Node

When working with VMs and TOSCA, the starting point will often be the tosca.nodes.Compute node type. This requests that a new “compute node” (for instance a VM) be provisioned. You can deploy the following TOSCA “topology template” defining a compute node:

tosca_definitions_version: tosca_simple_yaml_1_3

metadata:
  template_name: compute-example
  template_version: 1.0.0-SNAPSHOT

topology_template:
  node_templates:
    a_server:
      type: tosca.nodes.Compute

Try it, in the GUI, or copying to compute-example.yaml and running br deploy compute-example.yaml .

A Software Component Node

The next step is either to use a specific image and/or to have some software installed. If you’re interested in the former, or more details on configuring compute nodes, see Common Types. In this section, we’ll cover the latter.

First, we need to say which compute node our software is going to be hosted on. We will use a tosca.nodes.SoftwareComponent – which under the covers, has a TOSCA “requirement” called host that must point at a tosca.nodes.Compute node. In practice, this is straightforward to fulfill, we simply reference the compute node, called a_server here, in the host requirement:

tosca_definitions_version: tosca_simple_yaml_1_3

metadata:
  template_name: tosca-demo-0
  template_version: 1.0.0-SNAPSHOT

topology_template:
  node_templates:
    a_server:
      type: tosca.nodes.Compute
    a_software:
      type: tosca.nodes.SoftwareComponent
      requirements:
      - host: a_server

Again the above can be deployed in the GUI or with br deploy.

A Software Component with custom behaviour

We will invoke some bash scripts as part of the deployment.

Create directories 1-hello, 1-hello/scripts and bash scripts 1-hello/scripts/configure.sh, 1-hello/scripts/start.sh, 1-hello/scripts/stop.sh with content like the following:

echo configuring/starting/stopping my software # choose one operation per script

Create 1-hello/topology.yaml with the following content:

tosca_definitions_version: tosca_simple_yaml_1_3

metadata:
  template_name: tosca-demo-1
  template_version: 1.0.0-SNAPSHOT

topology_template:
  node_templates:
    a_server:
      type: tosca.nodes.Compute
      interfaces:
        Standard:
          operations:
            configure: classpath://scripts/configure.sh
    a_software:
      type: tosca.nodes.SoftwareComponent
      requirements:
      - host: a_server
      interfaces:
        Standard:
          operations:
            create: classpath://scripts/configure.sh
            start: classpath://scripts/start.sh
            stop: classpath://scripts/stop.sh

Create directory 1-hello/run and runnable tosca blueprint 1-hello/run/run.yaml with the following content:

tosca_definitions_version: tosca_simple_yaml_1_3

topology_template:
  node_templates:
    app:
      type: tosca-demo-1

Add new type and its dependencies into AMP catalog, by running the following command:

br catalog add 1-hello

And then deploy new application, by running:

br deploy 1-hello/run/run.yaml

Behaviour that we added by customizing the operations for the Standard interface, become part of application lifecycle, and can be called manually in AMP Effectors, when needed.

More behaviour, input data or configurations, can be added to an operation using artifacts as shown in the example below:

tosca_definitions_version: tosca_simple_yaml_1_3

metadata:
  template_name: tosca-demo-1
  template_version: 1.0.0-SNAPSHOT

topology_template:
  node_templates:
    a_server:
      type: tosca.nodes.Compute
    a_software:
      type: tosca.nodes.SoftwareComponent
      requirements:
      - host: a_server
      interfaces:
        Standard:
          operations:
            configure:
              implementation:
                primary: classpath://scripts/configure.sh
                dependencies:
                - config_file
      artifacts:
        config_file:
          file: classpath://scripts/hello.sh

Create the 1-hello/scripts/hello.sh with a simple command such as echo hello, respectively.

Inputs and Properties

Inputs are set on topology_template to allow custom values to be injected as arguments for node and relationship templates. The input values can be injected into properties or attributes, as shown in the following example:

tosca_definitions_version: tosca_simple_yaml_1_3
metadata:
  template_name: testTemplateWithInputs

topology_template:
  inputs:
    input_num_cpus:
      type: integer
      description: Number of CPUs for the server.
      default: 3
      
  node_templates:
    a-node:
      type: tosca.nodes.Compute
      properties:
        custom_cpus_no: {get_input: input_num_cpus}
      attributes:
        num_cpus: {get_input: input_num_cpus}

Properties are declared on node types. they can have default values or their values can be injected with inout values, as shown in the following example:

tosca_definitions_version: tosca_simple_yaml_1_3
metadata:
  template_name: testTemplateWithCustomPropertiesAndInputs

node_types:
  tosca-custom-node:
    derived_from: tosca.nodes.Compute
    properties:
      custom_cpus_no:
        type: integer
        required: true
      app.port:
        type: string
        required: false
        default: 8080

topology_template:
  inputs:
    input_num_cpus:
      type: integer
      description: Number of CPUs for the server.
      default: 3
      
  node_templates:
    a-node:
      type: tosca-custom-node
      properties:
        custom_cpus_no: {get_input: input_num_cpus}
        app.port: 9090

Dependency Injection: A Three Tier Co-Lo Deployment

There are two ways to connect nodes in the same application: through relationships and through attributes. Using relationships results in rigid node connection. The dependency between two nodes is declared in the node type and deploying a node template of one type without the other is not allowed. The following example shows the declaration of two node types and the relationship type used to link them. An Apache Tomcat node is declared requiring a Database node, using a relationship type named io.cloudsoft.depends-on-db.

tosca_definitions_version: tosca_simple_yaml_1_3

metadata:
  template_name: io.cloudsoft.tosca-node-types
  template_version: 0.1.0-SNAPSHOT

relationship_types:
  io.cloudsoft.depends-on-db:
    derived_from: tosca.relationships.DependsOn
    interfaces:
      Configure:
        pre_configure_source:
          inputs:
            DB_URL: { get_attribute: [ TARGET, db_url ] }
            DB_DRIVER: { get_attribute: [ TARGET, db_driver ] }
          implementation: classpath://tomcat-scripts/tomcat-configure_db.sh

node_types:
  tosca-tomcat8-node:
    derived_from: tosca.nodes.WebServer
    # configuration elements omitted for brevity
    requirements:
    - needs-db:
        relationship: io.cloudsoft.depends-on-db
        node: tosca.nodes.Database

  tosca-postgresql12-node:
    derived_from: tosca.nodes.Database
    # configuration elements omitted for brevity
    attributes:
      db_url: { concat: [ "postgresql://", get_attribute: [ HOST, public_address ] , ":", get_property: [ SELF, app.port ] ] }
      db_driver:
        type: string
        description: Driver to use to connect the DB
        default: "org.postgresql.Driver"

Creating a topology template using these three types is quite straight forward.

tosca_definitions_version: tosca_simple_yaml_1_3

metadata:
  template_name: io.cloudsoft.family-chat-postgresql
  template_version: 0.1.0-SNAPSHOT 

# some configurations were omitted for brevity
topology_template:

  node_templates:
    tomcat-host:
      type: tosca.nodes.Compute
    tomcat-instance:
      type: tosca-tomcat8-node
      requirements:
      - host: tomcat-host
      - needs-db: db-instance

    db-host:
      type: tosca.nodes.Compute
    db-instance:
      type: tosca-postgresql12-node
      requirements:
      - host: db-host

The other way to declare a dependency is by using attributes. Since a relationship is not enforced, those two node types can be used for node templates deployed independently. This promotes reusability, since the node templates can be used separately in different topology templates. This is especially useful when clusters of nodes are required. The following example shows an Apache Tomcat node type that does not declare a requirement (dependency) on a database node through a relationship, but through two properties that will be set with values when the database node is deployed. The values of those properties are then used as inputs for the interface configure operation. The database node require no changes.

tosca_definitions_version: tosca_simple_yaml_1_3

metadata:
  template_name: io.cloudsoft.tosca-node-types
  template_version: 0.1.0-SNAPSHOT

node_types:
  smart-tomcat8-node:
    derived_from: tosca-tomcat8-node
    # configuration elements omitted for brevity
    properties:
      db_url:
        type: string
        required: false
        description: URL of the database.
      db_driver:
        type: string
        required: false
        description: Driver of the database.
    interfaces:
      Standard:
        configure:
          inputs:
            DB_URL: { get_property: [ SELF, db_url ] }
            DB_DRIVER: { get_property: [ SELF, db_driver ] }
          implementation:
            primary: classpath://tomcat-scripts/tomcat-inject_db.sh

The following example shows how to use this node type in a simple topology template declaring a tomcat and a database node template.

tosca_definitions_version: tosca_simple_yaml_1_3

metadata:
  template_name: io.cloudsoft.family-chat-postgresql
  template_version: 0.1.0-SNAPSHOT 

# some configurations were omitted for brevity
topology_template:

  node_templates:
    tomcat-host:
      type: tosca.nodes.Compute
    tomcat-instance:
      type: smart-tomcat8-node
      properties:
        db_url:  { get_attribute: [ db-instance, db_url ] }
        db_driver: { get_attribute: [ db-instance, db_driver ] }
      requirements:
      - host: tomcat-host

    db-host:
      type: tosca.nodes.Compute
    db-instance:
      type: tosca-postgresql12-node
      requirements:
      - host: db-host

And since clusters where mentioned, the following example adds in a topology template encapsulating a tomcat host and node, that can be used as a cluster member.

tosca_definitions_version: tosca_simple_yaml_1_3

metadata:
  template_author: Cloudsoft
  template_name: io.cloudsoft.smart-tomcat9-node-member
  template_version: 0.1.0-SNAPSHOT
  
# some configurations were omitted for brevity 
topology_template:
  inputs:
    tomcat_db_url:
      default: null
    tomcat_db_driver:
      default: null
  node_templates:
    tomcat-host:
      type: tosca.nodes.Compute
      
    tomcat-instance:
      type: smart-tomcat9-node
      properties:
        db_url: { get_input: tomcat_db_url }
        db_driver: { get_input: tomcat_db_driver }
      requirements:
        - host: tomcat-host

The following example shows how to use this node type in a topology template declaring a cluster of tomcat nodes and a database node template.

tosca_definitions_version: tosca_simple_yaml_1_3

metadata:
  template_name: io.cloudsoft.cluster-with-postgresql
  template_version: 0.1.0-SNAPSHOT
  
# some configurations were omitted for brevity
topology_template:
  node_templates:
    db-host:
      type: tosca.nodes.Compute
    db-instance:
      type: tosca-postgresql12-node
      requirements:
      - host: db-host

    tomcat-cluster:
      type: tosca.entity.DynamicCluster
      properties:
        cluster.initial.size: 2
        dynamiccluster.memberspec:
          $brooklyn:entitySpec:  
            name: Apache Tomcat Member
            type: io.cloudsoft.smart-tomcat9-node-member
            brooklyn.config:
              tomcat_db_url: { get_attribute: [ db-instance, db_instance_url ] }
              tomcat_db_driver: { get_attribute: [ db-instance, db_instance_driver ] }