Skip to content

Commit

Permalink
Merge branch 'master' into dynamic_backends
Browse files Browse the repository at this point in the history
  • Loading branch information
Ian Clark committed May 30, 2020
2 parents 6ac198e + 6801a5f commit bbde0bd
Show file tree
Hide file tree
Showing 16 changed files with 168 additions and 51 deletions.
63 changes: 43 additions & 20 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,60 @@
---
sudo: required
dist: trusty
dist: xenial

language: python
python: "2.7"
python:
- "2.7"
- "3.5"

env:
- ANSIBLE_VERSION=latest
- ANSIBLE_VERSION=2.9.7
- ANSIBLE_VERSION=2.9.6
- ANSIBLE_VERSION=2.9.5
- ANSIBLE_VERSION=2.9.4
- ANSIBLE_VERSION=2.9.3
- ANSIBLE_VERSION=2.9.2
- ANSIBLE_VERSION=2.9.1
- ANSIBLE_VERSION=2.9.0
- ANSIBLE_VERSION=2.8.11
- ANSIBLE_VERSION=2.8.10
- ANSIBLE_VERSION=2.8.9
- ANSIBLE_VERSION=2.8.8
- ANSIBLE_VERSION=2.8.7
- ANSIBLE_VERSION=2.8.6
- ANSIBLE_VERSION=2.8.5
- ANSIBLE_VERSION=2.8.4
- ANSIBLE_VERSION=2.8.3
- ANSIBLE_VERSION=2.8.2
- ANSIBLE_VERSION=2.8.1
- ANSIBLE_VERSION=2.8.0
- ANSIBLE_VERSION=2.7.17
- ANSIBLE_VERSION=2.7.16
- ANSIBLE_VERSION=2.7.15
- ANSIBLE_VERSION=2.7.14
- ANSIBLE_VERSION=2.7.13
- ANSIBLE_VERSION=2.7.12
- ANSIBLE_VERSION=2.7.11
- ANSIBLE_VERSION=2.7.10
- ANSIBLE_VERSION=2.7.9
- ANSIBLE_VERSION=2.7.8
- ANSIBLE_VERSION=2.7.7
- ANSIBLE_VERSION=2.7.6
- ANSIBLE_VERSION=2.7.5
- ANSIBLE_VERSION=2.7.4
- ANSIBLE_VERSION=2.7.3
- ANSIBLE_VERSION=2.7.2
- ANSIBLE_VERSION=2.7.1
- ANSIBLE_VERSION=2.7.0
- ANSIBLE_VERSION=2.6.20
- ANSIBLE_VERSION=2.6.19
- ANSIBLE_VERSION=2.6.18
- ANSIBLE_VERSION=2.6.17
- ANSIBLE_VERSION=2.6.16
- ANSIBLE_VERSION=2.6.15
- ANSIBLE_VERSION=2.6.14
- ANSIBLE_VERSION=2.6.13
- ANSIBLE_VERSION=2.6.12
- ANSIBLE_VERSION=2.6.11
- ANSIBLE_VERSION=2.6.10
Expand All @@ -27,21 +68,6 @@ env:
- ANSIBLE_VERSION=2.6.2
- ANSIBLE_VERSION=2.6.1
- ANSIBLE_VERSION=2.6.0
- ANSIBLE_VERSION=2.5.14
- ANSIBLE_VERSION=2.5.13
- ANSIBLE_VERSION=2.5.12
- ANSIBLE_VERSION=2.5.11
- ANSIBLE_VERSION=2.5.10
- ANSIBLE_VERSION=2.5.9
- ANSIBLE_VERSION=2.5.8
- ANSIBLE_VERSION=2.5.7
- ANSIBLE_VERSION=2.5.6
- ANSIBLE_VERSION=2.5.5
- ANSIBLE_VERSION=2.5.4
- ANSIBLE_VERSION=2.5.3
- ANSIBLE_VERSION=2.5.2
- ANSIBLE_VERSION=2.5.1
- ANSIBLE_VERSION=2.5.0

branches:
only:
Expand Down Expand Up @@ -83,9 +109,6 @@ script:

notifications:
email: false
hipchat:
rooms:
secure: 5E26TUa3gV2/vSsuMtkAOtx7VsJf2XWEkfkhE8F1O2QvKrtRanVZZN9MJOba970GmZFQQne1so9HvXAeaPnD3LEcqy+SGAn6JvyH0mYy8i6a/gd9kxiOMHF3gTqCdDsu/SKPAB8N4J1eDvDo/6dfnVzifsZwvw92D9uOkEKi5cEiV8KhKAzK7tLQqAKESNt1BbnCAuTAz38WxJJVrMNPYlZ1gOqK9fIXfhRi0u+oo+21UqzOh6L65gAcD2PYvnt6Ak0KMgIo+iqui6xF7SG4IaMHk9G4FjY77cxxYy39HIAwNUArO0F1OO59b2oofdKX8w2X8TL+4t9zluKorgmvBGHtDcwxy9x2pjJqiTNpYSZyIRZMzM5ocA8y6JeGcl9VDTsO4hJWHqaB+WSulPYtGwZ1UMHPt0GmxdL3kf/ksmq6FJA4PGpV8YMHS65rMs9PibLeWnP7Ja4LQHlJ/eWUSQoCRECBrKxq4zgvTwEr9pxXeQXm85j29KO0c4UmJQhw1ISGuL6Z3VHOewCcdOvNzQmQn9y/rxVpmOWYlJpwPVAf3ht3D3C6t5aoViIwS508pnVzAycnpbxVPRpisQW/j/IC6PeChi3xv24n2pGJctoXtn/vWzt3VXEKKj9Y/Mr9hAEyNlNJfcaLhuicCcMa0d4j3ue8VRpjIjAnRJYBm28=
webhooks: https://galaxy.ansible.com/api/v1/notifications/
slack:
rooms:
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ RUN rm -rf $HOME/.cache
# ansible
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y gcc libffi-dev libssl-dev && \
apt-get clean
RUN pip install ansible==2.3.2.0
RUN pip install ansible==2.6.2
RUN rm -rf $HOME/.cache

# provision
Expand Down
46 changes: 41 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## haproxy

[![Build Status](https://travis-ci.org/Oefenweb/ansible-haproxy.svg?branch=master)](https://travis-ci.org/Oefenweb/ansible-haproxy) [![Ansible Galaxy](http://img.shields.io/badge/ansible--galaxy-haproxy-blue.svg)](https://galaxy.ansible.com/Oefenweb/haproxy)
[![Build Status](https://travis-ci.org/Oefenweb/ansible-haproxy.svg?branch=master)](https://travis-ci.org/Oefenweb/ansible-haproxy)
[![Ansible Galaxy](http://img.shields.io/badge/ansible--galaxy-haproxy-blue.svg)](https://galaxy.ansible.com/Oefenweb/haproxy)

Set up (the latest version of) [HAProxy](http://www.haproxy.org/) in Ubuntu systems.

Expand All @@ -10,7 +11,9 @@ Set up (the latest version of) [HAProxy](http://www.haproxy.org/) in Ubuntu syst

#### Variables

* `haproxy_version`: [default: `1.8`]: Version to install (e.g. `1.5`, `1.6`, `1.7`, `1.8`)
* `haproxy_use_ppa`: [default: `true`]: Whether or not to add the PPA (for installation)

* `haproxy_version`: [default: `1.8`]: Version to install (e.g. `1.5`, `1.6`, `1.7`, `1.8`, `1.9`, `2.0`, `2.1`)

* `haproxy_install`: [default: `[]`]: Additional packages to install (e.g. `socat`)

Expand All @@ -34,8 +37,10 @@ Set up (the latest version of) [HAProxy](http://www.haproxy.org/) in Ubuntu syst
* `haproxy_global_ca_base`: [default: `/etc/ssl/certs`]: Assigns a default directory to fetch SSL CA certificates and CRLs from when a relative path is used with `"ca-file"` or `"crl-file"` directives
* `haproxy_global_crt_base`: [default: `/etc/ssl/private`]: Assigns a default directory to fetch SSL certificates from when a relative path is used with `"crtfile"` directives
* `haproxy_global_ssl_default_bind_ciphers`: [default: `kEECDH+aRSA+AES:kRSA+AES:+AES256:RC4-SHA:!kEDH:!LOW:!EXP:!MD5:!aNULL:!eNULL`]: This setting is only available when support for OpenSSL was built in. It sets the default string describing the list of cipher algorithms ("cipher suite") that are negotiated during the SSL/TLS handshake for all `"bind"` lines which do not explicitly define theirs
* `haproxy_global_ssl_default_bind_ciphersuites`: [default: ``]: This setting is only available when support for OpenSSL was built in and OpenSSL 1.1.1 or later was used to build HAProxy. It sets the default string describing the list of cipher algorithms ("cipher suite") that are negotiated during the TLSv1.3 handshake for all `"bind"` lines which do not explicitly define theirs
* `haproxy_global_ssl_default_bind_options`: [default: `no-sslv3`]: This setting is only available when support for OpenSSL was built in. It sets default ssl-options to force on all `"bind"` lines
* `haproxy_global_ssl_default_server_ciphers`: [default: `kEECDH+aRSA+AES:kRSA+AES:+AES256:RC4-SHA:!kEDH:!LOW:!EXP:!MD5:!aNULL:!eNULL`]: This setting is only available when support for OpenSSL was built in. It sets the default string describing the list of cipher algorithms that are negotiated during the SSL/TLS handshake with the server, for all `"server"` lines which do not explicitly define theirs
* `haproxy_global_ssl_default_server_ciphersuites`: [default: ``]: This setting is only available when support for OpenSSL was built in and OpenSSL 1.1.1 or later was used to build HAProxy. It sets the default string describing the list of cipher algorithms that are negotiated duringthe TLSv1.3 handshake with the server, for all `"server"` lines which do not explicitly define theirs
* `haproxy_global_ssl_default_server_options`: [default: `no-sslv3`]: This setting is only available when support for OpenSSL was built in. It sets default ssl-options to force on all `"server"` lines
* `haproxy_global_ssl_engines`: [optional, default `[]`]: OpenSSL engine declarations (`>= 1.8.0` only)
* `haproxy_global_ssl_engines.{n}.name`: [required]: Sets the OpenSSL engine to use (e.g. `rdrand`)
Expand All @@ -46,7 +51,7 @@ Set up (the latest version of) [HAProxy](http://www.haproxy.org/) in Ubuntu syst
* `haproxy_global_tune`: [default: `[]`]: (Performance) tuning declarations
* `haproxy_global_tune.{n}.key`: [required]: Setting name (e.g. `ssl.cachesize`)
* `haproxy_global_tune.{n}.value`: [required]: Setting value (e.g. `50000`)
* `haproxy_global_option`: [default: `[]`]: Options (e.g. ['lua-load /etc/haproxy/acme-http01-webroot.lua', 'ssl-dh-param-file /etc/haproxy/dhparams.pem'])
* `haproxy_global_option`: [default: `[]`]: Options (e.g. `['lua-load /etc/haproxy/acme-http01-webroot.lua', 'ssl-dh-param-file /etc/haproxy/dhparams.pem']`)
* `haproxy_global_peers`: Peer list declarations
* `haproxy_global_peers.{n}.name`: Peer list name (e.g. `mypeers`)
* `haproxy_global_peers.{n}.peers`: Peer declarations
Expand All @@ -55,7 +60,7 @@ Set up (the latest version of) [HAProxy](http://www.haproxy.org/) in Ubuntu syst
* `haproxy_global_raw_options`: [default: `[]`]: Additional arbitrary lines to insert in the section

* `haproxy_defaults_log`: [default: `global`]: Enable per-instance logging of events and traffic. `global` should be used when the instance's logging parameters are the same as the global ones. This is the most common usage
* `haproxy_defaults_logformat`: [optional]: Allows you to customize the logs in http mode and tcp mode (e.g. `%{+Q}o\ %t\ %s\ %{-Q}r`)
* `haproxy_defaults_logformat`: [optional]: Allows you to customize the logs in http mode and tcp mode (e.g. `'"%{+Q}o\ %t\ %s\ %{-Q}r"'`)
* `haproxy_defaults_mode`: [default: `http`]: Set the running mode or protocol of the instance
* `haproxy_defaults_source`: [optional]: Set the source address or interface for connections from the proxy
* `haproxy_defaults_option`: [default: `[httplog, dontlognull]`]: Options (default)
Expand Down Expand Up @@ -142,6 +147,14 @@ Set up (the latest version of) [HAProxy](http://www.haproxy.org/) in Ubuntu syst
* `haproxy_listen.{n}.server.{n}.name`: [required]: The internal name assigned to this server
* `haproxy_listen.{n}.server.{n}.listen`: [required]: Defines a listening address and/or ports
* `haproxy_listen.{n}.server.{n}.param`: [optional]: A list of parameters for this server
* `haproxy_listen.{n}.server_template`: [optional]: Server template declarations
* `haproxy_listen.{n}.server_template.name`: [required]: A prefix for the server names to be built.
* `haproxy_listen.{n}.server_template.num`: [required]: Number or range of servers. If specified as `<num>`, this template initializes `<num>` servers with 1 up to `<num>` as server name suffixes. If specified as `<num_low>-<num_high>`, initializes with `<num_low>` up to `<num_high>` as server name suffixes.
* `haproxy_listen.{n}.server_template.fqdn`: [required]: A FQDN for all the servers this template initializes
* `haproxy_listen.{n}.server_template.port`: [optional]: Port specification
* `haproxy_listen.{n}.server_template.{n}.param`: [optional]: A list of parameters for this server template
* `haproxy_listen.{n}.retry_on`: [optional, default `[]`]: Specify when to attempt to automatically retry a failed request. Provide a list of keywords or HTTP status codes, each representing a type of failure event on which an attempt to retry the request is desired. For details, see HAProxy documentation.
* `haproxy_listen.{n}.retries`: [optional]: Number of retries to perform on a server after a connection failure
* `haproxy_listen.{n}.reqadd`: [optional]: Adds headers at the end of the HTTP request
* `haproxy_listen.{n}.reqadd.{n}.string`: [required]: The complete line to be added. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6)
* `haproxy_listen.{n}.reqadd.{n}.cond`: [optional]: A matching condition built from ACLs
Expand Down Expand Up @@ -194,7 +207,7 @@ Set up (the latest version of) [HAProxy](http://www.haproxy.org/) in Ubuntu syst
* `haproxy_frontend.{n}.bind_process`: [optional]: Limits the declaration to a certain set of processes numbers (e.g. `[all]`, `[1]`, `[2 ,3, 4]`)
* `haproxy_frontend.{n}.mode`: [required]: Set the running mode or protocol of the section (e.g. `http`)
* `haproxy_frontend.{n}.maxconn`: [optional]: Fix the maximum number of concurrent connections
* `haproxy_frontend.{n}.logformat`: [optional]: Specifies the log format string to use for traffic logs (e.g. `%{+Q}o\ %t\ %s\ %{-Q}r`)
* `haproxy_frontend.{n}.logformat`: [optional]: Specifies the log format string to use for traffic logs (e.g. `'"%{+Q}o\ %t\ %s\ %{-Q}r"'`)
* `haproxy_frontend.{n}.stick`: [optional]: Stick declarations
* `haproxy_frontend.{n}.stick.{n}.table`: [required]: Configure the stickiness table for the current section (e.g. `type ip size 500k`)
* `haproxy_frontend.{n}.option`: [optional]: Options to set (e.g. `[tcplog]`)
Expand Down Expand Up @@ -360,6 +373,16 @@ Set up (the latest version of) [HAProxy](http://www.haproxy.org/) in Ubuntu syst
* `haproxy_backend.{n}.server.{n}.name`: [required]: The internal name assigned to this server
* `haproxy_backend.{n}.server.{n}.listen`: [required]: Defines a listening address and/or ports
* `haproxy_backend.{n}.server.{n}.param`: [optional]: A list of parameters for this server
* `haproxy_backend.{n}.server_template`: [optional]: Server template declarations
* `haproxy_backend.{n}.server_template.name`: [required]: A prefix for the server names to be built.
* `haproxy_backend.{n}.server_template.num`: [required]: Number or range of servers. If specified as `<num>`, this template initializes `<num>` servers with 1 up to `<num>` as server name suffixes. If specified as `<num_low>-<num_high>`, initializes with `<num_low>` up to `<num_high>` as server name suffixes.
* `haproxy_backend.{n}.server_template.fqdn`: [required]: A FQDN for all the servers this template initializes
* `haproxy_backend.{n}.server_template.port`: [optional]: Port specification
* `haproxy_backend.{n}.server_template.{n}.param`: [optional]: A list of parameters for this server template
* `haproxy_backend.{n}.retry_on`: [optional, default `[]`]: Specify when to attempt to automatically retry a failed request. Provide a list of keywords or HTTP status codes, each representing a type of failure event on which an attempt to retry the request is desired. For details, see HAProxy documentation.
* `haproxy_backend.{n}.retries`: [optional]: Number of retries to perform on a server after a connection failure


* `haproxy_backend.{n}.errorfile`: [optional]: Errorfile declarations
* `haproxy_backend.{n}.errorfile.{n}.code`: [required]: The HTTP status code. Currently, HAProxy is capable of generating codes 200, 400, 403, 408, 500, 502, 503, and 504 (e.g. `400`)
* `haproxy_backend.{n}.errorfile.{n}.file`: [required]: A file containing the full HTTP response (e.g `/etc/haproxy/errors/400.http`)
Expand All @@ -374,6 +397,19 @@ Set up (the latest version of) [HAProxy](http://www.haproxy.org/) in Ubuntu syst
* `haproxy_userlists.{n}.users.{n}.insecure_password`: [optional] Plaintext password of this user. **One of `password` or `insecure_password` must be set**
* `haproxy_userlists.{n}.users.{n}.groups`: [optional] List of groups to add the user to

* `haproxy_resolvers`: [default: `[]`]: Resolvers (name servers) declarations
* `haproxy_resolvers.{n}.name`: [required]: The name of the name server list
* `haproxy_resolvers.{n}.nameservers`: [required] list of DNS servers
* `haproxy_resolvers.{n}.nameservers.{n}.name`: [required] label of the server, should be unique
* `haproxy_resolvers.{n}.nameservers.{n}.listen`: [required] Defines a listening address and/or ports, e.g. `8.8.8.8:53`
* `haproxy_resolvers.{n}.accepted_payload_size`: [optional]: Defines the maximum payload size (in bytes) accepted by HAProxy and announced to all the name servers configured in this resolvers section. If not set, HAProxy announces 512. (minimal value defined by RFC 6891)
* `haproxy_resolvers.{n}.parse_resolv_conf`: [optional]: If set to `true`, adds all nameservers found in `/etc/resolv.conf` to this resolver's nameservers list.
* `haproxy_resolvers.{n}.resolve_retries`: [optional]: Defines the number of queries to send to resolve a server name before giving up.
* `haproxy_resolvers.{n}.hold`: [optional]: A list of directives defining `<period>` during which the last name resolution should be kept based on last resolution `<status>`.
* `haproxy_resolvers.{n}.hold.{status}`: [optional]: hold directives in `<status>:<period>` format. Key must be one of (`nx`, `other`, `refused`, `timeout`, `valid`, `obsolete`). Value is interval between two successive name resolutions in HAProxy time format.
* `haproxy_resolvers.{n}.timeout`: [optional]: Defines timeouts related to name resolution
* `haproxy_resolvers.{n}.timeout.{event}`: [optional]: timeout directives in `<event>:<time>` format. Key must be one of (`resolve`, `retry`). Value is time related to the event in the HAProxy time format.

* `haproxy_acl_files`: [default: `[]`]: ACL file declarations
* `haproxy_acl_files.{n}.dest`: [required]: The remote path of the file (e.g. `/etc/haproxy/acl/api.map`)
* `haproxy_acl_files.{n}.content`: [default: `[]`]: The content (lines) of the file (e.g. `['v1.0 be_alpha', 'v1.1 be_bravo']`)
Expand Down
2 changes: 1 addition & 1 deletion Vagrantfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ boxes = [
:box => "bento/ubuntu-18.04",
:ip => '10.0.0.14',
:cpu => "50",
:ram => "256"
:ram => "384"
},
]

Expand Down
6 changes: 6 additions & 0 deletions defaults/main.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# defaults file for haproxy
---
haproxy_use_ppa: true
haproxy_version: 1.8

haproxy_dependencies:
Expand Down Expand Up @@ -28,8 +29,10 @@ haproxy_global_daemon: true
haproxy_global_ca_base: /etc/ssl/certs
haproxy_global_crt_base: /etc/ssl/private
haproxy_global_ssl_default_bind_ciphers: 'kEECDH+aRSA+AES:kRSA+AES:+AES256:RC4-SHA:!kEDH:!LOW:!EXP:!MD5:!aNULL:!eNULL'
haproxy_global_ssl_default_bind_ciphersuites: ''
haproxy_global_ssl_default_bind_options: 'no-sslv3'
haproxy_global_ssl_default_server_ciphers: 'kEECDH+aRSA+AES:kRSA+AES:+AES256:RC4-SHA:!kEDH:!LOW:!EXP:!MD5:!aNULL:!eNULL'
haproxy_global_ssl_default_server_ciphersuites: ''
haproxy_global_ssl_default_server_options: 'no-sslv3'
haproxy_global_nbproc: 1
haproxy_global_option: []
Expand Down Expand Up @@ -79,5 +82,8 @@ haproxy_backend: []
# user-lists section
haproxy_userlists: []

# resolvers section:
haproxy_resolvers: []

# ACL files
haproxy_acl_files: []
2 changes: 1 addition & 1 deletion meta/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ galaxy_info:
company: Oefenweb.nl B.V.
description: Set up the latest version of HAProxy in Ubuntu systems
license: MIT
min_ansible_version: 2.5.0.0
min_ansible_version: 2.6.0
platforms:
- name: Ubuntu
versions:
Expand Down
1 change: 1 addition & 0 deletions tasks/install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
apt_repository:
repo: "{{ haproxy_ppa }}"
update_cache: true
when: haproxy_use_ppa
tags:
- haproxy-install-add-repository

Expand Down
8 changes: 4 additions & 4 deletions tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,25 @@
- haproxy
- haproxy-check-version-support

- include: install.yml
- import_tasks: install.yml
tags:
- configuration
- haproxy
- haproxy-install

- include: certificates.yml
- import_tasks: certificates.yml
tags:
- configuration
- haproxy
- haproxy-certificates

- include: acl.yml
- import_tasks: acl.yml
tags:
- configuration
- haproxy
- haproxy-acl

- include: configuration.yml
- import_tasks: configuration.yml
tags:
- configuration
- haproxy
Expand Down
13 changes: 11 additions & 2 deletions templates/etc/haproxy/backend.cfg.j2
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ backend {{ backend.name }}
{% endfor %}
{% for compression in backend.compression | default([]) %}
compression {{ compression.name }} {{ compression.value }}

{% endfor %}
{% if backend.default_server_params | default([]) %}
default-server {% for param in backend.default_server_params | default([]) %} {{ param }}{% endfor %}
Expand All @@ -112,10 +111,20 @@ backend {{ backend.name }}

{% endfor %}
{% endfor %}
{% if backend.server_template is defined %}
server-template {{ backend.server_template.name }} {{ backend.server_template.num}} {{ backend.server_template.fqdn }}{% if backend.server_template.port is defined %}:{{ backend.server_template.port }}{% endif %} {% for param in backend.server_template.param | default([]) %} {{ param }}{% endfor %}

{% endif %}
{% if backend.retry_on is defined %}
retry-on {% for r in backend.retry_on %}{{ r }} {% endfor %}

{% endif %}
{% if backend.retries is defined %}
retries {{ backend.retries }}
{% endif %}
{% for errorfile in backend.errorfile | default([]) %}
errorfile {{ errorfile.code }} {{ errorfile.file }}
{% endfor %}

{% for line in backend.raw_options | default([]) %}
{{ line }}
{% endfor %}
Expand Down
Loading

0 comments on commit bbde0bd

Please sign in to comment.