{% import "caucase" as caucase with context %}
{%- set TRUE_VALUES = ['y', 'yes', '1', 'true'] -%}
{%- set HTTP3_PORT = instance_parameter_dict.get('configuration.http3-port', '443') %}
{%- if instance_parameter_dict.get('configuration.enable-http3', 'false').lower() in TRUE_VALUES %}
{%-   set FRONTEND_HTTP3 = True %}
{%- else %}
{%-   set FRONTEND_HTTP3 = False %}
{%- endif %}
{%- if FRONTEND_HTTP3 %}
{%-   set FRONTEND_HAPROXY_EXECUTABLE = software_parameter_dict['haproxy_quic_executable'] %}
{%- else %}
{%-   set FRONTEND_HAPROXY_EXECUTABLE = software_parameter_dict['haproxy_executable'] %}
{%- endif %}
{%- set BACKEND_HAPROXY_EXECUTABLE = software_parameter_dict['haproxy_executable'] %}
[buildout]
extends =
  {{ software_parameter_dict['profile_common'] }}
  {{ software_parameter_dict['profile_monitor'] }}
  {{ software_parameter_dict['profile_logrotate_base'] }}

parts =
  directory
  software-py
  switch-frontend-softwaretype
  caucase-updater
  caucase-updater-promise
  backend-client-caucase-updater
  backend-client-caucase-updater-promise
  promise-logrotate-setup

  trafficserver-launcher
  trafficserver-reload
  trafficserver-configuration-directory
  trafficserver-records-config
  trafficserver-remap-config
  trafficserver-plugin-config
  trafficserver-storage-config
  trafficserver-ip-allow-config
  trafficserver-logging-yaml
  trafficserver-promise-listen-port
  trafficserver-promise-cache-availability
  cron-entry-logrotate-trafficserver
## Monitor
  monitor-base
  monitor-ats-cache-stats-wrapper
  monitor-traffic-summary-last-stats-wrapper
  monitor-verify-re6st-connectivity

  frontend-haproxy-rsyslogd-configuration
  frontend-haproxy-rsyslogd
  logrotate-entry-frontend-haproxy
  frontend-haproxy
  frontend-haproxy-graceful
  promise-frontend-frontend-haproxy-configuration
  promise-frontend-haproxy-v4-https
  promise-frontend-haproxy-v4-http
  promise-frontend-haproxy-v6-https
  promise-frontend-haproxy-v6-http

  backend-haproxy-rsyslogd-configuration
  backend-haproxy-rsyslogd
  logrotate-entry-backend-haproxy
  backend-haproxy
  backend-haproxy-graceful
  promise-backend-haproxy-http
  promise-backend-haproxy-https
  promise-backend-haproxy-configuration

  slave-introspection-frontend
  slave-introspection-graceful
  promise-slave-introspection-https
  promise-slave-introspection-configuration
  logrotate-entry-slave-introspection
  backend-haproxy-statistic-frontend-promise

[software-py]
recipe = software

[frontend-node-id]
# Store id file in top of hierarchy, so it does not depend on directory creation
file = ${buildout:directory}/.frontend-node-id.txt
recipe = slapos.recipe.build
init =
  import os
  import secrets
  if not os.path.exists(options['file']):
    with open(options['file'], 'w') as fh:
      fh.write(secrets.token_urlsafe(4))
  with open(options['file'], 'r') as fh:
    options['value'] = fh.read()

[frontend-node-private-salt]
# Private, not communicated, stable hash, which can be used to salt other
# hashes, so their values are connected to the node, but practicaly impossible
# to crack (until the node is hacked itself, but then those values are
# stolen anyway)
recipe = slapos.recipe.build
init =
  import os
  import uuid
  if not os.path.exists(options['file']):
    with open(options['file'], 'w') as fh:
      fh.write(uuid.uuid4().hex)
  with open(options['file'], 'r') as fh:
    options['value'] = fh.read()
file = ${buildout:directory}/.frontend-node-private-salt.txt

[version-hash]
recipe = slapos.recipe.build
software-release-url = ${slap-connection:software-release-url}
hash-salt = ${frontend-node-private-salt:value}
init =
  import hashlib
  import base64
  options['value'] = base64.urlsafe_b64encode(hashlib.md5(''.join([options['software-release-url'].strip(), options['hash-salt']]).encode()).digest()).decode()

[frontend-node-information]
recipe = slapos.recipe.build
file = ${buildout:directory}/.frontend-node-information.json
node-id = ${frontend-node-id:value}
current-hash = ${version-hash:value}
current-software-release-url = ${version-hash:software-release-url}
init =
  import json
  changed = False
  try:
    with open(options['file'], 'r') as fh:
      data = json.load(fh)
  except Exception:
    changed = True
    data = {
      'node-id': options['node-id'],
      'version-hash-history': {options['current-hash']: options['current-software-release-url']}
    }
  if 'node-id' not in data:
    data['node-id'] = options['node-id']
    changed = True
  if 'version-hash-history' not in data:
    data['version-hash-history'] = {}
    changed = True
  if options['current-hash'] not in data['version-hash-history']:
    data['version-hash-history'][options['current-hash']] = options['current-software-release-url']
    changed = True
  if changed:
    with open(options['file'], 'w') as fh:
      json.dump(data, fh)
  options['value'] = data
# Create all needed directories
[directory]
recipe = slapos.cookbook:mkdirectory

bin = ${buildout:directory}/bin/
etc = ${buildout:directory}/etc/
srv = ${buildout:directory}/srv/
var = ${buildout:directory}/var/
tmp = ${:var}/tmp
template = ${buildout:directory}/template/

backup = ${:srv}/backup
log = ${:var}/log
run = ${:var}/run
backend-haproxy-rsyslogd-spool = ${:run}/backend-haproxy-rsyslogd-spool
frontend-haproxy-rsyslogd-spool = ${:run}/frontend-haproxy-rsyslogd-spool
service = ${:etc}/service
etc-run = ${:etc}/run

ca-dir = ${:srv}/ssl
backend-client-dir = ${:srv}/backend-client
# BBB: SlapOS Master non-zero knowledge BEGIN
bbb-ssl-dir = ${:srv}/bbb-ssl
# BBB: SlapOS Master non-zero knowledge END

frontend_cluster = ${:var}/frontend_cluster

# CSR publication
expose-csr = ${:srv}/expose-csr
expose-csr-etc = ${:etc}/expose-csr
expose-csr-var = ${:var}/expose-csr

# slave introspection
slave-introspection-var = ${:var}/slave-introspection

[switch-frontend-softwaretype]
recipe = slapos.cookbook:switch-softwaretype
default = dynamic-custom-personal-profile-slave-list:output
RootSoftwareInstance = ${:default}
single-default = dynamic-custom-personal-profile-slave-list:output
single-custom-personal = dynamic-custom-personal-profile-slave-list:output

[frontend-configuration]
ip-access-certificate = ${self-signed-ip-access:certificate}
slave-introspection-configuration = ${directory:etc}/slave-introspection-httpd-nginx.conf
slave-introspection-https-port = ${configuration:slave-introspection-https-port}
slave-introspection-secure_access = ${slave-introspection-frontend:connection-secure_access}

[self-signed-ip-access]
# Self Signed certificate for HTTPS IP accesses to the frontend
recipe = plone.recipe.command
update-command = ${:command}
ipv6 = ${slap-configuration:ipv6-random}
ipv4 = {{instance_parameter_dict['ipv4-random']}}
certificate = ${frontend-directory:master-autocert-dir}/ip-access-${:ipv6}-${:ipv4}.crt
{#- Can be stopped on error, as does not rely on self provided service #}
stop-on-error = True
command =
  [ -f ${:certificate} ] && exit 0
  rm -f ${:certificate}
  /bin/bash -c ' \
  {{ software_parameter_dict['openssl'] }} req \
     -new -newkey rsa:2048 -sha256 \
     -nodes -x509 -days 36500 \
     -keyout ${:certificate} \
     -subj "/CN=Self Signed IP Access" \
     -reqexts SAN \
     -extensions SAN \
     -config <(cat {{ software_parameter_dict['openssl_cnf'] }} \
         <(printf "\n[SAN]\nsubjectAltName=IP:${:ipv6},IP:${:ipv4}")) \
     -out ${:certificate}'

[self-signed-fallback-access]
# Self Signed certificate for HTTPS access to the frontend with fallback certificate
recipe = plone.recipe.command
update-command = ${:command}
ipv6 = ${slap-configuration:ipv6-random}
ipv4 = {{instance_parameter_dict['ipv4-random']}}
certificate = ${frontend-directory:master-autocert-dir}/fallback-access.crt
{#- Can be stopped on error, as does not rely on self provided service #}
stop-on-error = True
command =
  [ -f ${:certificate} ] && exit 0
  rm -f ${:certificate}
  /bin/bash -c ' \
  {{ software_parameter_dict['openssl'] }} req \
     -new -newkey rsa:2048 -sha256 \
     -nodes -x509 -days 36500 \
     -keyout ${:certificate} \
     -subj "/CN=Fallback certificate/OU={{ instance_parameter_dict['configuration.frontend-name'] }}" \
     -out ${:certificate}'

[jinja2-template-base]
recipe = slapos.recipe.template:jinja2
output = ${buildout:directory}/${:filename}
extensions = jinja2.ext.do
extra-context =
slapparameter_dict = {{ dumps(slapparameter_dict) }}
slap_software_type = {{ dumps(instance_parameter_dict['slap-software-type']) }}
context =
    import json_module json
    raw profile_common {{ software_parameter_dict['profile_common'] }}
    raw profile_logrotate_base {{ software_parameter_dict['profile_logrotate_base'] }}
    raw profile_monitor {{ software_parameter_dict['profile_monitor'] }}
    key slap_software_type :slap_software_type
    key slapparameter_dict :slapparameter_dict
    section directory directory
    ${:extra-context}

[software-release-path]
template-empty = {{ software_parameter_dict['template_empty'] }}
template-frontend-haproxy-configuration = {{ software_parameter_dict['template_frontend_haproxy_configuration'] }}
template-frontend-haproxy-crt-list = {{ software_parameter_dict['template_frontend_haproxy_crt_list'] }}
template-frontend-haproxy-rsyslogd-conf = {{ software_parameter_dict['template_frontend_haproxy_rsyslogd_conf'] }}
template-backend-haproxy-configuration = {{ software_parameter_dict['template_backend_haproxy_configuration'] }}
template-backend-haproxy-rsyslogd-conf = {{ software_parameter_dict['template_backend_haproxy_rsyslogd_conf'] }}
template-expose-csr-nginx-conf = {{ software_parameter_dict['template_expose_csr_nginx_conf'] }}

[kedifa-login-config]
d = ${directory:ca-dir}
template-csr = ${:d}/kedifa-login-template-csr.pem
key = ${:d}/kedifa-login-certificate.pem
certificate = ${:key}
ca-certificate = ${:d}/kedifa-caucase-ca.pem
cas-ca-certificate = ${:d}/kedifa-cas-caucase-ca.pem
crl = ${:d}/kedifa-login-crl.pem

[kedifa-login-csr]
recipe = plone.recipe.command
organization = {{ slapparameter_dict['cluster-identification'] }}
organizational_unit = {{ instance_parameter_dict['configuration.frontend-name'] }}
command =
{% if slapparameter_dict['kedifa-caucase-url'] %}
  if [ ! -f ${:template-csr} ] && [ ! -f ${:key} ]  ; then
    {{ software_parameter_dict['openssl'] }} req -new -sha256 \
      -newkey rsa:2048 -nodes -keyout ${:key} \
      -subj "/O=${:organization}/OU=${:organizational_unit}" \
      -out ${:template-csr}
  fi
{% endif %}
  test -f ${:key} && test -f ${:template-csr}
update-command = ${:command}
template-csr = ${kedifa-login-config:template-csr}
key = ${kedifa-login-config:key}
{#- Can be stopped on error, as does not rely on self provided service #}
stop-on-error = True

{{ caucase.updater(
     prefix='caucase-updater',
     buildout_bin_directory=software_parameter_dict['bin_directory'],
     updater_path='${directory:service}/kedifa-login-certificate-caucase-updater',
     url=slapparameter_dict['kedifa-caucase-url'],
     data_dir='${directory:srv}/caucase-updater',
     crt_path='${kedifa-login-config:certificate}',
     ca_path='${kedifa-login-config:ca-certificate}',
     crl_path='${kedifa-login-config:crl}',
     key_path='${kedifa-login-csr:key}',
     template_csr='${kedifa-login-csr:template-csr}'
)}}

[kedifa-configuration]
caucase-url = {{ slapparameter_dict['kedifa-caucase-url'] }}
ca-certificate = ${kedifa-login-config:ca-certificate}
certificate = ${kedifa-login-config:certificate}
cas-ca-certificate = ${kedifa-login-config:cas-ca-certificate}
csr = ${caucase-updater-csr:csr}
crl = ${kedifa-login-config:crl}
kedifa-updater-mapping-file = ${directory:etc}/kedifa_updater_mapping.txt
kedifa-updater-state-file = ${directory:srv}/kedifa_updater_state.json
slave_kedifa_information = {{ dumps(slapparameter_dict['slave-kedifa-information']) }}

[backend-client-login-config]
d = ${directory:backend-client-dir}
template-csr = ${:d}/csr.pem
key = ${:d}/certificate.pem
certificate = ${:key}
ca-certificate = ${:d}/ca.pem
cas-ca-certificate = ${:d}/cas-ca.pem
crl = ${:d}/crl.pem

[backend-client-login-csr]
recipe = plone.recipe.command
organization = {{ slapparameter_dict['cluster-identification'] }}
organizational_unit = {{ instance_parameter_dict['configuration.frontend-name'] }}
command =
{% if slapparameter_dict['backend-client-caucase-url'] %}
  if [ ! -f ${:template-csr} ] && [ ! -f ${:key} ]  ; then
    {{ software_parameter_dict['openssl'] }} req -new -sha256 \
      -newkey rsa:2048 -nodes -keyout ${:key} \
      -subj "/O=${:organization}/OU=${:organizational_unit}" \
      -out ${:template-csr}
  fi
{% endif %}
  test -f ${:key} && test -f ${:template-csr}
update-command = ${:command}
template-csr = ${backend-client-login-config:template-csr}
key = ${backend-client-login-config:key}
{#- Can be stopped on error, as does not rely on self provided service #}
stop-on-error = True

{{ caucase.updater(
     prefix='backend-client-caucase-updater',
     buildout_bin_directory=software_parameter_dict['bin_directory'],
     updater_path='${directory:service}/backend-client-login-certificate-caucase-updater',
     url=slapparameter_dict['backend-client-caucase-url'],
     data_dir='${directory:srv}/backend-client-caucase-updater',
     crt_path='${backend-client-login-config:certificate}',
     ca_path='${backend-client-login-config:ca-certificate}',
     crl_path='${backend-client-login-config:crl}',
     key_path='${backend-client-login-csr:key}',
     template_csr='${backend-client-login-csr:template-csr}'
)}}

[dynamic-custom-personal-profile-slave-list]
< = jinja2-template-base
depends = ${software-py:recipe}
url = {{ software_parameter_dict['profile_slave_list'] }}
filename = instance-slave-list.cfg
master_key_download_url = {{ dumps(slapparameter_dict['master-key-download-url']) }}
software_type = single-custom-personal
organization = {{ slapparameter_dict['cluster-identification'] }}
organizational-unit = {{ instance_parameter_dict['configuration.frontend-name'] }}
backend-client-caucase-url = {{ slapparameter_dict['backend-client-caucase-url'] }}
url-ready-file = ${directory:var}/url-ready.txt
http3-enable = {{ FRONTEND_HTTP3 }}
extra-context =
    key backend_client_caucase_url :backend-client-caucase-url
    import furl_module furl
    import urllib_module urllib
    import operator_module operator
    key master_key_download_url :master_key_download_url
    key url_ready_file :url-ready-file
    key expose_csr_organization :organization
    key expose_csr_organizational_unit :organizational-unit
    key global_ipv6 slap-configuration:ipv6-random
    key empty_template software-release-path:template-empty
    key template_expose_csr_nginx_conf software-release-path:template-expose-csr-nginx-conf
    key software_type :software_type
    key http3_enable :http3-enable
    key frontend_lazy_graceful_reload frontend-haproxy-lazy-graceful:output
    key monitor_base_url monitor-instance-parameter:monitor-base-url
    key node_id frontend-node-id:value
    key version_hash version-hash:value
    key software_release_url version-hash:software-release-url
    key node_information frontend-node-information:value
# BBB: SlapOS Master non-zero knowledge BEGIN
    key apache_certificate apache-certificate:output
# BBB: SlapOS Master non-zero knowledge END
    key custom_ssl_directory frontend-directory:custom-ssl-directory
## frontend haproxy
    key template_frontend_haproxy_configuration software-release-path:template-frontend-haproxy-configuration
    key template_frontend_haproxy_crt_list software-release-path:template-frontend-haproxy-crt-list
## backend haproxy
    key template_backend_haproxy_configuration software-release-path:template-backend-haproxy-configuration
## Configuration passed by section
    section frontend_directory frontend-directory
    section configuration configuration
    section frontend_haproxy_configuration frontend-haproxy-configuration
    section backend_haproxy_configuration backend-haproxy-configuration
    section instance_parameter_dict instance-parameter-section
    section frontend_configuration frontend-configuration
    section kedifa_configuration kedifa-configuration
    section software_parameter_dict software-parameter-section

# Deploy frontend with Jinja power
[frontend-haproxy-rsyslogd-config]
log-socket = ${directory:run}/fhlog.sck
log-file = ${directory:log}/frontend-haproxy.log
pid-file = ${directory:run}/frontend-haproxy-rsyslogd.pid
spool-directory = ${directory:frontend-haproxy-rsyslogd-spool}
graceful-command = kill -HUP $(cat ${:pid-file})
slave-log-directory = ${frontend-directory:slave-log}

[frontend-haproxy-rsyslogd-configuration]
<= jinja2-template-base
url = ${software-release-path:template-frontend-haproxy-rsyslogd-conf}
output = ${directory:etc}/frontend-haproxy-rsyslogd.conf
local_ipv4 = {{ dumps(instance_parameter_dict['ipv4-random']) }}

extra-context =
    key instance_home buildout:directory
    key master_certificate frontend-haproxy-configuration:master-certificate
    key access_log frontend-haproxy-configuration:access-log
    key slave_configuration_directory frontend-directory:slave-configuration
    section frontend_configuration frontend-configuration
    key http_port configuration:plain_http_port
    key https_port configuration:port
    key global_ipv6 slap-configuration:ipv6-random
    key local_ipv4 :local_ipv4
    key error_log frontend-haproxy-configuration:error-log
    key username monitor-instance-parameter:username
    key password monitor-htpasswd:passwd
# BBB: SlapOS Master non-zero knowledge BEGIN
    key apache_certificate apache-certificate:output
# BBB: SlapOS Master non-zero knowledge END
    section configuration frontend-haproxy-rsyslogd-config

[frontend-haproxy-rsyslogd]
recipe = slapos.cookbook:wrapper
command-line = {{ software_parameter_dict['rsyslogd_executable'] }} -i ${frontend-haproxy-rsyslogd-config:pid-file} -n -f ${frontend-haproxy-rsyslogd-configuration:output}
wrapper-path = ${directory:service}/frontend-haproxy-rsyslogd
hash-existing-files = ${buildout:directory}/software_release/buildout.cfg

[logrotate-entry-frontend-haproxy]
<= logrotate-entry-base
name = frontend-haproxy
log = ${frontend-haproxy-rsyslogd-config:log-file}
rotate-num = ${configuration:rotate-num}
# Note: Slaves do not define their own reload, as this would be repeated,
#       because sharedscripts work per entry, and each slave needs its own
#       olddir
#       Here we trust that there will be something to be rotated with error
#       or access log, and that this will trigger postrotate script.
post = ${frontend-haproxy-rsyslogd-lazy-graceful:output} &
delaycompress =

[frontend-haproxy-configuration]
file = ${directory:etc}/frontend-haproxy.cfg
crt-list = ${directory:etc}/frontend-haproxy-crt-list.txt
log-socket = ${frontend-haproxy-rsyslogd-config:log-socket}
access-log = ${directory:log}/frontend-access.log
error-log = ${directory:log}/frontend-error.log
pid-file = ${directory:run}/httpd.pid
frontend-graceful-command = ${frontend-haproxy-validate:output} && kill -USR2 $(cat ${:pid-file})
not-found-file = {{ software_parameter_dict['template_not_found_html'] }}
master-certificate = ${frontend-directory:master-autocert-dir}/master.pem
self-signed-fallback-certificate = ${self-signed-fallback-access:certificate}
http-port = ${configuration:plain_http_port}
https-port = ${configuration:port}
# Communication with ATS
cache-port = ${trafficserver-variable:input-port}
# slave instrspection
slave-introspection-access-log = ${directory:log}/slave-introspection-access.log
slave-introspection-error-log = ${directory:log}/slave-introspection-error.log
slave-introspection-pid-file = ${directory:run}/slave-introspection.pid
slave-introspection-graceful-command = ${slave-introspection-validate:output} && kill -HUP $(cat ${:slave-introspection-pid-file})
local_ipv4 = {{ dumps(instance_parameter_dict['ipv4-random']) }}
version-hash = ${version-hash:value}
node-id = ${frontend-node-id:value}
http3-enable = {{ FRONTEND_HTTP3 }}
http3-port = {{ HTTP3_PORT }}

# BBB: SlapOS Master non-zero knowledge BEGIN
[get-self-signed-fallback-access]
recipe = slapos.recipe.build
certificate-file = ${self-signed-fallback-access:certificate}
init =
  import os
  options['certificate'] = ''
  if os.path.exists(options['certificate-file']):
    with open(options['certificate-file'], 'r') as fh:
      options['certificate'] = fh.read()

[apache-certificate]
recipe = slapos.recipe.template:jinja2
inline =
{% raw %}
  {{ certificate or fallback_certificate }}
  {{ key or '' }}
{% endraw %}
context =
  key certificate configuration:apache-certificate
  key key configuration:apache-key
  key fallback_certificate get-self-signed-fallback-access:certificate
output = ${directory:bbb-ssl-dir}/frontend.crt
# BBB: SlapOS Master non-zero knowledge END

[frontend-directory]
recipe = slapos.cookbook:mkdirectory
slave-configuration = ${directory:etc}/frontend-haproxy.d/
slave-log = ${directory:log}/httpd
autocert = ${directory:srv}/autocert
master-autocert-dir = ${:autocert}/master-autocert
custom-ssl-directory = ${:slave-configuration}/ssl

#################
# Trafficserver
#################
[trafficserver-directory]
recipe = slapos.cookbook:mkdirectory
configuration = ${directory:etc}/trafficserver
local-state = ${directory:var}/trafficserver
bin_path = {{ software_parameter_dict['trafficserver'] }}/bin
log = ${directory:log}/trafficserver
cache-path = ${directory:srv}/ats_cache
logrotate-backup = ${logrotate-directory:logrotate-backup}/trafficserver

[trafficserver-variable]
wrapper-path = ${directory:service}/trafficserver
reload-path = ${directory:etc-run}/trafficserver-reload
local-ip = {{ instance_parameter_dict['ipv4-random'] }}
input-port = 23432
hostname = ${configuration:frontend-name}
plugin-config =
ip-allow-config = src_ip=0.0.0.0-255.255.255.255 action=ip_allow
cache-path = ${trafficserver-directory:cache-path}
disk-cache-size = ${configuration:disk-cache-size}
ram-cache-size = ${configuration:ram-cache-size}
templates-dir = {{ software_parameter_dict['trafficserver'] }}/etc/trafficserver/body_factory
request-timeout = ${configuration:request-timeout}
version-hash = ${version-hash:value}
node-id = ${frontend-node-id:value}

[trafficserver-configuration-directory]
recipe = plone.recipe.command
command = cp -rn {{ software_parameter_dict['trafficserver'] }}/etc/trafficserver/* ${:target}
target = ${trafficserver-directory:configuration}

[trafficserver-launcher]
recipe = slapos.cookbook:wrapper
command-line = {{ software_parameter_dict['trafficserver'] }}/bin/traffic_manager
wrapper-path = ${trafficserver-variable:wrapper-path}
environment =
  TS_ROOT=${buildout:directory}
  PROXY_CONFIG_CONFIG_DIR=${trafficserver-directory:configuration}
hash-existing-files = ${buildout:directory}/software_release/buildout.cfg

[trafficserver-reload]
recipe = slapos.cookbook:wrapper
command-line = {{ software_parameter_dict['trafficserver'] }}/bin/traffic_ctl config reload
wrapper-path = ${trafficserver-variable:reload-path}
environment = TS_ROOT=${buildout:directory}

# XXX Dedicated Jinja Section without slapparameter
[trafficserver-jinja2-template-base]
recipe = slapos.recipe.template:jinja2
output = ${trafficserver-directory:configuration}/${:filename}
extra-context =
context =
    section ats_directory trafficserver-directory
    section ats_configuration trafficserver-variable
    ${:extra-context}

[trafficserver-records-config]
< = trafficserver-jinja2-template-base
url = {{ software_parameter_dict['template_trafficserver_records_config'] }}
filename = records.config
extra-context =
    import os_module os

[trafficserver-storage-config]
< = trafficserver-jinja2-template-base
url = {{ software_parameter_dict['template_trafficserver_storage_config'] }}
filename = storage.config

[trafficserver-logging-yaml]
< = trafficserver-jinja2-template-base
url = {{ software_parameter_dict['template_trafficserver_logging_yaml'] }}
filename = logging.yaml

[trafficserver-remap-config]
<= trafficserver-jinja2-template-base
{%- raw %}
inline =
  map /HTTPS/ http://{{ ipv4 }}:{{ https_port }}
  map /HTTP/ http://{{ ipv4 }}:{{ http_port }}
{%- endraw %}
extra-context =
  raw ipv4 {{ instance_parameter_dict['ipv4-random'] }}
  key https_port backend-haproxy-configuration:https-port
  key http_port backend-haproxy-configuration:http-port

filename = remap.config

[trafficserver-plugin-config]
< = trafficserver-jinja2-template-base
url = {{ software_parameter_dict['template_empty'] }}
filename = plugin.config
context =
    key content trafficserver-variable:plugin-config

[trafficserver-ip-allow-config]
< = trafficserver-jinja2-template-base
url = {{ software_parameter_dict['template_empty'] }}
filename = ip_allow.config
context =
    key content trafficserver-variable:ip-allow-config

[trafficserver-promise-listen-port]
<= monitor-promise-base
promise = check_socket_listening
name = trafficserver-port-listening.py
config-host = ${trafficserver-variable:local-ip}
config-port = ${trafficserver-variable:input-port}

[trafficserver-ctl]
recipe = slapos.cookbook:wrapper
command-line = {{ software_parameter_dict['trafficserver'] }}/bin/traffic_ctl
wrapper-path = ${directory:bin}/traffic_ctl
environment = TS_ROOT=${buildout:directory}

[trafficserver-promise-cache-availability]
<= monitor-promise-base
promise = trafficserver_cache_availability
name = trafficserver-cache-availability.py
config-wrapper-path = ${trafficserver-ctl:wrapper-path}

[trafficserver-rotate-script]
< = jinja2-template-base
url = {{ software_parameter_dict['template_rotate_script'] }}
output = ${directory:bin}/trafficserver-rotate
xz_binary = {{ software_parameter_dict['xz_location'] ~ '/bin/xz' }}
pattern = *.old
# days to keep log files
keep_days = 365

extra-context =
  raw find_executable {{ software_parameter_dict['findutils'] }}/bin/find
  key log_dir trafficserver-directory:log
  key rotate_dir trafficserver-directory:logrotate-backup
  key xz_binary :xz_binary
  key keep_days :keep_days
  key pattern :pattern

[cron-entry-logrotate-trafficserver]
recipe = slapos.cookbook:cron.d
cron-entries = ${directory:etc}/cron.d
name = trafficserver-logrotate
frequency = 0 0 * * *
command = ${trafficserver-rotate-script:output}

### End of ATS sections

### Frontend Graceful and promises
[frontend-haproxy-configuration-state]
< = jinja2-template-base
url = {{ software_parameter_dict['template_configuration_state_script'] }}
output = ${directory:bin}/${:_buildout_section_name_}

path_list = ${frontend-haproxy-configuration:file} ${frontend-haproxy-configuration:crt-list} ${frontend-directory:master-autocert-dir}/*.key ${frontend-directory:master-autocert-dir}/*.crt ${frontend-directory:master-autocert-dir}/*.pem ${frontend-directory:autocert}/*.pem ${frontend-directory:custom-ssl-directory}/*.proxy_ca_crt ${directory:bbb-ssl-dir}/*.crt
sha256sum = {{ software_parameter_dict['sha256sum'] }}

extra-context =
    import os_module os
    raw find_executable {{ software_parameter_dict['findutils'] }}/bin/find
    key path_list :path_list
    key sha256sum :sha256sum
    key signature_file :signature_file

[frontend-haproxy-configuration-state-graceful]
< = frontend-haproxy-configuration-state
signature_file = ${directory:run}/graceful_configuration_state_signature

[frontend-haproxy-configuration-state-validate]
< = frontend-haproxy-configuration-state
signature_file = ${directory:run}/validate_configuration_state_signature

[frontend-haproxy-graceful]
< = jinja2-template-base
url = {{ software_parameter_dict['template_graceful_script'] }}
output = ${directory:etc-run}/frontend-haproxy-safe-graceful
mode = 0700
extra-context =
    key graceful_reload_command frontend-haproxy-configuration:frontend-graceful-command
    key configuration_state frontend-haproxy-configuration-state-graceful:output

[frontend-haproxy-validate]
< = jinja2-template-base
url = {{ software_parameter_dict['template_validate_script'] }}
output = ${directory:bin}/frontend-haproxy-validate
mode = 0700
last_state_file = ${directory:run}/frontend_haproxy_configuration_last_state
validate_command = {{ FRONTEND_HAPROXY_EXECUTABLE }} -f ${frontend-haproxy-configuration:file} -c
extra-context =
    raw find_executable {{ software_parameter_dict['findutils'] }}/bin/find
    key validate_command :validate_command
    key configuration_state_command frontend-haproxy-configuration-state-validate:output
    key last_state_file :last_state_file

[backend-haproxy-validate]
<= jinja2-template-base
url = {{ software_parameter_dict['template_validate_script'] }}
output = ${directory:bin}/backend-haproxy-validate
mode = 0700
last_state_file = ${directory:run}/backend_haproxy_configuration_last_state
validate_command = {{ BACKEND_HAPROXY_EXECUTABLE }} -f ${backend-haproxy-configuration:file} -c
extra-context =
    raw find_executable {{ software_parameter_dict['findutils'] }}/bin/find
    key validate_command :validate_command
    key configuration_state_command backend-haproxy-configuration-state-validate:output
    key last_state_file :last_state_file

[frontend-haproxy-lazy-graceful]
< = jinja2-template-base
url = {{ software_parameter_dict['template_lazy_script_call'] }}
output = ${directory:bin}/frontend-haproxy-lazy-graceful
mode = 0700
pid-file = ${directory:run}/lazy-graceful.pid
wait_time = 60
extra-context =
    key pid_file :pid-file
    key wait_time :wait_time
    key lazy_command frontend-haproxy-configuration:frontend-graceful-command

# Promises checking configuration:
[promise-helper-last-configuration-state]
< = jinja2-template-base
url = {{ software_parameter_dict['template_empty'] }}
output = ${directory:bin}/frontend-read-last-configuration-state
content =
  #!/bin/sh
  exit `cat ${frontend-haproxy-validate:last_state_file}`
context =
    key content :content

[promise-frontend-frontend-haproxy-configuration]
<= monitor-promise-base
promise = validate_frontend_configuration
name = frontend-frontend-haproxy-configuration-promise.py
config-verification-script = ${promise-helper-last-configuration-state:output}

[promise-frontend-haproxy-v4-https]
<= monitor-promise-base
promise = check_socket_listening
name = frontend_haproxy_ipv4_https.py
config-host = {{ instance_parameter_dict['ipv4-random'] }}
config-port = ${configuration:port}

[promise-frontend-haproxy-v4-http]
<= monitor-promise-base
promise = check_socket_listening
name = frontend_haproxy_ipv4_http.py
config-host = {{ instance_parameter_dict['ipv4-random'] }}
config-port = ${configuration:plain_http_port}

[promise-frontend-haproxy-v6-https]
<= monitor-promise-base
promise = check_socket_listening
name = frontend_haproxy_ipv6_https.py
config-host = {{ instance_parameter_dict['ipv6-random'] }}
config-port = ${configuration:port}

[promise-frontend-haproxy-v6-http]
<= monitor-promise-base
promise = check_socket_listening
name = frontend_haproxy_ipv6_http.py
config-host = {{ instance_parameter_dict['ipv6-random'] }}
config-port = ${configuration:plain_http_port}

[promise-backend-haproxy-http]
<= monitor-promise-base
promise = check_socket_listening
name = backend_haproxy_http.py
config-host = {{ instance_parameter_dict['ipv4-random'] }}
config-port = ${backend-haproxy-configuration:http-port}

[promise-backend-haproxy-https]
<= monitor-promise-base
promise = check_socket_listening
name = backend_haproxy_https.py
config-host = {{ instance_parameter_dict['ipv4-random'] }}
config-port = ${backend-haproxy-configuration:https-port}

[backend-haproxy-configuration]
file = ${directory:etc}/backend-haproxy.cfg
pid-file = ${directory:run}/backend-haproxy.pid
log-socket = ${backend-haproxy-rsyslogd-config:log-socket}
graceful-command = ${backend-haproxy-validate:output} && kill -USR2 $(cat ${:pid-file})
http-port = ${configuration:backend-haproxy-http-port}
https-port = ${configuration:backend-haproxy-https-port}
# Caucase related configuration
caucase-url = {{ slapparameter_dict['backend-client-caucase-url'] }}
ca-certificate = ${backend-client-login-config:ca-certificate}
certificate = ${backend-client-login-config:certificate}
cas-ca-certificate = ${backend-client-login-config:cas-ca-certificate}
csr = ${backend-client-caucase-updater-csr:csr}
crl = ${backend-client-login-config:crl}
# the statistic page
statistic-certificate = ${self-signed-ip-access:certificate}
statistic-port = ${configuration:backend-haproxy-statistic-port}
statistic-username = ${monitor-instance-parameter:username}
statistic-password = ${monitor-htpasswd:passwd}
statistic-identification = {{ instance_parameter_dict['configuration.frontend-name'] + ' @ ' + slapparameter_dict['cluster-identification'] }}
statistic-frontend-secure_access = ${backend-haproxy-statistic-frontend:connection-secure_access}
version-hash = ${version-hash:value}
node-id = ${frontend-node-id:value}

[frontend-haproxy]
recipe = slapos.cookbook:wrapper
command-line = {{ FRONTEND_HAPROXY_EXECUTABLE }} -f ${frontend-haproxy-configuration:file}
wrapper-path = ${directory:service}/frontend-haproxy
hash-existing-files = ${buildout:directory}/software_release/buildout.cfg

[frontend-haproxy-rsyslogd-lazy-graceful]
< = jinja2-template-base
url = {{ software_parameter_dict['template_lazy_script_call'] }}
output = ${directory:bin}/frontend-haproxy-rsyslogd-lazy-graceful
mode = 0700
pid-file = ${directory:run}/frontend-haproxy-rsyslogd-lazy-graceful.pid
wait_time = 60
extra-context =
    key pid_file :pid-file
    key wait_time :wait_time
    key lazy_command frontend-haproxy-rsyslogd-config:graceful-command

[backend-haproxy]
recipe = slapos.cookbook:wrapper
command-line = {{ BACKEND_HAPROXY_EXECUTABLE }} -f ${backend-haproxy-configuration:file}
wrapper-path = ${directory:service}/backend-haproxy
hash-existing-files = ${buildout:directory}/software_release/buildout.cfg

[backend-haproxy-rsyslogd-lazy-graceful]
< = jinja2-template-base
url = {{ software_parameter_dict['template_lazy_script_call'] }}
output = ${directory:bin}/backend-haproxy-rsyslogd-lazy-graceful
pid-file = ${directory:run}/backend-haproxy-rsyslogd-lazy-graceful.pid
wait_time = 60
extra-context =
    key pid_file :pid-file
    key wait_time :wait_time
    key lazy_command backend-haproxy-rsyslogd-config:graceful-command

[logrotate-entry-backend-haproxy]
<= logrotate-entry-base
name = backend-haproxy
log = ${backend-haproxy-rsyslogd-config:log-file}
rotate-num = ${configuration:rotate-num}
# Note: Slaves do not define their own reload, as this would be repeated,
#       because sharedscripts work per entry, and each slave needs its own
#       olddir
#       Here we trust that there will be something to be rotated with error
#       or access log, and that this will trigger postrotate script.
post = ${backend-haproxy-rsyslogd-lazy-graceful:output} &
delaycompress =

[backend-haproxy-configuration-state]
<= jinja2-template-base
url = {{ software_parameter_dict['template_configuration_state_script'] }}
output = ${directory:bin}/${:_buildout_section_name_}

path_list = ${backend-haproxy-configuration:file} ${backend-client-login-config:certificate}
sha256sum = {{ software_parameter_dict['sha256sum'] }}

extra-context =
    import os_module os
    raw find_executable {{ software_parameter_dict['findutils'] }}/bin/find
    key path_list :path_list
    key sha256sum :sha256sum
    key signature_file :signature_file

[backend-haproxy-configuration-state-graceful]
<= backend-haproxy-configuration-state
signature_file = ${directory:run}/backend_haproxy_graceful_configuration_state_signature

[backend-haproxy-configuration-state-validate]
<= backend-haproxy-configuration-state
signature_file = ${directory:run}/backend_haproxy_validate_configuration_state_signature

[backend-haproxy-graceful]
< = jinja2-template-base
url = {{ software_parameter_dict['template_graceful_script'] }}
output = ${directory:etc-run}/backend-haproxy-safe-graceful

extra-context =
    key graceful_reload_command backend-haproxy-configuration:graceful-command
    key configuration_state backend-haproxy-configuration-state-graceful:output

[backend-haproxy-validate]
<= jinja2-template-base
url = {{ software_parameter_dict['template_validate_script'] }}
output = ${directory:bin}/backend-haproxy-validate
last_state_file = ${directory:run}/backend_haproxy_configuration_last_state
validate_command = {{ BACKEND_HAPROXY_EXECUTABLE }} -f ${backend-haproxy-configuration:file} -c
extra-context =
    raw find_executable {{ software_parameter_dict['findutils'] }}/bin/find
    key validate_command :validate_command
    key configuration_state_command backend-haproxy-configuration-state-validate:output
    key last_state_file :last_state_file

[promise-backend-haproxy-configuration]
<= monitor-promise-base
promise = validate_frontend_configuration
name = backend-haproxy-configuration.py
config-verification-script = ${promise-backend-haproxy-configuration-helper:output}

[promise-backend-haproxy-configuration-helper]
< = jinja2-template-base
url = {{ software_parameter_dict['template_empty'] }}
output = ${directory:bin}/backend-haproxy-read-last-configuration-state
content =
  #!/bin/sh
  exit `cat ${backend-haproxy-validate:last_state_file}`
context =
    key content :content

[backend-haproxy-rsyslogd-config]
log-socket = ${directory:run}/bhlog.sck
log-file = ${directory:log}/backend-haproxy.log
pid-file = ${directory:run}/backend-haproxy-rsyslogd.pid
spool-directory = ${directory:backend-haproxy-rsyslogd-spool}
graceful-command = kill -HUP $(cat ${:pid-file})
log-directory = ${frontend-directory:slave-log}

[backend-haproxy-rsyslogd-configuration]
<= jinja2-template-base
url = ${software-release-path:template-backend-haproxy-rsyslogd-conf}
output = ${directory:etc}/backend-haproxy-rsyslogd.conf
extra-context =
  section configuration backend-haproxy-rsyslogd-config

[backend-haproxy-rsyslogd]
recipe = slapos.cookbook:wrapper
command-line = {{ software_parameter_dict['rsyslogd_executable'] }} -i ${backend-haproxy-rsyslogd-config:pid-file} -n -f ${backend-haproxy-rsyslogd-configuration:output}
wrapper-path = ${directory:service}/backend-haproxy-rsyslogd
hash-existing-files = ${buildout:directory}/software_release/buildout.cfg

#######
# Monitoring sections
#

[monitor-instance-parameter]
# Note: Workaround for monitor stack, which uses monitor-httpd-port parameter
#       directly, and in our case it can come from the network, thus resulting
#       with need to strip !py!'u'
monitor-httpd-port = {{ instance_parameter_dict['configuration.monitor-httpd-port'] | int }}
password = {{ instance_parameter_dict['configuration.monitor-password'] | string }}

[monitor-conf-parameters]
private-path-list +=
  ${logrotate-directory:logrotate-backup}


[monitor-traffic-summary-last-stats-wrapper]
< = jinja2-template-base
url = {{ software_parameter_dict['template_wrapper'] }}
output = ${directory:bin}/traffic-summary-last-stats_every_1_hour
command = export TS_ROOT=${buildout:directory} && echo "<pre>$({{ software_parameter_dict['trafficserver'] }}/bin/traffic_logstats -f ${trafficserver-directory:log}/squid.blog)</pre>"
extra-context =
  key content monitor-traffic-summary-last-stats-wrapper:command

# Produce ATS Cache stats
[monitor-ats-cache-stats-wrapper]
< = jinja2-template-base
url = {{ software_parameter_dict['template_wrapper'] }}
output = ${directory:bin}/ats-cache-stats_every_1_hour
command = export TS_ROOT=${buildout:directory} && echo "<pre>$({{ software_parameter_dict['trafficserver'] }}/bin/traffic_shell ${monitor-ats-cache-stats-config:output})</pre>"
extra-context =
  key content monitor-ats-cache-stats-wrapper:command

[monitor-ats-cache-stats-config]
< = jinja2-template-base
url = {{ software_parameter_dict['template_empty'] }}
output = ${trafficserver-configuration-directory:target}/cache-config.stats
context =
    raw content show:cache-stats

[monitor-verify-re6st-connectivity]
<= monitor-promise-base
promise = check_url_available
name = re6st-connectivity.py
config-url = ${configuration:re6st-verification-url}

[slave-introspection-frontend]
<= slap-connection
recipe = slapos.cookbook:requestoptional
name = Slave Introspection Frontend {{ instance_parameter_dict['configuration.frontend-name'] }}
software-url = http://git.erp5.org/gitweb/slapos.git/blob_plain/HEAD:/software/apache-frontend/software.cfg
shared = true
config-url = https://[${slap-configuration:ipv6-random}]:{{ instance_parameter_dict['configuration.slave-introspection-https-port'] }}/
config-https-only = true
return = secure_access

[backend-haproxy-statistic-frontend]
<= slap-connection
recipe = slapos.cookbook:requestoptional
name = Backend Haproxy Statistic Frontend {{ instance_parameter_dict['configuration.frontend-name'] }}
software-url = http://git.erp5.org/gitweb/slapos.git/blob_plain/HEAD:/software/apache-frontend/software.cfg
shared = true
config-url = https://[${slap-configuration:ipv6-random}]:{{ instance_parameter_dict['configuration.backend-haproxy-statistic-port'] }}/
config-https-only = true
return = domain secure_access

[backend-haproxy-statistic-frontend-promise]
<= monitor-promise-base
promise = check_url_available
name = backend-haproxy-statistic-frontend.py
config-http-code = 401
config-url =
  ${backend-haproxy-statistic-frontend:connection-secure_access}

[slave-introspection-configuration-state]
<= jinja2-template-base
url = {{ software_parameter_dict['template_configuration_state_script'] }}
output = ${directory:bin}/${:_buildout_section_name_}

path_list = ${frontend-configuration:slave-introspection-configuration} ${frontend-configuration:ip-access-certificate}
sha256sum = {{ software_parameter_dict['sha256sum'] }}

extra-context =
    import os_module os
    raw find_executable {{ software_parameter_dict['findutils'] }}/bin/find
    key path_list :path_list
    key sha256sum :sha256sum
    key signature_file :signature_file

[slave-introspection-configuration-state-graceful]
<= slave-introspection-configuration-state
signature_file = ${directory:run}/slave_introspection_graceful_configuration_state_signature

[slave-introspection-configuration-state-validate]
<= slave-introspection-configuration-state
signature_file = ${directory:run}/slave_introspection_validate_configuration_state_signature

[slave-introspection-graceful]
< = jinja2-template-base
url = {{ software_parameter_dict['template_graceful_script'] }}
output = ${directory:etc-run}/slave-introspection-safe-graceful

extra-context =
    key graceful_reload_command frontend-haproxy-configuration:slave-introspection-graceful-command
    key configuration_state slave-introspection-configuration-state-graceful:output

[slave-introspection-validate]
<= jinja2-template-base
url = {{ software_parameter_dict['template_validate_script'] }}
output = ${directory:bin}/slave-introspection-validate
last_state_file = ${directory:run}/slave_introspection_configuration_last_state
validate_command = {{ software_parameter_dict['nginx'] }} -c ${frontend-configuration:slave-introspection-configuration} -t
extra-context =
    raw find_executable {{ software_parameter_dict['findutils'] }}/bin/find
    key validate_command :validate_command
    key configuration_state_command slave-introspection-configuration-state-validate:output
    key last_state_file :last_state_file

[promise-slave-introspection-configuration]
<= monitor-promise-base
promise = validate_frontend_configuration
name = slave-introspection-configuration.py
config-verification-script = ${promise-slave-introspection-configuration-helper:output}

[promise-slave-introspection-configuration-helper]
< = jinja2-template-base
url = {{ software_parameter_dict['template_empty'] }}
output = ${directory:bin}/slave-introspection-read-last-configuration-state
content =
  #!/bin/sh
  exit `cat ${slave-introspection-validate:last_state_file}`
context =
    key content :content

[promise-slave-introspection-https]
<= monitor-promise-base
promise = check_socket_listening
name = slave_introspection_https.py
config-host = {{ instance_parameter_dict['ipv6-random'] }}
config-port = ${frontend-configuration:slave-introspection-https-port}

[logrotate-entry-slave-introspection]
<= logrotate-entry-base
name = slave-introspection
log = ${frontend-haproxy-configuration:slave-introspection-access-log} ${frontend-haproxy-configuration:slave-introspection-error-log}
rotate-num = ${configuration:rotate-num}
post = kill -USR2 $(cat ${frontend-haproxy-configuration:slave-introspection-pid-file})
delaycompress =

[logrotate-setup-validate]
<= jinja2-template-base
url = {{ software_parameter_dict['template_wrapper'] }}
output = ${directory:scripts}/logrotate-setup-validate
command =
  if ${logrotate:wrapper-path} -d > ${:state-file} 2>&1 ; then
    cat /dev/null > ${:state-file}
  fi
extra-context =
  key content :command
state-file = ${directory:run}/logrotate-setup.state

[promise-logrotate-setup]
<= monitor-promise-base
promise = check_file_state
name = ${:_buildout_section_name_}.py
config-filename = ${logrotate-setup-validate:state-file}
config-state = empty

[configuration]
{%- for key, value in instance_parameter_dict.items() -%}
{%-   if key.startswith('configuration.') %}
{{ key.replace('configuration.', '') }} = {{ dumps(value) }}
{%-   endif -%}
{%- endfor %}

[instance-parameter-section]
{#- There are dangerous keys like recipe, etc #}
{#- XXX: Some other approach would be useful #}
{%- set DROP_KEY_LIST = ['recipe', '__buildout_signature__', 'computer', 'partition', 'url', 'key', 'cert'] %}
{%- for key, value in instance_parameter_dict.items() -%}
{%-   if not key.startswith('configuration.') and key not in DROP_KEY_LIST %}
{{ key }} = {{ dumps(value) }}
{%-   endif -%}
{%- endfor %}

[software-parameter-section]
{%- for key, value in software_parameter_dict.items() %}
{{ key }} = {{ dumps(value) }}
{%- endfor %}
