heat_template_version: 2014-10-16

description: >
  This template will boot a Docker Swarm-Mode cluster. A swarm cluster
  is made up of several master nodes, and N worker nodes. Every node in
  the cluster, including the master, is running a Docker daemon and
  joins the swarm as a manager or as a worker.  The managers are
  listening on port 2375. By default, the cluster is made up of one
  master node and one worker node.

parameters:

  #
  # REQUIRED PARAMETERS
  #
  ssh_key_name:
    type: string
    description: name of ssh key to be provisioned on our server

  external_network:
    type: string
    description: uuid/name of a network to use for floating ip addresses

  fixed_network:
    type: string
    description: uuid/name of an existing network to use to provision machines
    default: ""

  fixed_subnet:
    type: string
    description: uuid/name of an existing subnet to use to provision machines
    default: ""

  cluster_uuid:
    type: string
    description: identifier for the cluster this template is generating

  magnum_url:
    type: string
    description: endpoint to retrieve TLS certs from

  server_image:
    type: string
    description: glance image used to boot the server

  #
  # OPTIONAL PARAMETERS
  #
  master_flavor:
    type: string
    description: flavor to use when booting the swarm master
    default: m1.small

  node_flavor:
    type: string
    description: flavor to use when booting the swarm node

  dns_nameserver:
    type: string
    description: address of a dns nameserver reachable in your environment
    default: 8.8.8.8

  http_proxy:
    type: string
    description: http proxy address for docker
    default: ""

  https_proxy:
    type: string
    description: https proxy address for docker
    default: ""

  no_proxy:
    type: string
    description: no proxies for docker
    default: ""

  number_of_masters:
    type: number
    description: how many swarm masters to spawn
    default: 1

  number_of_nodes:
    type: number
    description: how many swarm nodes to spawn
    default: 1

  number_of_secondary_masters:
    type: number
    description: how many secondary masters to spawn

  fixed_network_cidr:
    type: string
    description: network range for fixed ip network
    default: "10.0.0.0/24"

  tls_disabled:
    type: boolean
    description: whether or not to enable TLS
    default: False

  docker_volume_size:
    type: number
    description: >
      size of a cinder volume to allocate to docker for container/image
      storage
    default: 0

  docker_volume_type:
    type: string
    description: >
      type of a cinder volume to allocate to docker for container/image
      storage

  docker_storage_driver:
    type: string
    description: docker storage driver name
    default: "devicemapper"

  loadbalancing_protocol:
    type: string
    description: >
      The protocol which is used for load balancing. If you want to change
      tls_disabled option to 'True', please change this to "HTTP".
    default: TCP
    constraints:
      - allowed_values: ["TCP", "HTTP"]

  swarm_port:
    type: number
    description: >
      The port which are used by swarm manager to provide swarm service.
    default: 2375

  trustee_domain_id:
    type: string
    description: domain id of the trustee
    default: ""

  trustee_user_id:
    type: string
    description: user id of the trustee
    default: ""

  trustee_username:
    type: string
    description: username of the trustee
    default: ""

  trustee_password:
    type: string
    description: password of the trustee
    default: ""
    hidden: true

  trust_id:
    type: string
    description: id of the trust which is used by the trustee
    default: ""
    hidden: true

  auth_url:
    type: string
    description: url for keystone

  volume_driver:
    type: string
    description: volume driver to use for container storage
    default: ""
    constraints:
      - allowed_values: ["","rexray"]

  rexray_preempt:
    type: string
    description: >
      enables any host to take control of a volume irrespective of whether
      other hosts are using the volume
    default: "false"

  verify_ca:
    type: boolean
    description: whether or not to validate certificate authority

  openstack_ca:
    type: string
    hidden: true
    description: The OpenStack CA certificate to install on the node.

  nodes_affinity_policy:
    type: string
    description: >
      affinity policy for nodes server group
    constraints:
      - allowed_values: ["affinity", "anti-affinity", "soft-affinity",
                         "soft-anti-affinity"]

  availability_zone:
    type: string
    description: >
      availability zone for master and nodes
    default: ""

resources:

  ######################################################################
  #
  # network resources.  allocate a network and router for our server.
  # it would also be possible to take advantage of existing network
  # resources (and have the deployer provide network and subnet ids,
  # etc, as parameters), but I wanted to minmize the amount of
  # configuration necessary to make this go.

  network:
    type: ../../common/templates/network.yaml
    properties:
      existing_network: {get_param: fixed_network}
      existing_subnet: {get_param: fixed_subnet}
      private_network_cidr: {get_param: fixed_network_cidr}
      dns_nameserver: {get_param: dns_nameserver}
      external_network: {get_param: external_network}

  api_lb:
    type: ../../common/templates/lb.yaml
    properties:
      fixed_subnet: {get_attr: [network, fixed_subnet]}
      external_network: {get_param: external_network}
      protocol: {get_param: loadbalancing_protocol}
      port: {get_param: swarm_port}

  ######################################################################
  #
  # security groups.  we need to permit network traffic of various
  # sorts.
  #

  secgroup_swarm_manager:
    type: "OS::Neutron::SecurityGroup"
    properties:
      rules:
        - protocol: icmp
        - protocol: tcp
          port_range_min: 22
          port_range_max: 22
        - protocol: tcp
          port_range_min: 2375
          port_range_max: 2375
        - protocol: tcp
          port_range_min: 2377
          port_range_max: 2377
        - protocol: tcp
          remote_ip_prefix: {get_param: fixed_network_cidr}
          port_range_min: 1
          port_range_max: 65535
        - protocol: udp
          port_range_min: 53
          port_range_max: 53

  secgroup_swarm_node:
    type: "OS::Neutron::SecurityGroup"
    properties:
      rules:
        - protocol: icmp
        - protocol: tcp
        - protocol: udp

  ######################################################################
  #
  # resources that expose the IPs of either the swarm master or a given
  # LBaaS pool depending on whether LBaaS is enabled for the cluster.
  #

  api_address_lb_switch:
    type: Magnum::ApiGatewaySwitcher
    properties:
      pool_public_ip: {get_attr: [api_lb, floating_address]}
      pool_private_ip: {get_attr: [api_lb, address]}
      master_public_ip: {get_attr: [swarm_primary_master, resource.0.swarm_master_external_ip]}
      master_private_ip: {get_attr: [swarm_primary_master, resource.0.swarm_master_ip]}

  ######################################################################
  #
  # resources that expose the IPs of either floating ip or a given
  # fixed ip depending on whether FloatingIP is enabled for the cluster.
  #

  api_address_floating_switch:
    type: Magnum::FloatingIPAddressSwitcher
    properties:
      public_ip: {get_attr: [api_address_lb_switch, public_ip]}
      private_ip: {get_attr: [api_address_lb_switch, private_ip]}

  ######################################################################
  #
  # resources that expose the server group for all nodes include master
  # and minions.
  #

  nodes_server_group:
    type: OS::Nova::ServerGroup
    properties:
      policies: [{get_param: nodes_affinity_policy}]

  ######################################################################
  #
  # Swarm manager is responsible for the entire cluster and manages the
  # resources of multiple Docker hosts at scale.
  # It supports high availability by create a primary manager and multiple
  # replica instances.

  swarm_primary_master:
    type: "OS::Heat::ResourceGroup"
    depends_on:
      - network
    properties:
      count: 1
      resource_def:
        type: swarmmaster.yaml
        properties:
          name:
            list_join:
              - '-'
              - [{ get_param: 'OS::stack_name' }, 'primary-master', '%index%']
          is_primary_master: True
          ssh_key_name: {get_param: ssh_key_name}
          server_image: {get_param: server_image}
          server_flavor: {get_param: master_flavor}
          docker_volume_size: {get_param: docker_volume_size}
          docker_volume_type: {get_param: docker_volume_type}
          docker_storage_driver: {get_param: docker_storage_driver}
          fixed_network_id: {get_attr: [network, fixed_network]}
          fixed_subnet_id: {get_attr: [network, fixed_subnet]}
          external_network: {get_param: external_network}
          http_proxy: {get_param: http_proxy}
          https_proxy: {get_param: https_proxy}
          no_proxy: {get_param: no_proxy}
          swarm_api_ip: {get_attr: [api_lb, address]}
          cluster_uuid: {get_param: cluster_uuid}
          magnum_url: {get_param: magnum_url}
          tls_disabled: {get_param: tls_disabled}
          secgroup_swarm_master_id: {get_resource: secgroup_swarm_manager}
          swarm_port: {get_param: swarm_port}
          api_pool_id: {get_attr: [api_lb, pool_id]}
          api_ip_address: {get_attr: [api_lb, floating_address]}
          trustee_user_id: {get_param: trustee_user_id}
          trustee_password: {get_param: trustee_password}
          trust_id: {get_param: trust_id}
          auth_url: {get_param: auth_url}
          volume_driver: {get_param: volume_driver}
          rexray_preempt: {get_param: rexray_preempt}
          verify_ca: {get_param: verify_ca}
          openstack_ca: {get_param: openstack_ca}
          nodes_server_group_id: {get_resource: nodes_server_group}
          availability_zone: {get_param: availability_zone}

  swarm_secondary_masters:
    type: "OS::Heat::ResourceGroup"
    depends_on:
      - network
      - swarm_primary_master
    properties:
      count: {get_param: number_of_secondary_masters}
      resource_def:
        type: swarmmaster.yaml
        properties:
          name:
            list_join:
              - '-'
              - [{ get_param: 'OS::stack_name' }, 'secondary-master', '%index%']
          ssh_key_name: {get_param: ssh_key_name}
          server_image: {get_param: server_image}
          server_flavor: {get_param: master_flavor}
          docker_volume_size: {get_param: docker_volume_size}
          docker_volume_type: {get_param: docker_volume_type}
          docker_storage_driver: {get_param: docker_storage_driver}
          fixed_network_id: {get_attr: [network, fixed_network]}
          fixed_subnet_id: {get_attr: [network, fixed_subnet]}
          external_network: {get_param: external_network}
          http_proxy: {get_param: http_proxy}
          https_proxy: {get_param: https_proxy}
          no_proxy: {get_param: no_proxy}
          swarm_api_ip: {get_attr: [api_address_lb_switch, private_ip]}
          cluster_uuid: {get_param: cluster_uuid}
          magnum_url: {get_param: magnum_url}
          tls_disabled: {get_param: tls_disabled}
          secgroup_swarm_master_id: {get_resource: secgroup_swarm_manager}
          swarm_port: {get_param: swarm_port}
          api_pool_id: {get_attr: [api_lb, pool_id]}
          api_ip_address: {get_attr: [api_lb, floating_address]}
          trustee_user_id: {get_param: trustee_user_id}
          trustee_password: {get_param: trustee_password}
          trust_id: {get_param: trust_id}
          auth_url: {get_param: auth_url}
          volume_driver: {get_param: volume_driver}
          rexray_preempt: {get_param: rexray_preempt}
          verify_ca: {get_param: verify_ca}
          openstack_ca: {get_param: openstack_ca}
          nodes_server_group_id: {get_resource: nodes_server_group}
          availability_zone: {get_param: availability_zone}

  swarm_nodes:
    type: "OS::Heat::ResourceGroup"
    depends_on:
      - network
      - swarm_primary_master
    properties:
      count: {get_param: number_of_nodes}
      resource_def:
        type: swarmnode.yaml
        properties:
          name:
            list_join:
              - '-'
              - [{ get_param: 'OS::stack_name' }, 'node', '%index%']
          ssh_key_name: {get_param: ssh_key_name}
          server_image: {get_param: server_image}
          server_flavor: {get_param: node_flavor}
          docker_volume_size: {get_param: docker_volume_size}
          docker_volume_type: {get_param: docker_volume_type}
          docker_storage_driver: {get_param: docker_storage_driver}
          fixed_network_id: {get_attr: [network, fixed_network]}
          fixed_subnet_id: {get_attr: [network, fixed_subnet]}
          external_network: {get_param: external_network}
          http_proxy: {get_param: http_proxy}
          https_proxy: {get_param: https_proxy}
          no_proxy: {get_param: no_proxy}
          swarm_api_ip: {get_attr: [api_address_lb_switch, private_ip]}
          cluster_uuid: {get_param: cluster_uuid}
          magnum_url: {get_param: magnum_url}
          tls_disabled: {get_param: tls_disabled}
          secgroup_swarm_node_id: {get_resource: secgroup_swarm_node}
          api_ip_address: {get_attr: [api_address_lb_switch, public_ip]}
          trustee_domain_id: {get_param: trustee_domain_id}
          trustee_user_id: {get_param: trustee_user_id}
          trustee_username: {get_param: trustee_username}
          trustee_password: {get_param: trustee_password}
          trust_id: {get_param: trust_id}
          auth_url: {get_param: auth_url}
          volume_driver: {get_param: volume_driver}
          rexray_preempt: {get_param: rexray_preempt}
          verify_ca: {get_param: verify_ca}
          openstack_ca: {get_param: openstack_ca}
          nodes_server_group_id: {get_resource: nodes_server_group}
          availability_zone: {get_param: availability_zone}

outputs:

  api_address:
    value:
      str_replace:
        template: api_ip_address
        params:
          api_ip_address: {get_attr: [api_address_floating_switch, ip_address]}
    description: >
      This is the API endpoint of the Swarm masters. Use this to access
      the Swarm API server from outside the cluster.

  swarm_primary_master_private:
    value: {get_attr: [swarm_primary_master, swarm_master_ip]}
    description: >
      This is a list of the "private" addresses of all the Swarm masters.

  swarm_primary_master:
    value: {get_attr: [swarm_primary_master, swarm_master_external_ip]}
    description: >
      This is a list of "public" ip addresses of all Swarm masters.
      Use these addresses to log into the Swarm masters via ssh.

  swarm_secondary_masters:
    value: {get_attr: [swarm_secondary_masters, swarm_master_external_ip]}
    description: >
      This is a list of "public" ip addresses of all Swarm masters.
      Use these addresses to log into the Swarm masters via ssh.

  swarm_nodes_private:
    value: {get_attr: [swarm_nodes, swarm_node_ip]}
    description: >
      This is a list of the "private" addresses of all the Swarm nodes.

  swarm_nodes:
    value: {get_attr: [swarm_nodes, swarm_node_external_ip]}
    description: >
      This is a list of the "public" addresses of all the Swarm nodes. Use
      these addresses to, e.g., log into the nodes.
