Table Of Contents

TOSCA

The service templates are defined using OASIS’s TOSCA (“Topology and Orchestration Specification for Cloud Applications”) Simple Profile in YAML Version 1.3 standard (html) (pdf) with the follow differences:

Extensions

  • add ‘any’ schema type for properties and attributes definitions

  • ‘additionalProperties’ field in type metadata

  • allow metadata field on inputs, outputs, artifacts, and repositories

  • add “sensitive” property and datatype metadata field

  • add “immutable” property metadata field

  • add “environment” keyword to implementation definition

  • add “eval” function

  • add “type” in capability assignment

  • allow workflow to be imported

  • workflow “target” accepts type names

  • groups can have other groups as members

  • An operation’s operation_host field can also be set to a node template’s name.

  • added OPERATION_HOST as a reserved function keyword.

  • add “discover” and “default” directives

  • add “default_for” keyword to relationship templates

  • add “defaults” section to interface definitions

Not yet implemented and non-conformance with the TOSCA 1.3 specification

  • The get_operation_output function (use resultTemplate instead)

  • “copy” keyword (use the dsl section or merge directives instead)

  • get_artifact function (only implemented for artifacts that are container images)

  • CSAR manifests and archives (implemented but untested)

  • substitution mapping

  • triggers

  • notifications

  • node_filters

  • xml schema constraints

Extensions to built-in definitions

# Copyright (c) 2020 Adam Souzis
# SPDX-License-Identifier: MIT
tosca_definitions_version: tosca_simple_unfurl_1_0_0 # unfurl/v1alpha1.ServiceTemplate
metadata:
  template_name: Unfurl types
  template_author: onecommons.org
  template_version: 1.0.0
node_types:
  tosca.nodes.Root:
    interfaces:
      Install: # all nodes can implement this interface
        type: unfurl.interfaces.Install

  unfurl.nodes.Installer:
    derived_from: tosca.nodes.Root
    capabilities:
      installer:
        type: unfurl.capabilities.Installer

  unfurl.nodes.Installation:
    derived_from: tosca.nodes.Root
    requirements:
      - installer:
          capability: unfurl.capabilities.Installer
          node: unfurl.nodes.Installer
          relationship: unfurl.relationships.InstalledBy
          occurrences: [0, 1] # it isn't necessarily required

  unfurl.nodes.Default:
    derived_from: unfurl.nodes.Installation
    description: "Used if pre-existing instances are declared with no TOSCA template"

  unfurl.nodes.Installer.Terraform:
    derived_from: unfurl.nodes.Installer
    properties:
      dir:
        type: string
        default:
          eval:
            get_dir: spec.home
    interfaces:
      defaults:
        implementation:
          className: unfurl.configurators.terraform.TerraformConfigurator
        inputs:
          dir: { get_property: [SELF, dir] }
      Standard:
        operations:
          delete:
          create:
      Install:
        operations:
          check:

  unfurl.nodes.K8sCluster:
    derived_from: tosca.nodes.Root
    capabilities:
      host:
        type: tosca.capabilities.Container
        valid_source_types:
          [unfurl.nodes.K8sRawResource, unfurl.nodes.K8sNamespace]
      endpoint:
        type: unfurl.capabilities.Endpoint.K8sCluster
    attributes:
      apiServer:
        description: URL to the cluster's api server
        type: string
        metadata:
          # mark this is as immutable to make sure the connection is pointing at the same cluster in subsequent runs
          immutable: true
    interfaces:
      Install:
        operations:
          check: unfurl.configurators.k8s.ClusterConfigurator
          discover: unfurl.configurators.k8s.ClusterConfigurator

  unfurl.nodes.K8sRawResource:
    derived_from: tosca.nodes.Root
    requirements:
      - host:
          node: unfurl.nodes.K8sCluster
          relationship: tosca.relationships.HostedOn
    properties:
      definition:
        type: any
        required: false
    attributes:
      apiResource:
        type: map
        required: false
      name:
        type: string
        default:
          eval: .name
    interfaces:
      Standard:
        configure: unfurl.configurators.k8s.ResourceConfigurator
        delete: unfurl.configurators.k8s.ResourceConfigurator
      Install:
        operations:
          check: unfurl.configurators.k8s.ResourceConfigurator
          discover: unfurl.configurators.k8s.ResourceConfigurator

  unfurl.nodes.K8sNamespace:
    derived_from: unfurl.nodes.K8sRawResource
    capabilities:
      host:
        type: tosca.capabilities.Container
        valid_source_types: [unfurl.nodes.K8sResource]
    properties:
      name:
        type: string
        default: default
        metadata:
          # namespaces can't be renamed
          immutable: true

  unfurl.nodes.K8sResource:
    derived_from: unfurl.nodes.K8sRawResource
    requirements:
      - host:
          node: unfurl.nodes.K8sNamespace
          relationship: tosca.relationships.HostedOn
    attributes:
      namespace:
        type: string
        default: { get_property: [HOST, name] }

  unfurl.nodes.K8sSecretResource:
    derived_from: unfurl.nodes.K8sResource
    properties:
      data:
        type: map
        required: false
        metadata:
          sensitive: true
      apiResource:
        type: map
        required: false
        metadata:
          sensitive: true

  unfurl.nodes.CloudAccount:
    derived_from: tosca.nodes.Root
    attributes:
      account_id:
        description: "Cloud provider specific account identifier"
        type: string

  unfurl.nodes.GCPProject:
    derived_from: unfurl.nodes.CloudAccount
    properties:
      project:
        description: "Name of the Google Cloud project"
        type: string

  unfurl.nodes.AWSAccount:
    derived_from: unfurl.nodes.CloudAccount

data_types:
  unfurl.datatypes.EnvVar:
    derived_from: tosca.datatypes.Root
    type: string
    description: The value of an environment variable whose name matches the property's name

capability_types:
  unfurl.capabilities.Installer:
    derived_from: tosca.capabilities.Root

  unfurl.capabilities.Endpoint.K8sCluster:
    derived_from: tosca.capabilities.Endpoint.Admin
    description: >
      Capability to connect to a K8sCluster. See unfurl.relationships.ConnectsTo.K8sCluster
      for the semantics of its "secure" and "credential" properties.
    properties:
      host:
        type: string
        description: >
          URL of the HTTP API of Kubernetes is exposed. Format: https://<host>:<port>"
        required: false
      secure:
        type: boolean
        description: >
          If set to false, the server's certificate should not be checked for validity. This will make your HTTPS connections insecure.
        required: false

  unfurl.capabilities.Endpoint.Ansible:
    derived_from: tosca.capabilities.Endpoint.Admin
    description: Capability to connect to Ansible
    properties:
      connection:
        description: The connection type (sets "ansible_connection")
        type: string
        default: local
      port:
        type: tosca:PortDef
        description: sets "ansible_port"
        required: false
      host:
        type: string
        description: Sets "ansible_host"
        required: false
      user:
        description: Sets "ansible_user" if not set in credentials
        type: string
        required: false
      authentication_type:
        description: "Type of authentication required, should match the credential's token_type"
        type: string
        required: false
      hostvars:
        type: map
        required: false
        description: >
          Passed to ansible as host vars
          See https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html#connecting-to-hosts-behavioral-inventory-parameters

  unfurl.capabilities.Endpoint.SSH:
    derived_from: unfurl.capabilities.Endpoint.Ansible
    description: Capability to connect to the host via SSH
    properties:
      protocol:
        type: string
        value: ssh
        default: ssh
      connection:
        type: string
        default: ssh
      port:
        type: tosca:PortDef
        default: 22

relationship_types:
  unfurl.relationships.InstalledBy:
    derived_from: tosca.relationships.Root
    valid_target_types: [unfurl.capabilities.Installer]

  unfurl.relationships.ConnectsTo.K8sCluster:
    derived_from: tosca.relationships.ConnectsTo
    valid_target_types: [unfurl.capabilities.Endpoint.K8sCluster]
    properties:
      KUBECONFIG:
        type: unfurl.datatypes.EnvVar
        description: >
          Path to an existing Kubernetes config file. If not provided, and no other connection
          options are provided, and the KUBECONFIG environment variable is not set, the default location will be used (~/.kube/config.json).
        default: {get_env: KUBECONFIG}
      context:
        type: string
        description: >
          The name of a context found in the config file. If not set the current-context will be used.
        required: false
      secure:
        type: boolean
        description: >
          If false, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
        default: true
      credential:
        description: >
          token_type is either "api_key" or "password" (default is "password")
          Its "keys" map can have the following values:
          "cert_file": Path to a cert file for the certificate authority
          "ssl_ca_cert": Path to a client certificate file for TLS
          "key_file": Path to a client key file for TLS
        type: tosca.datatypes.Credential
        required: false
        metadata:
          sensitive: true

  unfurl.relationships.ConnectsTo.Ansible:
    derived_from: tosca.relationships.ConnectsTo
    valid_target_types: [unfurl.capabilities.Endpoint.Ansible]
    properties:
      credential:
        description: Its "user" property sets "ansible_user", add properties like "ssh_private_key_file" to "keys"
        type: tosca.datatypes.Credential
        required: false
        metadata:
          sensitive: true
      hostvars:
        type: map
        required: false
        description: >
          Passed to ansible as host vars
          See https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html#connecting-to-hosts-behavioral-inventory-parameters

  unfurl.relationships.ConnectsTo.CloudAccount:
    derived_from: tosca.relationships.ConnectsTo
    # valid_target_types: [unfurl.capabilities.Endpoint.CloudAccount]

  unfurl.relationships.ConnectsTo.GCPProject:
    derived_from: unfurl.relationships.ConnectsTo.CloudAccount
    properties:
      # admin_email
      # organization_id
      # billing_account
      CLOUDSDK_CORE_PROJECT:
         description: id of the project
         type: unfurl.datatypes.EnvVar
         default: {get_env: CLOUDSDK_CORE_PROJECT}
      CLOUDSDK_COMPUTE_REGION:
         description: default region to use
         type: unfurl.datatypes.EnvVar
         default: {get_env: CLOUDSDK_COMPUTE_REGION}
      CLOUDSDK_COMPUTE_ZONE:
         description: default zone to use
         type: unfurl.datatypes.EnvVar
         default: {get_env: CLOUDSDK_COMPUTE_ZONE}
      GOOGLE_APPLICATION_CREDENTIALS:
        description: "Path to file containing service account private keys in JSON format"
        type: unfurl.datatypes.EnvVar
        default: {get_env: GOOGLE_APPLICATION_CREDENTIALS}

      # other authentication options:
      GOOGLE_OAUTH_ACCESS_TOKEN:
        description: A temporary OAuth 2.0 access token obtained from the Google Authorization server
        type: unfurl.datatypes.EnvVar
        default: {get_env: GOOGLE_OAUTH_ACCESS_TOKEN}
      GCP_SERVICE_ACCOUNT_CONTENTS:
        description: "Content of file containing service account private keys"
        type: tosca.datatypes.json
        default: {get_env: GCP_SERVICE_ACCOUNT_CONTENTS}
        metadata:
          sensitive: true
        # some of the keys in the credential json:
        #   project_id
        #   type: auth_kind: type of authentication being used (choices: machineaccount, serviceaccount, application)
        #   client_email: email associated with the project
        #   scopes: The specific scopes that you want the actions to use.
      GCP_AUTH_KIND:
        type: unfurl.datatypes.EnvVar
        constraints:
          - valid_values: [application, machineaccount, serviceaccount]
        default: {get_env: [GCP_AUTH_KIND, 'serviceaccount']}
      scopes:
        type: list
        required: false

  unfurl.relationships.ConnectsTo.AWSAccount:
    derived_from: unfurl.relationships.ConnectsTo.CloudAccount
    properties:
      endpoints:
         description: custom service endpoints
         type: map
         required: false
      AWS_DEFAULT_REGION:
        description: "The default region to use, e.g. us-west-1, us-west-2, etc."
        type: unfurl.datatypes.EnvVar
        default: {get_env: AWS_DEFAULT_REGION}
      AWS_ACCESS_KEY_ID:
        description: "The access key for your AWS account"
        type: unfurl.datatypes.EnvVar
        default: {get_env: AWS_ACCESS_KEY_ID}
      AWS_SECRET_ACCESS_KEY:
        description: "The secret key for your AWS account."
        type: unfurl.datatypes.EnvVar
        default: {get_env: AWS_SECRET_ACCESS_KEY}
        metadata:
          sensitive: true
      # other authentication options:
      AWS_SESSION_TOKEN:
        description: "The session key for your AWS account."
        type: unfurl.datatypes.EnvVar
        default: {get_env: AWS_SESSION_TOKEN}
        metadata:
          sensitive: true
      AWS_PROFILE:
        type: unfurl.datatypes.EnvVar
        default: {get_env: AWS_PROFILE}
      AWS_SHARED_CREDENTIALS_FILE:
        type: unfurl.datatypes.EnvVar
        default: {get_env: AWS_SHARED_CREDENTIALS_FILE}
      AWS_CONFIG_FILE:
        type: unfurl.datatypes.EnvVar
        default: {get_env: AWS_CONFIG_FILE}
    # see https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html#using-environment-variables
    # for more environment variables

  unfurl.relationships.ConnectsTo.Azure:
    derived_from: unfurl.relationships.ConnectsTo.CloudAccount
    properties:
      AZURE_CLIENT_ID:
        type: unfurl.datatypes.EnvVar
        default: {get_env: AZURE_CLIENT_ID}
      AZURE_TENANT:
        type: unfurl.datatypes.EnvVar
        default: {get_env: AZURE_TENANT}
      AZURE_SUBSCRIPTION_ID:
        description: for authentication with service principal
        type: unfurl.datatypes.EnvVar
        default: {get_env: AZURE_SUBSCRIPTION_ID}
      AZURE_SECRET:
        description: for authentication with service principal
        type: unfurl.datatypes.EnvVar
        default: {get_env: AZURE_SECRET}
        metadata:
          sensitive: true
      AZURE_AD_USER:
        description: for authentication with Active Directory
        type: unfurl.datatypes.EnvVar
        default: {get_env: AZURE_AD_USER}
      AZURE_PASSWORD:
        description: for authentication with Active Directory
        type: unfurl.datatypes.EnvVar
        default: {get_env: AZURE_PASSWORD}
        metadata:
          sensitive: true
      AZURE_ADFS_AUTHORITY_URL:
        description: set if you have your own ADFS authority
        type: unfurl.datatypes.EnvVar
        default: {get_env: AZURE_ADFS_AUTHORITY_URL}

  unfurl.relationships.ConnectsTo.Packet:
    derived_from: unfurl.relationships.ConnectsTo.CloudAccount
    properties:
      project:
        description: UUID to packet project
        type: string
      PACKET_API_TOKEN:
        type: unfurl.datatypes.EnvVar
        default: {get_env: PACKET_API_TOKEN}
        metadata:
          sensitive: true

  unfurl.relationships.ConnectsTo.OpenStack:
    derived_from: unfurl.relationships.ConnectsTo.CloudAccount
    # from pyrax (rackspace, openstack)
    # RAX_CREDS_FILE
    # CLOUD_ID_TYPE "rackspace" or "keystone"
    # CLOUD_REGION default: "DFW"
    # also CLOUD_* in https://github.com/pycontribs/pyrax/blob/master/docs/getting_started.md#available-configuration-settings

  unfurl.relationships.ConnectsTo.Rackspace:
    derived_from: unfurl.relationships.ConnectsTo.OpenStack

interface_types:
  unfurl.interfaces.Install:
    derived_from: tosca.interfaces.Root
    check:
      description: Checks and sets the status and attributes of the instance
    discover:
      description: Discovers current state of the current instance and (possibly) related instances, updates the spec as needed.
    revert:
      description: Restore the instance to the state it was original found in

group_types:
  unfurl.groups.AnsibleInventoryGroup:
    derived_from: tosca.groups.Root
    description: Use this to place hosts in Ansible inventory groups
    properties:
      hostvars:
        description: Ansible hostvars for members of this group
        type: map
        default: {}
    members: [tosca.nodes.Compute, unfurl.groups.ansibleInventoryGroup]