heat_template_version: 2014-10-16

description: >
  This template will boot a Docker swarm cluster. A swarm cluster is made up
  of several master nodes, and N agent nodes. Every node in the cluster,
  including the master, is running a Docker daemon and a swarm agent
  advertising it to the cluster. The master is running an addition swarm
  master container listening on port 2376. By default, the cluster is made
  up of one master node and one agent 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: ""

  discovery_url:
    type: string
    description: url provided for node discovery

  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
    default: m1.small
    description: flavor to use when booting the swarm master

  node_flavor:
    type: string
    default: m1.small
    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

  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

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

  network_driver:
    type: string
    description: network driver to use for instantiating container networks
    default: None

  flannel_network_cidr:
    type: string
    description: network range for flannel overlay network
    default: 10.100.0.0/16

  flannel_network_subnetlen:
    type: number
    description: size of subnet assigned to each master
    default: 24

  flannel_backend:
    type: string
    description: >
      specify the backend for flannel, default udp backend
    default: "udp"
    constraints:
      - allowed_values: ["udp", "vxlan", "host-gw"]

  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: 2376

  swarm_version:
    type: string
    description: version of swarm used for swarm cluster
    default: 1.2.5

  swarm_strategy:
    type: string
    description: >
        schedule strategy to be used by swarm manager
    default: "spread"

  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

  registry_enabled:
    type: boolean
    description: >
      Indicates whether the docker registry is enabled.
    default: false

  registry_port:
    type: number
    description: port of registry service
    default: 5000

  swift_region:
    type: string
    description: region of swift service
    default: ""

  registry_container:
    type: string
    description: >
      name of swift container which docker registry stores images in
    default: "container"

  registry_insecure:
    type: boolean
    description: >
      indicates whether to skip TLS verification between registry and backend storage
    default: true

  registry_chunksize:
    type: number
    description: >
      size fo the data segments for the swift dynamic large objects
    default: 5242880

  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"

  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"]

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}

  etcd_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: 2379

  ######################################################################
  #
  # 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: 2376
          port_range_max: 2376
        - 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_masters, resource.0.swarm_master_external_ip]}
      master_private_ip: {get_attr: [swarm_masters, resource.0.swarm_master_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}]

  etcd_address_lb_switch:
    type: Magnum::ApiGatewaySwitcher
    properties:
      pool_private_ip: {get_attr: [etcd_lb, address]}
      master_private_ip: {get_attr: [swarm_masters, resource.0.swarm_master_ip]}

  ######################################################################
  #
  # 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_masters:
    type: "OS::Heat::ResourceGroup"
    depends_on:
      - network
    properties:
      count: {get_param: number_of_masters}
      resource_def:
        type: swarmmaster.yaml
        properties:
          name:
            list_join:
              - '-'
              - [{ get_param: 'OS::stack_name' }, '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}
          discovery_url: {get_param: discovery_url}
          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}
          verify_ca: {get_param: verify_ca}
          secgroup_swarm_master_id: {get_resource: secgroup_swarm_manager}
          network_driver: {get_param: network_driver}
          flannel_network_cidr: {get_param: flannel_network_cidr}
          flannel_network_subnetlen: {get_param: flannel_network_subnetlen}
          flannel_backend: {get_param: flannel_backend}
          swarm_port: {get_param: swarm_port}
          api_pool_id: {get_attr: [api_lb, pool_id]}
          etcd_pool_id: {get_attr: [etcd_lb, pool_id]}
          etcd_server_ip: {get_attr: [etcd_lb, address]}
          api_ip_address: {get_attr: [api_lb, floating_address]}
          swarm_version: {get_param: swarm_version}
          swarm_strategy: {get_param: swarm_strategy}
          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}
          openstack_ca: {get_param: openstack_ca}
          nodes_server_group_id: {get_resource: nodes_server_group}

  swarm_nodes:
    type: "OS::Heat::ResourceGroup"
    depends_on:
      - network
    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}
          verify_ca: {get_param: verify_ca}
          secgroup_swarm_node_id: {get_resource: secgroup_swarm_node}
          flannel_network_cidr: {get_param: flannel_network_cidr}
          network_driver: {get_param: network_driver}
          etcd_server_ip: {get_attr: [etcd_address_lb_switch, private_ip]}
          api_ip_address: {get_attr: [api_address_lb_switch, public_ip]}
          swarm_version: {get_param: swarm_version}
          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}
          registry_enabled: {get_param: registry_enabled}
          registry_port: {get_param: registry_port}
          swift_region: {get_param: swift_region}
          registry_container: {get_param: registry_container}
          registry_insecure: {get_param: registry_insecure}
          registry_chunksize: {get_param: registry_chunksize}
          volume_driver: {get_param: volume_driver}
          rexray_preempt: {get_param: rexray_preempt}
          openstack_ca: {get_param: openstack_ca}
          nodes_server_group_id: {get_resource: nodes_server_group}

outputs:

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

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

  swarm_masters:
    value: {get_attr: [swarm_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.

  discovery_url:
    value: {get_param: discovery_url}
    description: >
      This the discovery url for Swarm cluster.
