instance-balancer.cfg.in 15.8 KB
Newer Older
1
{% import "caucase" as caucase with context %}
2 3
{% set part_list = [] -%}
{% macro section(name) %}{% do part_list.append(name) %}{{ name }}{% endmacro -%}
4
{% set ssl_parameter_dict = slapparameter_dict['ssl'] -%}
5
{% set frontend_caucase_url_list = ssl_parameter_dict.get('frontend_caucase_url_list', []) -%}
6
{% set shared_ca_path = slapparameter_dict.get('shared-certificate-authority-path') -%}
7 8 9 10 11 12 13 14 15 16 17 18 19 20
{#
XXX: This template only supports exactly one IPv4 and (if ipv6 is used) one IPv6
per partition. No more (undefined result), no less (IndexError).
-#}
# TODO: insert varnish between apache & haproxy.
# And think of a way to specify which urls goe through varnish, which go
# directly to haproxy. (maybe just passing literal configuration file chunk)
{% set ipv4 = (ipv4_set | list)[0] -%}
{% set apache_ip_list = [ipv4] -%}
{% if ipv6_set -%}
{%   set ipv6 = (ipv6_set | list)[0] -%}
{%   do apache_ip_list.append('[' ~ ipv6 ~ ']') -%}
{% endif -%}

21
[jinja2-template-base]
22
recipe = slapos.recipe.template:jinja2
23 24
mode = 644

25 26 27 28 29 30
{{ caucase.updater(
     prefix='caucase-updater',
     buildout_bin_directory=parameter_dict['bin-directory'],
     updater_path='${directory:services-on-watch}/caucase-updater',
     url=ssl_parameter_dict['caucase-url'],
     data_dir='${directory:srv}/caucase-updater',
31
     crt_path='${apache-conf-ssl:caucase-cert}',
32 33
     ca_path='${directory:srv}/caucase-updater/ca.crt',
     crl_path='${directory:srv}/caucase-updater/crl.pem',
34
     key_path='${apache-conf-ssl:caucase-key}',
35 36 37 38 39 40
     on_renew='${apache-graceful:output}',
     max_sleep=ssl_parameter_dict.get('max-crl-update-delay', 1.0),
     template_csr_pem=ssl_parameter_dict.get('csr'),
     openssl=parameter_dict['openssl'] ~ '/bin/openssl',
)}}
{% do section('caucase-updater') -%}
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
{% do section('caucase-updater-promise') -%}

{% set frontend_caucase_url_hash_list = [] -%}
{% for frontend_caucase_url in frontend_caucase_url_list -%}
{%   set hash = hashlib.md5(frontend_caucase_url).hexdigest() -%}
{%   do frontend_caucase_url_hash_list.append(hash) -%}
{%   set data_dir = '${directory:srv}/client-cert-ca/%s' % hash -%}
{{   caucase.updater(
       prefix='caucase-updater-%s' % hash,
       buildout_bin_directory=parameter_dict['bin-directory'],
       updater_path='${directory:services-on-watch}/caucase-updater-%s' % hash,
       url=frontend_caucase_url,
       data_dir=data_dir,
       ca_path='%s/ca.crt' % data_dir,
       crl_path='%s/crl.pem' % data_dir,
       on_renew='${caucase-updater-housekeeper:output}; ${apache-graceful:output}',
       max_sleep=ssl_parameter_dict.get('max-crl-update-delay', 1.0),
       openssl=parameter_dict['openssl'] ~ '/bin/openssl',
     )}}
{%   do section('caucase-updater-%s' % hash) -%}
{% endfor -%}

{% if frontend_caucase_url_hash_list -%}
[caucase-updater-housekeeper]
recipe = collective.recipe.template
output = ${directory:bin}/caucase-updater-housekeeper
mode = 700
input =
  inline:
  #!${buildout:executable}
  import glob
  import os
  import subprocess
  hash_list = {{ repr(frontend_caucase_url_hash_list) }}
  crt_list = ['%s.crt' % e for e in hash_list]
  crl_list = ['%s.crl' % e for e in hash_list]
{% if shared_ca_path -%}
  crt_list.append('{{ shared_ca_path }}/cacert.pem')
  crl_list.append('{{ shared_ca_path }}/crl')
{% endif -%}
  for path in glob.glob('${apache-conf-ssl:ca-cert-dir}/*.crt'):
    if os.path.basename(path) not in crt_list:
      os.unlink(path)
  for path in glob.glob('${apache-conf-ssl:crl-dir}/*.crl'):
    if os.path.basename(path) not in crl_list:
      os.unlink(path)
  for hash in hash_list:
    crt = '${directory:srv}/client-cert-ca/%s/ca.crt' % hash
    crt_link = '${apache-conf-ssl:ca-cert-dir}/%s.crt' % hash
    crl = '${directory:srv}/client-cert-ca/%s/crl.pem' % hash
    crl_link = '${apache-conf-ssl:crl-dir}/%s.crl' % hash
    if os.path.isfile(crt) and not os.path.islink(crt_link):
      os.symlink(crt, crt_link)
    if os.path.isfile(crl) and not os.path.islink(crl_link):
      os.symlink(crl, crl_link)
  subprocess.check_call(['{{ parameter_dict["openssl"] }}/bin/c_rehash', '${apache-conf-ssl:ca-cert-dir}'])
  subprocess.check_call(['{{ parameter_dict["openssl"] }}/bin/c_rehash', '${apache-conf-ssl:crl-dir}'])

[caucase-updater-housekeeper-run]
recipe = plone.recipe.command
command = ${caucase-updater-housekeeper:output}
update-command = ${:command}
{% endif -%}
104

105 106
{% set haproxy_dict = {} -%}
{% set apache_dict = {} -%}
107 108
{% set zope_virtualhost_monster_backend_dict = {} %}
{% set test_runner_url_dict = {} %} {# family_name => list of apache URLs #}
109
{% set next_port = itertools.count(slapparameter_dict['tcpv4-port']).next -%}
110 111 112
{% for family_name, parameter_id_list in sorted(
  slapparameter_dict['zope-family-dict'].iteritems()) -%}
{%   set zope_family_address_list = [] -%}
113
{%   set ssl_authentication = slapparameter_dict['ssl-authentication-dict'].get(family_name, False) -%}
114 115 116 117 118 119 120
{%   set has_webdav = [] -%}
{%   for parameter_id in parameter_id_list -%}
{%     set zope_address_list = slapparameter_dict[parameter_id] -%}
{%     for zope_address, maxconn, webdav in zope_address_list -%}
{%       if webdav -%}
{%         do has_webdav.append(None) %}
{%       endif -%}
121
{%       set zope_effective_address = zope_address -%}
122
{%       do zope_family_address_list.append((zope_effective_address, maxconn, webdav)) -%}
123
{%     endfor -%}
124

125 126 127
{#     # Generate entries with rewrite rule for test runnners #}
{%     set test_runner_address_list = slapparameter_dict.get(parameter_id ~ '-test-runner-address-list', []) %}
{%     if test_runner_address_list -%}
128 129 130
{%       set test_runner_backend_mapping = {} %}
{%       set test_runner_apache_url_list = [] %}
{%       set test_runner_external_port = next_port() %}
131
{%       for i, (test_runner_internal_ip, test_runner_internal_port) in enumerate(test_runner_address_list) %}
132 133 134 135 136 137 138 139 140 141
{%         do test_runner_backend_mapping.__setitem__(
                'unit_test_' ~ i,
                'http://' ~ test_runner_internal_ip ~ ':' ~ test_runner_internal_port ) %}
{%         do test_runner_apache_url_list.append(
                'https://' ~ ipv4 ~ ':' ~ test_runner_external_port ~ '/unit_test_' ~ i ~ '/' ) %}
{%       endfor %}
{%       do zope_virtualhost_monster_backend_dict.__setitem__(
              (ipv4, test_runner_external_port),
              ( ssl_authentication, test_runner_backend_mapping ) ) -%}
{%       do test_runner_url_dict.__setitem__(family_name, test_runner_apache_url_list) -%}
142
{%     endif -%}
143
{%   endfor -%}
144

145 146 147 148 149 150 151
{# Make rendering fail artificially if any family has no known backend.
 # This is useful as haproxy's hot-reconfiguration mechanism is
 # supervisord-incompatible.
 # As jinja2 postpones KeyError until place-holder value is actually used,
 # do a no-op getitem.
-#}
{%   do zope_family_address_list[0][0] -%}
152
{%   set haproxy_port = next_port() -%}
153
{%   set backend_path = slapparameter_dict['backend-path-dict'][family_name] -%}
154 155 156 157 158 159 160 161
{%   do haproxy_dict.__setitem__(family_name, (haproxy_port, zope_family_address_list)) -%}
{%   if has_webdav -%}
{%     set internal_scheme = 'http' -%}{# mod_rewrite does not recognise webdav scheme -#}
{%     set external_scheme = 'webdavs' -%}
{%   else %}
{%     set internal_scheme = 'http' -%}
{%     set external_scheme = 'https' -%}
{%   endif -%}
162
{%   do apache_dict.__setitem__(family_name, (next_port(), external_scheme, internal_scheme ~ '://' ~ ipv4 ~ ':' ~ haproxy_port ~ backend_path, slapparameter_dict['ssl-authentication-dict'].get(family_name, False))) -%}
163 164 165 166 167 168 169 170 171
{% endfor -%}

[haproxy-cfg-parameter-dict]
socket-path = ${directory:run}/haproxy.sock
server-check-path = {{ dumps(slapparameter_dict['haproxy-server-check-path']) }}
backend-dict = {{ dumps(haproxy_dict) }}
ip = {{ ipv4 }}

[haproxy-cfg]
172
< = jinja2-template-base
173 174 175 176 177 178 179 180 181
template = {{ parameter_dict['template-haproxy-cfg'] }}
rendered = ${directory:etc}/haproxy.cfg
context = section parameter_dict haproxy-cfg-parameter-dict
extensions = jinja2.ext.do

[{{ section('haproxy') }}]
recipe = slapos.cookbook:wrapper
wrapper-path = ${directory:services}/haproxy
command-line = "{{ parameter_dict['haproxy'] }}/sbin/haproxy" -f "${haproxy-cfg:rendered}"
182
hash-files = ${haproxy-cfg:rendered}
183 184 185 186

[apache-conf-ssl]
cert = ${directory:apache-conf}/apache.crt
key = ${directory:apache-conf}/apache.pem
187 188 189
# XXX caucase certificate is not supported by caddy for now
caucase-cert = ${directory:apache-conf}/apache-caucase.crt
caucase-key = ${directory:apache-conf}/apache-caucase.pem
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
{% if frontend_caucase_url_list -%}
depends = ${caucase-updater-housekeeper-run:recipe}
ca-cert-dir = ${directory:apache-ca-cert-dir}
crl-dir = ${directory:apache-crl-dir}
{%- endif %}

[simplefile]
< = jinja2-template-base
template = inline:{{ '{{ content }}' }}

{% macro simplefile(section_name, file_path, content, mode='') -%}
{%   set content_section_name = section_name ~ '-content' -%}
[{{  content_section_name }}]
content = {{ dumps(content) }}

[{{  section(section_name) }}]
< = simplefile
rendered = {{ file_path }}
context = key content {{content_section_name}}:content
mode = {{ mode }}
{%- endmacro %}
211

212 213 214 215 216 217 218 219 220 221 222 223 224
[apache-ssl]
{% if ssl_parameter_dict.get('key') -%}
key = ${apache-ssl-key:rendered}
cert = ${apache-ssl-cert:rendered}
{{ simplefile('apache-ssl-key', '${apache-conf-ssl:key}', ssl_parameter_dict['key']) }}
{{ simplefile('apache-ssl-cert', '${apache-conf-ssl:cert}', ssl_parameter_dict['cert']) }}
{% else %}
recipe = plone.recipe.command
command = "{{ parameter_dict['openssl'] }}/bin/openssl" req -newkey rsa -batch -new -x509 -days 3650 -nodes -keyout "${:key}" -out "${:cert}"
key = ${apache-conf-ssl:key}
cert = ${apache-conf-ssl:cert}
{%- endif %}

225
[apache-conf-parameter-dict]
226
backend-list = {{ dumps(apache_dict.values()) }}
227
zope-virtualhost-monster-backend-dict = {{ dumps(zope_virtualhost_monster_backend_dict) }}
228 229
ip-list = {{ dumps(apache_ip_list) }}
pid-file = ${directory:run}/apache.pid
230
log-dir = ${directory:log}
231 232 233 234 235
error-log = ${directory:log}/apache-error.log
access-log = ${directory:log}/apache-access.log
# Apache 2.4's default value (60 seconds) can be a bit too short
timeout = 300
# Basic SSL server configuration
236 237
cert = ${apache-ssl:cert}
key = ${apache-ssl:key}
238 239
cipher =
ssl-session-cache = ${directory:log}/apache-ssl-session-cache
240
{% if frontend_caucase_url_list -%}
241
# Client x509 auth
242 243
ca-cert-dir = ${apache-conf-ssl:ca-cert-dir}
crl-dir = ${apache-conf-ssl:crl-dir}
244 245
{%- endif %}

246
[apache-conf]
247
< = jinja2-template-base
248 249 250 251 252 253 254 255
template = {{ parameter_dict['template-apache-conf'] }}
rendered = ${directory:apache-conf}/apache.conf
context = section parameter_dict apache-conf-parameter-dict

[{{ section('apache') }}]
recipe = slapos.cookbook:wrapper
wrapper-path = ${directory:services}/apache
command-line = "{{ parameter_dict['apache'] }}/bin/httpd" -f "${apache-conf:rendered}" -DFOREGROUND
256 257 258 259 260 261
wait-for-files =
  ${apache-conf-ssl:cert}
  ${apache-conf-ssl:key}

[apache-graceful]
recipe = collective.recipe.template
262 263
output = ${directory:bin}/apache-httpd-graceful
mode = 700
264 265 266 267
input = inline:
  #!/bin/sh
  kill -USR1 "$(cat '${apache-conf-parameter-dict:pid-file}')"

268
[{{ section('apache-promise') }}]
269
<= monitor-promise-base
270
# Check any apache port in ipv4, expect other ports and ipv6 to behave consistently
271 272 273 274
module = check_port_listening
name = apache.py
config-hostname = {{ ipv4 }}
config-port = {{ apache_dict.values()[0][0] }}
275

276
[{{ section('publish') }}]
277
recipe = slapos.cookbook:publish.serialised
278
{% for family_name, (apache_port, scheme, _, _) in apache_dict.items() -%}
279 280 281
{{   family_name ~ '-v6' }} = {% if ipv6_set %}{{ scheme ~ '://[' ~ ipv6 ~ ']:' ~ apache_port }}{% endif %}
{{   family_name }} = {{ scheme ~ '://' ~ ipv4 ~ ':' ~ apache_port }}
{% endfor -%}
282 283 284 285
{% for family_name, test_runner_url_list in test_runner_url_dict.items() -%}
{{    family_name ~ '-test-runner-url-list' }} = {{ dumps(test_runner_url_list) }}
{% endfor -%}

286
monitor-base-url = ${monitor-publish-parameters:monitor-base-url}
287

288 289 290 291 292 293 294
{% set apache_service_log_list = {} -%}
{% for family_name, (_, _, _, authentication) in apache_dict.items() -%}
{%   if authentication -%}
{%     set base_name = 'apache-' ~ family_name -%}
{%     do part_list.append('logrotate-' ~ base_name) -%}
{%     do apache_service_log_list.__setitem__(family_name, base_name) -%}
[logrotate-{{ base_name }}]
295
< = logrotate-entry-base
296 297
name = {{ base_name }}
log = ${apache-conf-parameter-dict:log-dir}/{{ base_name }}-error.log ${apache-conf-parameter-dict:log-dir}/{{ base_name }}-access.log
298
post = test ! -s ${apache-conf-parameter-dict:pid-file} || {{ parameter_dict['bin-directory'] }}/slapos-kill --pidfile ${apache-conf-parameter-dict:pid-file} -s USR1
299 300 301
{%   endif -%}
{% endfor -%}

302
[{{ section('logrotate-apache') }}]
303
< = logrotate-entry-base
304 305
name = apache
log = ${apache-conf-parameter-dict:error-log} ${apache-conf-parameter-dict:access-log}
306
post = test ! -s ${apache-conf-parameter-dict:pid-file} || {{ parameter_dict['bin-directory'] }}/slapos-kill --pidfile ${apache-conf-parameter-dict:pid-file} -s USR1
307 308 309 310

[directory]
recipe = slapos.cookbook:mkdirectory
apache-conf = ${:etc}/apache
311 312 313 314
{% if frontend_caucase_url_list -%}
apache-ca-cert-dir = ${:apache-conf}/ssl.crt
apache-crl-dir = ${:apache-conf}/ssl.crl
{% endif -%}
315 316 317
bin = ${buildout:directory}/bin
etc = ${buildout:directory}/etc
services = ${:etc}/run
318
services-on-watch = ${:etc}/service
319 320 321
var = ${buildout:directory}/var
run = ${:var}/run
log = ${:var}/log
322
srv = ${buildout:directory}/srv
323 324 325 326 327 328
ca-dir = ${buildout:directory}/srv/ssl
requests = ${:ca-dir}/requests
private = ${:ca-dir}/private
certs = ${:ca-dir}/certs
newcerts = ${:ca-dir}/newcerts
crl = ${:ca-dir}/crl
329 330
apachedex = ${monitor-directory:private}/apachedex

331 332 333 334 335 336
[{{ section('resiliency-exclude-file') }}]
# Generate rdiff exclude file in case of resiliency
< = jinja2-template-base
template = {{ 'inline:{{ "${directory:log}/**\\n" }}' }}
rendered = ${directory:srv}/exporter.exclude

337
[{{ section('monitor-generate-apachedex-report') }}]
338 339 340 341 342 343 344 345 346
recipe = slapos.cookbook:cron.d
cron-entries = ${cron:cron-entries}
name = generate-apachedex-report
# The goal is to be executed before logrotate log rotation.
# Here, logrotate-entry-base:frequency = daily, so we run at 23 o'clock every day.
frequency = 0 23 * * *
command = ${monitor-generate-apachedex-report-wrapper:wrapper-path}

[monitor-generate-apachedex-report-wrapper]
347
recipe = slapos.cookbook:wrapper
348
wrapper-path = ${directory:bin}/${:command}
349
command-line = "{{ parameter_dict['run-apachedex-location'] }}" "{{ parameter_dict['apachedex-location'] }}" "${directory:apachedex}" ${monitor-publish-parameters:monitor-base-url}/private/apachedex --apache-log-list "${apachedex-parameters:apache-log-list}" --configuration "${apachedex-parameters:configuration}"
350
command = generate-apachedex-report
351 352 353 354 355

[apachedex-parameters]
# XXX - Sample log file with curent date: apache_access.log-%(date)s.gz
# which will be equivalent to apache_access.log-20150112.gz if the date is 2015-01-12
apache-log-list = ${apache-conf-parameter-dict:access-log}
356 357 358 359
configuration = {{ slapparameter_dict['apachedex-configuration'] }}
promise-threshold = {{ slapparameter_dict['apachedex-promise-threshold'] }}

[{{ section('monitor-promise-apachedex-result') }}]
360 361 362 363
<= monitor-promise-base
module = check_command_execute
name = check-apachedex-result.py
config-command = "{{ parameter_dict['promise-check-apachedex-result'] }}" --apachedex_path "${directory:apachedex}" --status_file ${monitor-directory:private}/apachedex.report.json --threshold "${apachedex-parameters:promise-threshold}"
364

365
[{{ section('promise-check-computer-memory') }}]
366 367 368 369
<= monitor-promise-base
module = check_command_execute
name = check-computer-memory.py
config-command = "{{ parameter_dict["check-computer-memory-binary"] }}" -db ${monitor-instance-parameter:collector-db} --threshold "{{ slapparameter_dict["computer-memory-percent-threshold"] }}" --unit percent
370

371 372
[monitor-instance-parameter]
monitor-httpd-ipv6 = {{ (ipv6_set | list)[0] }}
373
monitor-httpd-port = {{ next_port() }}
374
monitor-title = {{ slapparameter_dict['name'] }}
375
password = {{ slapparameter_dict['monitor-passwd'] }}
376 377 378

[buildout]
extends =
379
  {{ template_monitor }}
380 381
parts +=
  {{ part_list | join('\n  ') }}