heat_template_version: pike

description: >
  This is a template which will build the TLS Certificates necessary
  for the load balancer using the given parameters.

parameters:
  # Can be overridden via parameter_defaults in the environment
  SSLCertificate:
    default: ''
    description: >
      The content of the SSL certificate (without Key) in PEM format.
    type: string
  SSLIntermediateCertificate:
    default: ''
    description: >
      The content of an SSL intermediate CA certificate in PEM format.
    type: string
  SSLKey:
    description: >
      The content of the SSL Key in PEM format.
    type: string
    hidden: true

  # Can be overridden by parameter_defaults if the user wants to try deploying
  # this in a distro that doesn't support this path.
  DeployedSSLCertificatePath:
    default: '/etc/pki/tls/private/overcloud_endpoint.pem'
    description: >
        The filepath of the certificate as it will be stored in the controller.
    type: string

  # Passed in by the controller
  NodeIndex:
    default: 0
    type: number
  server:
    description: ID of the controller node to apply this config to
    type: string

resources:
  ControllerTLSConfig:
    type: OS::Heat::SoftwareConfig
    properties:
      group: script
      inputs:
        - name: cert_path
        - name: cert_chain_content
      outputs:
        - name: chain_md5sum
        - name: cert_modulus
        - name: key_modulus
      config: |
        #!/bin/sh
        # If the HAProxy container tried to load this, it'll be a directory and
        # will make this fail.
        if [ -d ${cert_path} ]; then
            rmdir ${cert_path}
            HAPROXY_TLS_UPDATE_NEEDED=1
        else
            HAPROXY_TLS_UPDATE_NEEDED=0
        fi
        cat > ${cert_path} << EOF
        ${cert_chain_content}
        EOF
        chmod 0440 ${cert_path}
        chown root:haproxy ${cert_path}
        md5sum ${cert_path} > ${heat_outputs_path}.chain_md5sum
        openssl x509 -noout -modulus -in ${cert_path} \
          | openssl md5 | cut -c 10- \
          > ${heat_outputs_path}.cert_modulus
        openssl rsa -noout -modulus -in ${cert_path} \
          | openssl md5 | cut -c 10- \
          > ${heat_outputs_path}.key_modulus
        # We need to reload haproxy in case the certificate changed because
        # puppet doesn't know the contents of the cert file.
        haproxy_status=$(systemctl is-active haproxy)
        if [ "$haproxy_status" = "active" ]; then
            systemctl reload haproxy
        fi
        pacemaker_status=$(systemctl is-active pacemaker)
        # If we need an update and pacemaker is being used, we need to restart
        # the pacemaker resource on the bootstrap node. We don't support the update
        # in non-pacemaker cases.
        if [[ $HAPROXY_TLS_UPDATE_NEEDED -eq 1 && "$pacemaker_status" == "active" ]]; then
            BOOTSTRAPNODE=$(hiera -c /etc/puppet/hiera.yaml bootstrap_nodeid)
            MY_HOSTNAME=$(hostname)
            if [[ "$BOOTSTRAPNODE" == "$MY_HOSTNAME" ]]; then
                # Triggers an update
                HAPROXY_RESOURCE_NAME=$(pcs status | grep container | grep haproxy | sed 's/^.*container.*: \(.*\) .*/\1/')
                if [[ -n "$HAPROXY_RESOURCE_NAME" ]]; then
                    pcs resource restart "$HAPROXY_RESOURCE_NAME"
                fi
            fi
        elif [[ $HAPROXY_TLS_UPDATE_NEEDED -eq 0 ]]; then
            # Handles reloading HAProxy and fetching a new certificate if
            # necessary
            HAPROXY_CONTAINER_ID=$(docker ps | grep haproxy | awk '{print $1}')
            if [[ -n "$HAPROXY_CONTAINER_ID" ]]; then
                if [[ "$pacemaker_status" == "active" ]]; then
                    # We copy the certificate from the mount point to the desired
                    # path
                    docker exec "$HAPROXY_CONTAINER_ID" cp /var/lib/kolla/config_files/src-tls${cert_path} ${cert_path}
                fi
                docker kill --signal=HUP "$HAPROXY_CONTAINER_ID"
            fi
        fi


  ControllerTLSDeployment:
    type: OS::Heat::SoftwareDeployment
    properties:
      name: ControllerTLSDeployment
      config: {get_resource: ControllerTLSConfig}
      server: {get_param: server}
      input_values:
        cert_path: {get_param: DeployedSSLCertificatePath}
        cert_chain_content:
          list_join:
            - ''
            - - {get_param: SSLCertificate}
              - {get_param: SSLIntermediateCertificate}
              - {get_param: SSLKey}

outputs:
  deploy_stdout:
    description: Deployment reference
    value: {get_attr: [ControllerTLSDeployment, chain_md5sum]}
  deployed_ssl_certificate_path:
    description: The location that the TLS certificate was deployed to.
    value: {get_param: DeployedSSLCertificatePath}
  key_modulus_md5:
    description: MD5 checksum of the Key SSL Modulus
    value: {get_attr: [ControllerTLSDeployment, key_modulus]}
  cert_modulus_md5:
    description: MD5 checksum of the Certificate SSL Modulus
    value: {get_attr: [ControllerTLSDeployment, cert_modulus]}
