class oci::swiftproxy_haproxy(
  $swiftproxy_hostname,
  $use_ssl,
  $haproxy_timeout_http_request,
  $haproxy_timeout_queue,
  $haproxy_timeout_connect,
  $haproxy_timeout_client,
  $haproxy_timeout_server,
  $haproxy_timeout_check,
  $machine_ip,
  $machine_hostname,
  $pass_haproxy_stats,
  $swift_proxy_haproxy_rate_limit,
  $swift_proxy_disable_rate_limit,
  $api_port,
  $net_whitelist,
  $all_swiftproxy,
  $all_swiftproxy_ip,
  $backend_server_type                      = 'swift', # can be 'swift' or 'radosgw'
  $swift_proxy_haproxy_custom_delay_trigger = 100,
  $swift_proxy_haproxy_custom_delay_ms      = 50,
) {
  if $use_ssl {
    file { "/etc/haproxy/ssl":
      ensure                  => directory,
      owner                   => 'root',
      mode                    => '0755',
      selinux_ignore_defaults => true,
      require                 => Package['haproxy'],
    }->
    file { "/etc/haproxy/ssl/private":
      ensure                  => directory,
      owner                   => 'root',
      mode                    => '0755',
      selinux_ignore_defaults => true,
    }->
    file { "/etc/haproxy/ssl/private/oci-pki-swiftproxy.pem":
      ensure                  => present,
      owner                   => "haproxy",
      source                  => "/etc/ssl/private/oci-pki-swiftproxy.pem",
      selinux_ignore_defaults => true,
      mode                    => '0600',
      notify                  => Service['haproxy'],
    }
  }

  package { 'haproxy-cmd':
    ensure => present,
  }

  if $facts['os']['lsb'] != undef{
    $mycodename = $facts['os']['lsb']['distcodename']
  }else{
    $mycodename = $facts['os']['distro']['codename']
  }

  if $mycodename == 'stretch' or $mycodename == 'buster' {
    # This used to work with haproxy 1.8,
    # let's see if we can get rid of it
    $haproxy_replace_path = 'reqrep'
    $haproxy_replace_head = 'rspirep'
    $use_haproxy_21_format = false
  }else{
    # This works with haproxy >= 2.1
    $haproxy_replace_path  = 'http-request replace-path'
    $haproxy_replace_head  = 'http-response replace-header'
    $use_haproxy_21_format = true
  }

  $delay = $swift_proxy_haproxy_custom_delay_ms
  file { '/etc/haproxy/custom_delay.lua':
    ensure  => file,
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
    content => template('oci/haproxy_custom_delay.erb'),
    notify  => Service['haproxy'],
  }
  class { 'haproxy':
    restart_command => 'systemctl reload haproxy',
    global_options   => {
      'tune.bufsize' =>  '32768',
      'log'      => '/dev/log local0',
      'chroot'  => '/var/lib/haproxy',
      'pidfile' => '/var/run/haproxy.pid',
      'maxconn'  => '40960',
      'user'     => 'haproxy',
      'group'    => 'haproxy',
      'daemon'   => '',
      'stats'   => [
        'socket /var/lib/haproxy/stats',
        'socket /var/lib/haproxy/admin.sock mode 600 level admin',
        'timeout 30s'
      ],
      'nbthread' => '8',
      'ssl-default-bind-ciphers'   => 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256',
      'ssl-default-bind-options'   => 'no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets',
      'ssl-default-server-ciphers' => 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256',
      'ssl-default-server-options' => 'no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets',
      'lua-load'                   => '/etc/haproxy/custom_delay.lua'
    },
    defaults_options => {
      'log'     => 'global',
      'mode'    => 'http',
      'retries' => '3',
      'maxconn' => '40960',
      'timeout' => [
        "http-request ${haproxy_timeout_http_request}",
        "queue ${haproxy_timeout_queue}",
        "connect ${haproxy_timeout_connect}",
        "client ${haproxy_timeout_client}",
        "server ${haproxy_timeout_server}",
        "check ${haproxy_timeout_check}",
      ],
      'option'  => [
        'httplog',
        'logasap',
        'accept-invalid-http-response',
       ],
    },
    merge_options => false,
  }->
  haproxy::listen { 'hapstats':
    section_name => 'statshttp',
    bind         => { "${machine_ip}:8088" => 'user root'},
    mode         => 'http',
    options      => {
        'stats' => [ 'uri /', "auth admin:${pass_haproxy_stats}", 'refresh 15', 'admin if TRUE', 'realm Haproxy Statistics', ],
    },
  }

  $swift_proxy_haproxy_rate_limit_real = $swift_proxy_haproxy_rate_limit * 10
  $swift_proxy_haproxy_custom_delay_trigger_real = $swift_proxy_haproxy_custom_delay_trigger * 10

  file { '/etc/haproxy/s3_blacklist.map':
      ensure  => 'present',
      replace => 'no', # Do not overwrite any manual change, as this file is for manual customization.
      content => '#Account_s3',
      mode    => '0644',
    }


    file { '/etc/haproxy/swift_blacklist.map':
      ensure  => 'present',
      replace => 'no', # Do not overwrite any manual change, as this file is for manual customization.
      content => '#Account_path_Swift exemple
/object/v1/AUTH_7060a6f3dfaf6a698553658dac2b512f',
    }

  # Global frontend for all services with dispatch depending on the URL
  if $swift_proxy_disable_rate_limit {
    haproxy::frontend { 'openstackfe':
      mode      => 'http',
      bind      => { ":::${api_port}" => ['v4v6 ssl', 'crt', '/etc/haproxy/ssl/private/oci-pki-swiftproxy.pem', 'crt', '/etc/haproxy/ssl/private/'] },
      options   => [

        { 'acl'         => 'url_swift path_beg -i /object'},
        { 'use_backend' => 'swiftbe if url_swift'},
        { 'use_backend' => 'swiftbe_always'},
        # Set HSTS (63072000 seconds = 2 years)
        { 'http-response' => 'set-header Strict-Transport-Security max-age=63072000'},
        { 'option'      => [
            'httplog',
            'logasap',
          ],
        }
      ]
    }
  } else {
    $haproxy_network_whitelist = $net_whitelist.join(' ')

    file { '/etc/haproxy/ratelimit_paths.map':
      ensure  => 'present',
      replace => 'no', # Do not overwrite any manual change, as this file is for manual customization.
      content => '# Example path for Swift API:
/object/v1/AUTH_7060a6f3dfaf6a698553658dac2b512f
# S3 example:
/default/Containername/
',
      mode    => '0644',
    }
    ~> file { '/etc/haproxy/ratelimit_bypass_paths.map':
      ensure  => 'present',
      replace => 'no', # Do not overwrite any manual change, as this file is for manual customization.
      content => '# Example path for Swift API:
#Path
AUTH_a148daf606a03800ddc401e830bc48f3/Containername
',
      mode    => '0644',
    }
    ~> haproxy::frontend { 'openstackfe':
      mode      => 'http',
      bind      => { ":::${api_port}" => ['v4v6 ssl', 'crt', '/etc/haproxy/ssl/private/oci-pki-swiftproxy.pem', 'crt', '/etc/haproxy/ssl/private/'] },
      options   => [
        {'acl'                       => "network_allowed src ${haproxy_network_whitelist}"},
        {'stick-table'               => 'type ip size 10k expire 30s store http_req_rate(10s)'},
        {'acl rate_limit_acl'        => 'path_beg -f /etc/haproxy/ratelimit_paths.map'},
        {'acl rate_limit_bypass_acl' => 'path_beg -f /etc/haproxy/ratelimit_bypass_paths.map'},
        {'acl s3_blacklist'          => 'req.hdrs -m sub -f /etc/haproxy/s3_blacklist.map'},
        {'acl swift_blacklist'          => 'path_beg -f /etc/haproxy/swift_blacklist.map'},
        {'http-request'              => 'track-sc0 src'},
        {'http-request'              => 'lua.delay_request if rate_limit_acl !network_allowed'},
        {'http-request'              => "lua.delay_request if { sc_http_req_rate(0) gt ${swift_proxy_haproxy_custom_delay_trigger_real} } !rate_limit_bypass_acl !network_allowed"},
        {'http-request'              => "deny deny_status 429 if { sc_http_req_rate(0) gt ${swift_proxy_haproxy_rate_limit_real} } !network_allowed"},
        {'http-request'              => "deny deny_status 503 content-type text/html string '<p>Backend S3 maintenance</p>' if s3_blacklist"},
        {'http-request'              => "deny deny_status 503 content-type text/html string '<p>Backend Swift maintenance</p>' if swift_blacklist"},
        { 'acl'                      => 'url_swift path_beg -i /object'},
        { 'use_backend'              => 'swiftbe if url_swift'},
        { 'use_backend'              => 'swiftbe_always'},
        # Set HSTS (63072000 seconds = 2 years)
        { 'http-response'            => 'set-header Strict-Transport-Security max-age=63072000'},
        { 'option'                   => [
            'httplog',
            'logasap',
          ],
        }
      ]
    }
  }

  if $backend_server_type == 'swift' {
    $backend_server_port = 8080
    # This backend + backend-member is for the /object version, as
    # advertized in our endpoints.
    if $use_haproxy_21_format {
      $tweak_path_object = '^/object/?(.*) /\1'
    }else{
      $tweak_path_object = '^([^\ ]*\ /)object[/]?(.*)     \1\2'
    }
    haproxy::backend { 'swiftbe':
      options => [
        { 'option'              => 'httpchk GET /healthcheck' },
        { 'option'              => 'forwardfor' },
        { 'mode'                => 'http' },
        { 'balance'             => 'source' },
        { $haproxy_replace_path => $tweak_path_object},
      ],
    }
    # This backend + backend-member is for without the /object version, as
    # per what will S3 clients use.
    haproxy::backend { 'swiftbe_always':
      options => [
        { 'option'  => 'httpchk GET /healthcheck' },
        { 'option'  => 'forwardfor' },
        { 'mode'    => 'http' },
        { 'balance' => 'source' },
      ],
    }
  } elsif $backend_server_type == 'radosgw' {
    $backend_server_port = 7480
    if $use_haproxy_21_format {
      $tweak_path_object = '^/object/?(.*) /swift/\1'
    }else{
      $tweak_path_object = '^([^\ ]*\ /)object[/]?(.*)     \1/swift\2'
    }
    haproxy::backend { 'swiftbe':
      options => [
        { 'option'               => 'httpchk' },
        { 'http-check'           => 'send meth HEAD' },
        { 'http-check'           => 'expect rstatus 200|405' },
        { 'option'               => 'forwardfor' },
        { 'mode'                 => 'http' },
        { 'balance'              => 'roundrobin' },
        { $haproxy_replace_path  => $tweak_path_object },
      ],
    }
    haproxy::backend { 'swiftbe_always':
      options => [
        { 'option'     => 'httpchk' },
        { 'http-check' => 'send meth HEAD' },
        { 'http-check' => 'expect rstatus 200|405' },
        { 'option'     => 'forwardfor' },
        { 'mode'       => 'http' },
        { 'balance'    => 'roundrobin' },
      ],
    }
  } else {
    fatal("unsupported backend_server_type '$backend_server_type'")
  }

  haproxy::balancermember { 'swiftbm':
    listening_service => 'swiftbe',
    ipaddresses       => [ $machine_ip ],
    server_names      => [ $machine_hostname ],
    ports             => $backend_server_port,
    options           => 'check',
  }

  $other_swift_proxies = $all_swiftproxy - $machine_hostname
  $other_swift_proxies_ip = $all_swiftproxy_ip - $machine_ip
  haproxy::balancermember { 'swiftbmbackup':
    listening_service => 'swiftbe',
    ipaddresses       => $other_swift_proxies_ip,
    server_names      => $other_swift_proxies,
    ports             => $backend_server_port,
    options           => "check backup",
  }

  haproxy::balancermember { 'swiftbm_always':
    listening_service => 'swiftbe_always',
    ipaddresses       => [ $machine_ip ],
    server_names      => [ $machine_hostname ],
    ports             => $backend_server_port,
    options           => 'check',
  }
  # Other swift if the local machine one fails.
  haproxy::balancermember { 'swiftbm_alwaysbackup':
    listening_service => 'swiftbe_always',
    ipaddresses       => $other_swift_proxies_ip,
    server_names      => $other_swift_proxies,
    ports             => $backend_server_port,
    options           => "check backup",
  }

  # update Rsyslog HAProxy priority to avoid log mess between HAProxy & Swift proxy
  exec { 'move-haproxy-rsyslog-priority':
    command => 'cp /etc/rsyslog.d/49-haproxy.conf /etc/rsyslog.d/15-haproxy.conf',
    onlyif  => 'test -e /etc/rsyslog.d/49-haproxy.conf',
    creates => '/etc/rsyslog.d/15-haproxy.conf',
    path    => ['/bin', '/usr/bin', '/sbin', '/usr/sbin'],
  }

  exec { 'remove-default-configuration':
    command => 'rm /etc/rsyslog.d/49-haproxy.conf',
    onlyif  => 'test -e /etc/rsyslog.d/49-haproxy.conf',
    path    => ['/bin', '/usr/bin', '/sbin', '/usr/sbin'],
    notify  => Service['rsyslog'],
  }
}
