heat_template_version: rocky

description: >
  OpenStack containerized swift proxy service

parameters:
  ContainerSwiftProxyImage:
    description: image
    type: string
  ContainerSwiftConfigImage:
    description: The container image to use for the swift config_volume
    type: string
  EndpointMap:
    default: {}
    description: Mapping of service endpoint -> protocol. Typically set
                 via parameter_defaults in the resource registry.
    type: json
  ServiceData:
    default: {}
    description: Dictionary packing service data
    type: json
  ServiceNetMap:
    default: {}
    description: Mapping of service_name -> network name. Typically set
                 via parameter_defaults in the resource registry.  This
                 mapping overrides those in ServiceNetMapDefaults.
    type: json
  DefaultPasswords:
    default: {}
    type: json
  RoleName:
    default: ''
    description: Role name on which the service is applied
    type: string
  RoleParameters:
    default: {}
    description: Parameters specific to the role
    type: json
  SwiftEncryptionEnabled:
    description: Set to True to enable data-at-rest encryption in Swift
    default: false
    type: boolean
  DeployIdentifier:
    default: ''
    type: string
    description: >
      Setting this to a unique value will re-run any deployment tasks which
      perform configuration on a Heat stack-update.
  SwiftPassword:
    description: The password for the swift service account
    type: string
    hidden: true
  SwiftProxyNodeTimeout:
    default: 60
    description: Timeout for requests going from swift-proxy to swift a/c/o services.
    type: number
  SwiftWorkers:
    default: 0
    description: Number of workers for Swift service.
    type: string
  KeystoneRegion:
    type: string
    default: 'regionOne'
    description: Keystone region for endpoint
  MonitoringSubscriptionSwiftProxy:
    default: 'overcloud-swift-proxy'
    type: string
  SwiftCeilometerPipelineEnabled:
    description: Set to False to disable the swift proxy ceilometer pipeline.
    default: false
    type: boolean
  SwiftCeilometerIgnoreProjects:
    default: ['service']
    description: Comma-seperated list of project names to ignore.
    type: comma_delimited_list
  RpcUseSSL:
    default: false
    description: >
        Messaging client subscriber parameter to specify
        an SSL connection to the messaging host.
    type: string
  EnableInternalTLS:
    type: boolean
    default: false
  SwiftCorsAllowedOrigin:
    type: string
    default: ''
    description: Indicate whether this resource may be shared with the domain received in the request
                 "origin" header.

conditions:

  internal_tls_enabled: {equals: [{get_param: EnableInternalTLS}, true]}
  swift_encryption_enabled: {equals : [{get_param: SwiftEncryptionEnabled}, true]}
  ceilometer_pipeline_enabled: {equals : [{get_param: SwiftCeilometerPipelineEnabled}, true]}
  use_tls_proxy: {equals : [{get_param: EnableInternalTLS}, true]}
  cors_allowed_origin_unset: {equals : [{get_param: SwiftCorsAllowedOrigin}, '']}
  swift_workers_zero: {equals : [{get_param: SwiftWorkers}, '0']}

resources:

  ContainersCommon:
    type: ../containers-common.yaml

  SwiftBase:
    type: ./swift-base.yaml
    properties:
      EndpointMap: {get_param: EndpointMap}
      ServiceData: {get_param: ServiceData}
      ServiceNetMap: {get_param: ServiceNetMap}
      DefaultPasswords: {get_param: DefaultPasswords}
      RoleName: {get_param: RoleName}
      RoleParameters: {get_param: RoleParameters}

  TLSProxyBase:
    type: OS::TripleO::Services::TLSProxyBase
    properties:
      ServiceData: {get_param: ServiceData}
      ServiceNetMap: {get_param: ServiceNetMap}
      DefaultPasswords: {get_param: DefaultPasswords}
      EndpointMap: {get_param: EndpointMap}
      EnableInternalTLS: {get_param: EnableInternalTLS}

outputs:
  role_data:
    description: Role data for the swift proxy.
    value:
      service_name: swift_proxy
      firewall_rules:
        '122 swift proxy':
          dport:
            - 8080
            - 13808
      monitoring_subscription: {get_param: MonitoringSubscriptionSwiftProxy}
      config_settings:
        map_merge:
          - get_attr: [SwiftBase, role_data, config_settings]
          - get_attr: [TLSProxyBase, role_data, config_settings]
          -
            if:
            - cors_allowed_origin_unset
            - {}
            - swift::proxy::cors_allow_origin: {get_param: SwiftCorsAllowedOrigin}
          - swift::proxy::authtoken::auth_uri: {get_param: [EndpointMap, KeystoneInternal, uri_no_suffix]}
            swift::proxy::authtoken::auth_url: {get_param: [EndpointMap, KeystoneInternal, uri_no_suffix]}
            swift::proxy::authtoken::password: {get_param: SwiftPassword}
            swift::proxy::authtoken::project_name: 'service'
            swift::proxy::s3token::auth_uri: {get_param: [EndpointMap, KeystoneV3Internal, uri]}
            swift::proxy::node_timeout: {get_param: SwiftProxyNodeTimeout}
          -
            if:
            - swift_workers_zero
            - {}
            - swift::proxy::workers: {get_param: SwiftWorkers}
          -
            if:
            - ceilometer_pipeline_enabled
            -
              swift::proxy::ceilometer::auth_uri: {get_param: [EndpointMap, KeystoneInternal, uri_no_suffix]}
              swift::proxy::ceilometer::auth_url: {get_param: [EndpointMap, KeystoneInternal, uri_no_suffix]}
              swift::proxy::ceilometer::password: {get_param: SwiftPassword}
              swift::proxy::ceilometer::ignore_projects: {get_param: SwiftCeilometerIgnoreProjects}
              swift::proxy::ceilometer::nonblocking_notify: true
            - {}
          - swift::proxy::staticweb::url_base: {get_param: [EndpointMap, SwiftPublic, uri_no_suffix]}
            tripleo::profile::base::swift::proxy::ceilometer_messaging_use_ssl: {get_param: RpcUseSSL}
            tripleo::profile::base::swift::proxy::ceilometer_enabled: {get_param: SwiftCeilometerPipelineEnabled}
            swift::proxy::keystone::operator_roles:
              - admin
              - swiftoperator
              - ResellerAdmin
            swift::proxy::versioned_writes::allow_versioned_writes: true
          - if:
            - swift_encryption_enabled
            -
              swift::keymaster::key_id: 'test_id'
              swift::keymaster::username: 'swift'
              swift::keymaster::password: {get_param: SwiftPassword}
              swift::keymaster::project_name: 'service'
              swift::keymaster::project_domain_id: 'default'
              swift::keymaster::user_domain_id: 'default'
              swift::keymaster::auth_endpoint: {get_param: [EndpointMap, KeystoneInternal, uri]}
            - {}
          - swift::proxy::pipeline:
              yaql:
                expression: $.data.pipeline.where($ != '')
                data:
                  pipeline:
                  - 'catch_errors'
                  - 'healthcheck'
                  - 'proxy-logging'
                  - 'cache'
                  - 'ratelimit'
                  - 'bulk'
                  - 'tempurl'
                  - 'formpost'
                  - 'authtoken'
                  - 's3api'
                  - 's3token'
                  - 'keystone'
                  - 'staticweb'
                  - 'copy'
                  - 'container_quotas'
                  - 'account_quotas'
                  - 'slo'
                  - 'dlo'
                  - 'versioned_writes'
                  -
                    if:
                    - ceilometer_pipeline_enabled
                    - 'ceilometer'
                    - ''
                  -
                    if:
                    - swift_encryption_enabled
                    - 'kms_keymaster'
                    - ''
                  -
                    if:
                    - swift_encryption_enabled
                    - 'encryption'
                    - ''
                  - 'proxy-logging'
                  - 'proxy-server'
            swift::proxy::account_autocreate: true
            # NOTE: bind IP is found in hiera replacing the network name with the
            # local node IP for the given network; replacement examples
            # (eg. for internal_api):
            # internal_api -> IP
            # internal_api_uri -> [IP]
            # internal_api_subnet - > IP/CIDR
            tripleo::profile::base::swift::proxy::tls_proxy_bind_ip:
              str_replace:
                template:
                  "%{hiera('$NETWORK')}"
                params:
                  $NETWORK: {get_param: [ServiceNetMap, SwiftProxyNetwork]}
            tripleo::profile::base::swift::proxy::tls_proxy_fqdn:
              str_replace:
                template:
                  "%{hiera('fqdn_$NETWORK')}"
                params:
                  $NETWORK: {get_param: [ServiceNetMap, SwiftProxyNetwork]}
            tripleo::profile::base::swift::proxy::tls_proxy_port:
              get_param: [EndpointMap, SwiftInternal, port]
            swift::proxy::port: {get_param: [EndpointMap, SwiftInternal, port]}
            swift::proxy::proxy_local_net_ip:
              if:
              - use_tls_proxy
              - "%{hiera('localhost_address')}"
              - str_replace:
                  template:
                    "%{hiera('$NETWORK')}"
                  params:
                    $NETWORK: {get_param: [ServiceNetMap, SwiftProxyNetwork]}
      service_config_settings:
        keystone:
          swift::keystone::auth::public_url: {get_param: [EndpointMap, SwiftPublic, uri]}
          swift::keystone::auth::internal_url: {get_param: [EndpointMap, SwiftInternal, uri]}
          swift::keystone::auth::admin_url: {get_param: [EndpointMap, SwiftAdmin, uri]}
          swift::keystone::auth::public_url_s3: {get_param: [EndpointMap, SwiftS3Public, uri]}
          swift::keystone::auth::internal_url_s3: {get_param: [EndpointMap, SwiftS3Internal, uri]}
          swift::keystone::auth::admin_url_s3: {get_param: [EndpointMap, SwiftS3Admin, uri]}
          swift::keystone::auth::password: {get_param: SwiftPassword}
          swift::keystone::auth::region: {get_param: KeystoneRegion}
          swift::keystone::auth::tenant: 'service'
          swift::keystone::auth::configure_s3_endpoint: false
          swift::keystone::auth::operator_roles:
            - admin
            - swiftoperator
            - ResellerAdmin
      # BEGIN DOCKER SETTINGS
      puppet_config:
        config_volume: swift
        puppet_tags: swift_config,swift_proxy_config,swift_keymaster_config
        step_config: |
          include ::tripleo::profile::base::swift::proxy
        config_image: {get_param: ContainerSwiftConfigImage}
      kolla_config:
        /var/lib/kolla/config_files/swift_proxy.json:
          command: /usr/bin/swift-proxy-server /etc/swift/proxy-server.conf
          config_files:
            - source: "/var/lib/kolla/config_files/src/*"
              dest: "/"
              merge: true
              preserve_properties: true
        /var/lib/kolla/config_files/swift_proxy_tls_proxy.json:
          command: /usr/sbin/httpd -DFOREGROUND
          config_files:
            - source: "/var/lib/kolla/config_files/src/etc/httpd/conf.d"
              dest: "/etc/httpd/conf.d"
              merge: false
              preserve_properties: true
            - source: "/var/lib/kolla/config_files/src/*"
              dest: "/"
              merge: true
              preserve_properties: true
      container_config_scripts:
        create_swift_secret.sh:
          mode: "0700"
          content: |
            #!/bin/bash
            export OS_PROJECT_DOMAIN_ID=$(crudini --get /etc/swift/keymaster.conf kms_keymaster project_domain_id)
            export OS_USER_DOMAIN_ID=$(crudini --get /etc/swift/keymaster.conf kms_keymaster user_domain_id)
            export OS_PROJECT_NAME=$(crudini --get /etc/swift/keymaster.conf kms_keymaster project_name)
            export OS_USERNAME=$(crudini --get /etc/swift/keymaster.conf kms_keymaster username)
            export OS_PASSWORD=$(crudini --get /etc/swift/keymaster.conf kms_keymaster password)
            export OS_AUTH_URL=$(crudini --get /etc/swift/keymaster.conf kms_keymaster auth_endpoint)
            export OS_AUTH_TYPE=password
            export OS_IDENTITY_API_VERSION=3

            echo "Check if secret already exists"
            secret_href=$(openstack secret list --name swift_root_secret_uuid)
            rc=$?
            if [[ $rc != 0 ]]; then
              echo "Failed to check secrets, check if Barbican in enabled and responding properly"
              exit $rc;
            fi
            if [ -z "$secret_href" ]; then
              echo "Create new secret"
              order_href=$(openstack secret order create --name swift_root_secret_uuid --payload-content-type="application/octet-stream" --algorithm aes --bit-length 256 --mode ctr key -f value -c "Order href")
            fi
        set_swift_keymaster_key_id.sh:
          mode: "0700"
          content: |
            #!/bin/bash
            export OS_PROJECT_DOMAIN_ID=$(crudini --get /etc/swift/keymaster.conf kms_keymaster project_domain_id)
            export OS_USER_DOMAIN_ID=$(crudini --get /etc/swift/keymaster.conf kms_keymaster user_domain_id)
            export OS_PROJECT_NAME=$(crudini --get /etc/swift/keymaster.conf kms_keymaster project_name)
            export OS_USERNAME=$(crudini --get /etc/swift/keymaster.conf kms_keymaster username)
            export OS_PASSWORD=$(crudini --get /etc/swift/keymaster.conf kms_keymaster password)
            export OS_AUTH_URL=$(crudini --get /etc/swift/keymaster.conf kms_keymaster auth_endpoint)
            export OS_AUTH_TYPE=password
            export OS_IDENTITY_API_VERSION=3
            echo "retrieve key_id"
            loop_wait=2
            for i in {0..5}; do
              #TODO update uuid from mistral here too
              secret_href=$(openstack secret list --name swift_root_secret_uuid)
              if [ "$secret_href" ]; then
                echo "set key_id in keymaster.conf"
                secret_href=$(openstack secret list --name swift_root_secret_uuid -f value -c "Secret href")
                crudini --set /etc/swift/keymaster.conf kms_keymaster key_id ${secret_href##*/}
                exit 0
              else
                echo "no key, wait for $loop_wait and check again"
                sleep $loop_wait
                ((loop_wait++))
              fi
            done
            echo "Failed to set secret in keymaster.conf, check if Barbican is enabled and responding properly"
            exit 1
      docker_config:
        step_4:
          map_merge:
            - if:
                - swift_encryption_enabled
                - create_swift_secret:
                    # NOTE: Barbican should be started before creating secrets
                    start_order: 0
                    image: &swift_proxy_image {get_param: ContainerSwiftProxyImage}
                    net: host
                    detach: false
                    volumes:
                        list_concat:
                          - {get_attr: [ContainersCommon, volumes]}
                          -
                            - /var/lib/config-data/puppet-generated/swift/etc/swift:/etc/swift:ro
                            - /var/lib/container-config-scripts/create_swift_secret.sh:/create_swift_secret.sh:ro
                    user: root
                    command: "/usr/bin/bootstrap_host_exec swift_proxy /create_swift_secret.sh"
                - {}
            - if:
                - swift_encryption_enabled
                - set_swift_secret:
                    start_order: 1
                    image: *swift_proxy_image
                    net: host
                    detach: false
                    volumes:
                        list_concat:
                          - {get_attr: [ContainersCommon, volumes]}
                          -
                            - /var/lib/config-data/puppet-generated/swift/etc/swift:/etc/swift:rw
                            - /var/lib/container-config-scripts/set_swift_keymaster_key_id.sh:/set_swift_keymaster_key_id.sh:ro
                    user: root
                    command: "/set_swift_keymaster_key_id.sh"
                    environment:
                      # NOTE: this should force this container to re-run on each
                      # update (scale-out, etc.)
                      TRIPLEO_DEPLOY_IDENTIFIER: {get_param: DeployIdentifier}
                - {}
            - swift_proxy:
                image: *swift_proxy_image
                start_order: 2
                net: host
                user: swift
                restart: always
                healthcheck:
                  test: /openstack/healthcheck
                volumes:
                  list_concat:
                    - {get_attr: [ContainersCommon, volumes]}
                    -
                      - /var/lib/kolla/config_files/swift_proxy.json:/var/lib/kolla/config_files/config.json:ro
                      - /var/lib/config-data/puppet-generated/swift/:/var/lib/kolla/config_files/src:ro
                      - /run:/run
                      - /srv/node:/srv/node
                      - /dev:/dev
                      - /var/log/containers/swift:/var/log/swift:z
                environment:
                  KOLLA_CONFIG_STRATEGY: COPY_ALWAYS
            - if:
                - internal_tls_enabled
                - swift_proxy_tls_proxy:
                    image: *swift_proxy_image
                    net: host
                    user: root
                    restart: always
                    volumes:
                      list_concat:
                        - {get_attr: [ContainersCommon, volumes]}
                        -
                          - /var/lib/kolla/config_files/swift_proxy_tls_proxy.json:/var/lib/kolla/config_files/config.json:ro
                          - /var/lib/config-data/puppet-generated/swift/:/var/lib/kolla/config_files/src:ro
                          - /etc/pki/tls/certs/httpd:/etc/pki/tls/certs/httpd:ro
                          - /etc/pki/tls/private/httpd:/etc/pki/tls/private/httpd:ro
                    environment:
                      KOLLA_CONFIG_STRATEGY: COPY_ALWAYS
                - {}
      host_prep_tasks:
        - name: create persistent directories
          file:
            path: "{{ item.path }}"
            state: directory
            setype: "{{ item.setype }}"
          with_items:
            - { 'path': /srv/node, 'setype': svirt_sandbox_file_t }
            - { 'path': /var/log/swift, 'setype': svirt_sandbox_file_t }
            - { 'path': /var/log/containers/swift, 'setype': svirt_sandbox_file_t, 'mode': '0750' }
      deploy_steps_tasks:
        - name: Configure rsyslog for swift-proxy
          when: step|int == 1
          block:
            - name: Check if rsyslog exists
              shell: systemctl list-unit-files --type=service | grep -q rsyslog
              register: rsyslog_config
              failed_when: rsyslog_config.rc == 2
            - block:
                - name: Forward logging to swift.log file
                  copy:
                    content: |
                      # Fix for https://bugs.launchpad.net/tripleo/+bug/1776180
                      local2.*                 /var/log/containers/swift/swift.log
                      &                        stop
                    dest: /etc/rsyslog.d/openstack-swift.conf
                  register: logconfig
                - name: Restart rsyslogd service after logging conf change
                  service: name=rsyslog state=restarted
                  when:
                    - logconfig is changed
              when:
                - rsyslog_config is changed
                - rsyslog_config.rc == 0
      update_tasks:
        - name: Check swift containers log folder/symlink exists
          stat:
            path: /var/log/containers/swift
          register: swift_log_link
        - name: Delete if symlink
          file:
            path: /var/log/containers/swift
            state: absent
          when: swift_log_link.stat.islnk is defined and swift_log_link.stat.islnk
      metadata_settings:
        get_attr: [TLSProxyBase, role_data, metadata_settings]
      fast_forward_upgrade_tasks:
        - when:
            - step|int == 0
            - release == 'ocata'
          block:
            - name: Check if swift-proxy or swift-object-expirer are deployed
              command: systemctl is-enabled --quiet "{{ item }}"
              ignore_errors: True
              register: swift_proxy_services_enabled_result
              with_items:
                - openstack-swift-proxy
                - openstack-swift-object-expirer
            - name: Set fact swift_proxy_services_enabled
              set_fact:
                swift_proxy_services_enabled: "{{ swift_proxy_services_enabled_result }}"
        - name: Stop swift-proxy and swift-object-expirer services
          service: name={{ item.item }} state=stopped enabled=no
          with_items: "{{ swift_proxy_services_enabled.results }}"
          when:
            - step|int == 1
            - release == 'ocata'
            - item.rc == 0
