From 15fbafb82aa9db4cd56e6b5c4d10a0fad25d252b Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Fri, 11 Mar 2016 17:26:11 -0500 Subject: [PATCH 01/52] Fix #505 - Git ignore *.retry files --- .gitignore | 1 + CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index a82987b04e..fd2a86f891 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .vagrant vendor/roles *.py[co] +*.retry diff --git a/CHANGELOG.md b/CHANGELOG.md index 282ebdd5e7..ca259af78d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* Fix #505 - Git ignore \*.retry file * Fix Ansible deprecations for bare variables ([#510](https://github.com/roots/trellis/pull/510)) * Fixes #508 - update php-xdebug config file path ([#509](https://github.com/roots/trellis/pull/509)) * Add php-mbstring extension ([#504](https://github.com/roots/trellis/pull/504)) From 874d3f8f074ac679e0257c562f0514ff50e3592f Mon Sep 17 00:00:00 2001 From: Bart Nagel Date: Mon, 14 Mar 2016 13:51:00 -0700 Subject: [PATCH 02/52] Remove execute permissions from files which don't need them --- roles/fail2ban/LICENSE | 0 roles/fail2ban/README.md | 0 roles/fail2ban/defaults/main.yml | 0 roles/fail2ban/handlers/main.yml | 0 roles/fail2ban/tasks/main.yml | 0 roles/fail2ban/templates/fail2ban.local.j2 | 0 roles/fail2ban/templates/jail.local.j2 | 0 roles/ferm/LICENSE | 0 roles/ferm/README.md | 0 roles/ferm/defaults/main.yml | 0 roles/ferm/handlers/main.yml | 0 roles/ferm/tasks/main.yml | 0 roles/ferm/templates/etc/default/ferm.j2 | 0 roles/ferm/templates/etc/ferm/ferm.conf.j2 | 0 roles/ferm/templates/etc/ferm/filter-input.d/dport_accept.conf.j2 | 0 roles/ferm/templates/etc/ferm/filter-input.d/dport_limit.conf.j2 | 0 roles/sshd/LICENSE | 0 roles/sshd/README.md | 0 roles/sshd/defaults/main.yml | 0 roles/sshd/handlers/main.yml | 0 roles/sshd/tasks/main.yml | 0 roles/sshd/templates/sshd_config.j2 | 0 22 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 roles/fail2ban/LICENSE mode change 100755 => 100644 roles/fail2ban/README.md mode change 100755 => 100644 roles/fail2ban/defaults/main.yml mode change 100755 => 100644 roles/fail2ban/handlers/main.yml mode change 100755 => 100644 roles/fail2ban/tasks/main.yml mode change 100755 => 100644 roles/fail2ban/templates/fail2ban.local.j2 mode change 100755 => 100644 roles/fail2ban/templates/jail.local.j2 mode change 100755 => 100644 roles/ferm/LICENSE mode change 100755 => 100644 roles/ferm/README.md mode change 100755 => 100644 roles/ferm/defaults/main.yml mode change 100755 => 100644 roles/ferm/handlers/main.yml mode change 100755 => 100644 roles/ferm/tasks/main.yml mode change 100755 => 100644 roles/ferm/templates/etc/default/ferm.j2 mode change 100755 => 100644 roles/ferm/templates/etc/ferm/ferm.conf.j2 mode change 100755 => 100644 roles/ferm/templates/etc/ferm/filter-input.d/dport_accept.conf.j2 mode change 100755 => 100644 roles/ferm/templates/etc/ferm/filter-input.d/dport_limit.conf.j2 mode change 100755 => 100644 roles/sshd/LICENSE mode change 100755 => 100644 roles/sshd/README.md mode change 100755 => 100644 roles/sshd/defaults/main.yml mode change 100755 => 100644 roles/sshd/handlers/main.yml mode change 100755 => 100644 roles/sshd/tasks/main.yml mode change 100755 => 100644 roles/sshd/templates/sshd_config.j2 diff --git a/roles/fail2ban/LICENSE b/roles/fail2ban/LICENSE old mode 100755 new mode 100644 diff --git a/roles/fail2ban/README.md b/roles/fail2ban/README.md old mode 100755 new mode 100644 diff --git a/roles/fail2ban/defaults/main.yml b/roles/fail2ban/defaults/main.yml old mode 100755 new mode 100644 diff --git a/roles/fail2ban/handlers/main.yml b/roles/fail2ban/handlers/main.yml old mode 100755 new mode 100644 diff --git a/roles/fail2ban/tasks/main.yml b/roles/fail2ban/tasks/main.yml old mode 100755 new mode 100644 diff --git a/roles/fail2ban/templates/fail2ban.local.j2 b/roles/fail2ban/templates/fail2ban.local.j2 old mode 100755 new mode 100644 diff --git a/roles/fail2ban/templates/jail.local.j2 b/roles/fail2ban/templates/jail.local.j2 old mode 100755 new mode 100644 diff --git a/roles/ferm/LICENSE b/roles/ferm/LICENSE old mode 100755 new mode 100644 diff --git a/roles/ferm/README.md b/roles/ferm/README.md old mode 100755 new mode 100644 diff --git a/roles/ferm/defaults/main.yml b/roles/ferm/defaults/main.yml old mode 100755 new mode 100644 diff --git a/roles/ferm/handlers/main.yml b/roles/ferm/handlers/main.yml old mode 100755 new mode 100644 diff --git a/roles/ferm/tasks/main.yml b/roles/ferm/tasks/main.yml old mode 100755 new mode 100644 diff --git a/roles/ferm/templates/etc/default/ferm.j2 b/roles/ferm/templates/etc/default/ferm.j2 old mode 100755 new mode 100644 diff --git a/roles/ferm/templates/etc/ferm/ferm.conf.j2 b/roles/ferm/templates/etc/ferm/ferm.conf.j2 old mode 100755 new mode 100644 diff --git a/roles/ferm/templates/etc/ferm/filter-input.d/dport_accept.conf.j2 b/roles/ferm/templates/etc/ferm/filter-input.d/dport_accept.conf.j2 old mode 100755 new mode 100644 diff --git a/roles/ferm/templates/etc/ferm/filter-input.d/dport_limit.conf.j2 b/roles/ferm/templates/etc/ferm/filter-input.d/dport_limit.conf.j2 old mode 100755 new mode 100644 diff --git a/roles/sshd/LICENSE b/roles/sshd/LICENSE old mode 100755 new mode 100644 diff --git a/roles/sshd/README.md b/roles/sshd/README.md old mode 100755 new mode 100644 diff --git a/roles/sshd/defaults/main.yml b/roles/sshd/defaults/main.yml old mode 100755 new mode 100644 diff --git a/roles/sshd/handlers/main.yml b/roles/sshd/handlers/main.yml old mode 100755 new mode 100644 diff --git a/roles/sshd/tasks/main.yml b/roles/sshd/tasks/main.yml old mode 100755 new mode 100644 diff --git a/roles/sshd/templates/sshd_config.j2 b/roles/sshd/templates/sshd_config.j2 old mode 100755 new mode 100644 From 54716061d5014ab1687f823f31f0c08801e1de9c Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Thu, 17 Mar 2016 12:18:38 -0400 Subject: [PATCH 03/52] Improve Git repo format validation --- CHANGELOG.md | 1 + deploy.yml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca259af78d..ea5128b0ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* Improve Git repo format validation [#516](https://github.com/roots/trellis/pull/516) * Fix #505 - Git ignore \*.retry file * Fix Ansible deprecations for bare variables ([#510](https://github.com/roots/trellis/pull/510)) * Fixes #508 - update php-xdebug config file path ([#509](https://github.com/roots/trellis/pull/509)) diff --git a/deploy.yml b/deploy.yml index 4a4cbbaacc..e110aca79f 100644 --- a/deploy.yml +++ b/deploy.yml @@ -26,10 +26,10 @@ fail: msg: | Invalid Git repository. - Ensure that your site's `repo` variable is defined in `group_vars/{{ env }}/wordpress_sites.yml` and uses the SSH format (example: git@github.com/roots/bedrock.git) + Ensure that your site's `repo` variable is defined in `group_vars/{{ env }}/wordpress_sites.yml` and uses the SSH format (example: git@github.com:roots/bedrock.git) More info: > https://roots.io/trellis/docs/deploys/ - when: project.repo is not defined or not project.repo | match("git@.*.git") + when: project.repo is not defined or not project.repo | match(".*@.*:.*\.git") roles: - deploy From e646a8fa3723e728527651f82166d04b53cbee15 Mon Sep 17 00:00:00 2001 From: Duncan Brown Date: Mon, 21 Mar 2016 16:09:10 +0000 Subject: [PATCH 04/52] Disable MariaDB binary logging by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This feature eats up disk space, and it’s not necessary unless you’re doing DB replication, in which case you’ll have other things to configure anyway. --- roles/mariadb/defaults/main.yml | 1 + roles/mariadb/tasks/main.yml | 8 ++++++++ roles/mariadb/templates/disable-binary-logging.cnf | 2 ++ 3 files changed, 11 insertions(+) create mode 100644 roles/mariadb/templates/disable-binary-logging.cnf diff --git a/roles/mariadb/defaults/main.yml b/roles/mariadb/defaults/main.yml index bb240e3905..6ebb4267f9 100644 --- a/roles/mariadb/defaults/main.yml +++ b/roles/mariadb/defaults/main.yml @@ -1,3 +1,4 @@ keyserver_fingerprint: "0xcbcb082a1bb943db" mirror: nyc2.mirrors.digitalocean.com version: "10.0" +binary_logging_disabled: true diff --git a/roles/mariadb/tasks/main.yml b/roles/mariadb/tasks/main.yml index 75555d0657..11d407da40 100644 --- a/roles/mariadb/tasks/main.yml +++ b/roles/mariadb/tasks/main.yml @@ -17,6 +17,14 @@ name: mariadb-server state: present +- name: Disable MariaDB binary logging + template: + src: disable-binary-logging.cnf + dest: /etc/mysql/conf.d + owner: root + group: root + when: binary_logging_disabled + - name: Start MariaDB MySQL Server service: name: mysql diff --git a/roles/mariadb/templates/disable-binary-logging.cnf b/roles/mariadb/templates/disable-binary-logging.cnf new file mode 100644 index 0000000000..f48416abcd --- /dev/null +++ b/roles/mariadb/templates/disable-binary-logging.cnf @@ -0,0 +1,2 @@ +[mysqld] +skip-log-bin From d05170b36f74d215933bad84818b65d6a8efda62 Mon Sep 17 00:00:00 2001 From: Duncan Brown Date: Mon, 21 Mar 2016 16:10:20 +0000 Subject: [PATCH 05/52] Restart MariaDB after config change This task was previously `Start`. It did nothing, because the `Install` task already started the server. Now that there is extra config to pick up, a restart is necessary. --- roles/mariadb/tasks/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/roles/mariadb/tasks/main.yml b/roles/mariadb/tasks/main.yml index 11d407da40..71aaa7661e 100644 --- a/roles/mariadb/tasks/main.yml +++ b/roles/mariadb/tasks/main.yml @@ -25,10 +25,10 @@ group: root when: binary_logging_disabled -- name: Start MariaDB MySQL Server +- name: Restart MariaDB MySQL Server service: name: mysql - state: started + state: restarted enabled: true - name: Set root user password From 15d453c8a0c3f5eab37c62f9d812fc001f18d5a7 Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Fri, 11 Mar 2016 17:25:53 -0500 Subject: [PATCH 06/52] =?UTF-8?q?Let=E2=80=99s=20Encrypt=20integration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #328 --- CHANGELOG.md | 1 + README.md | 1 + group_vars/development/wordpress_sites.yml | 1 + group_vars/production/wordpress_sites.yml | 3 +- group_vars/staging/wordpress_sites.yml | 3 +- roles/common/handlers/main.yml | 16 +++-- roles/letsencrypt/README.md | 9 +++ roles/letsencrypt/defaults/main.yml | 43 +++++++++++++ roles/letsencrypt/tasks/certificates.yml | 64 +++++++++++++++++++ roles/letsencrypt/tasks/main.yml | 14 ++++ roles/letsencrypt/tasks/nginx.yml | 55 ++++++++++++++++ roles/letsencrypt/tasks/setup.yml | 56 ++++++++++++++++ .../templates/acme-challenge-location.conf.j2 | 4 ++ .../templates/nginx-challenge-site.conf.j2 | 11 ++++ roles/letsencrypt/templates/renew-certs.py | 54 ++++++++++++++++ roles/nginx/defaults/main.yml | 1 + roles/nginx/tasks/main.yml | 3 +- roles/users/tasks/main.yml | 2 +- roles/wordpress-setup/tasks/main.yml | 5 +- roles/wordpress-setup/tasks/nginx.yml | 29 ++++++--- .../tasks/self-signed-certificate.yml | 27 ++------ .../templates/wordpress-site.conf.j2 | 38 +++++++---- server.yml | 1 + 23 files changed, 386 insertions(+), 55 deletions(-) create mode 100644 roles/letsencrypt/README.md create mode 100644 roles/letsencrypt/defaults/main.yml create mode 100644 roles/letsencrypt/tasks/certificates.yml create mode 100644 roles/letsencrypt/tasks/main.yml create mode 100644 roles/letsencrypt/tasks/nginx.yml create mode 100644 roles/letsencrypt/tasks/setup.yml create mode 100644 roles/letsencrypt/templates/acme-challenge-location.conf.j2 create mode 100644 roles/letsencrypt/templates/nginx-challenge-site.conf.j2 create mode 100644 roles/letsencrypt/templates/renew-certs.py diff --git a/CHANGELOG.md b/CHANGELOG.md index ea5128b0ba..e065995d9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* Let's Encrypt integration ([#518](https://github.com/roots/trellis/pull/518)) * Improve Git repo format validation [#516](https://github.com/roots/trellis/pull/516) * Fix #505 - Git ignore \*.retry file * Fix Ansible deprecations for bare variables ([#510](https://github.com/roots/trellis/pull/510)) diff --git a/README.md b/README.md index f17c62447f..631e21e44b 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Trellis will configure a server with the following and more: * PHP 7.0 * MariaDB (a drop-in MySQL replacement) * SSL support (scores an A+ on the [Qualys SSL Labs Test](https://www.ssllabs.com/ssltest/)) +* Let's Encrypt integration for free SSL certificates * HTTP/2 support (requires SSL) * Composer * WP-CLI diff --git a/group_vars/development/wordpress_sites.yml b/group_vars/development/wordpress_sites.yml index 23cc06eb8f..6efe3b1094 100644 --- a/group_vars/development/wordpress_sites.yml +++ b/group_vars/development/wordpress_sites.yml @@ -15,6 +15,7 @@ wordpress_sites: subdomains: false ssl: enabled: false + provider: self-signed cache: enabled: false duration: 30s diff --git a/group_vars/production/wordpress_sites.yml b/group_vars/production/wordpress_sites.yml index b505ad47b6..67373a20fc 100644 --- a/group_vars/production/wordpress_sites.yml +++ b/group_vars/production/wordpress_sites.yml @@ -12,8 +12,7 @@ wordpress_sites: subdomains: false ssl: enabled: false - # cert: ~/ssl/example.com.crt - # key: ~/ssl/example.com.key + provider: letsencrypt cache: enabled: false duration: 30s diff --git a/group_vars/staging/wordpress_sites.yml b/group_vars/staging/wordpress_sites.yml index 1a7122fce9..e1711f587b 100644 --- a/group_vars/staging/wordpress_sites.yml +++ b/group_vars/staging/wordpress_sites.yml @@ -12,8 +12,7 @@ wordpress_sites: subdomains: false ssl: enabled: false - # cert: ~/ssl/example.com.crt - # key: ~/ssl/example.com.key + provider: letsencrypt cache: enabled: false duration: 30s diff --git a/roles/common/handlers/main.yml b/roles/common/handlers/main.yml index 9fa5b31ce3..f0abc92939 100644 --- a/roles/common/handlers/main.yml +++ b/roles/common/handlers/main.yml @@ -1,9 +1,15 @@ --- - name: restart memcached - service: name=memcached state=restarted - -- name: reload nginx - service: name=nginx state=reloaded + service: + name: memcached + state: restarted - name: reload php-fpm - service: name=php7.0-fpm state=reloaded + service: + name: php7.0-fpm + state: reloaded + +- name: reload nginx + service: + name: nginx + state: reloaded diff --git a/roles/letsencrypt/README.md b/roles/letsencrypt/README.md new file mode 100644 index 0000000000..55354b1042 --- /dev/null +++ b/roles/letsencrypt/README.md @@ -0,0 +1,9 @@ +# Let’s encrypt/acme-tiny role for Ansible + +## License + +MIT + +## Author Information + +This role was created by Andreas Wolf. Visit my [website](http://a-w.io) and [Github profile](https://github.com/andreaswolf/) or follow me on [Twitter](https://twitter.com/andreaswo). diff --git a/roles/letsencrypt/defaults/main.yml b/roles/letsencrypt/defaults/main.yml new file mode 100644 index 0000000000..8bbb5c38a1 --- /dev/null +++ b/roles/letsencrypt/defaults/main.yml @@ -0,0 +1,43 @@ +sites_using_letsencrypt: "[{% for name, site in wordpress_sites.iteritems() if site.ssl.enabled and site.ssl.provider | default('manual') == 'letsencrypt' %}'{{ name }}',{% endfor %}]" +letsencrypt_enabled: "{{ sites_using_letsencrypt | count > 0 }}" +site_uses_letsencrypt: "{{ item.value.ssl is defined and item.value.ssl.enabled | default(false) and item.value.ssl.provider | default('manual') == 'letsencrypt' }}" + +acme_tiny_repo: 'https://github.com/diafygi/acme-tiny.git' +acme_tiny_commit: '69a457269a6392ac31b629b4e103e8ea7dd282c9' + +acme_tiny_software_directory: /usr/local/letsencrypt +acme_tiny_data_directory: /var/lib/letsencrypt +acme_tiny_challenges_directory: "{{ www_root }}/letsencrypt" + +# Path to the local file containing the account key to copy to the server. +# Secure this file using Git-crypt for example. +# Leave this blank to generate a new account key that will need to be registered manually with Letsencrypt.org +#letsencrypt_account_key_source_file: /my/account.key + +# Content of the account key to copy to the server. +# Secure this key using Ansible Vault for example. +# Leave this blank to generate a new account key that will need to be registered manually with Letsencrypt.org +#letsencrypt_account_key_source_content: | +# -----BEGIN RSA PRIVATE KEY----- +# MIIJKAJBBBKCaGEA63J7t9dqyua5+Q+P6M3iHtLEKpF/AZcZNBHr1F2Oo8+Hfyvl +# KWXliiWjUORxDxI1c56Rw2VCIExnFjWJAdSLv6/XaQWo2T7U28bkKbAlCF9= +# -----END RSA PRIVATE KEY----- + +letsencrypt_ca: 'https://acme-v01.api.letsencrypt.org' + +letsencrypt_account_key: '{{ acme_tiny_data_directory }}/account.key' + +letsencrypt_intermediate_cert_path: /etc/ssl/certs/lets-encrypt-x1-cross-signed.pem +letsencrypt_intermediate_cert_url: 'https://letsencrypt.org/certs/lets-encrypt-x1-cross-signed.pem' +letsencrypt_intermediate_cert_sha256sum: '6c0a324bb803e9d66b8986ea2085bb9d6bdfe33f5c04a03a3f7024f4aa8e7a2d' + +letsencrypt_keys_dir: "{{ nginx_ssl_path }}/letsencrypt" +letsencrypt_certs_dir: "{{ nginx_ssl_path }}/letsencrypt" + +# the minimum age (in days) after which a certificate will be renewed +letsencrypt_min_renewal_age: 60 + +# the days of a month the cronjob should be run. Make sure to run it rather often, three times per month is a pretty +# good value. It does not harm to run it often, as it will only regenerate certificates that have passed a certain age +# (60 days by default). +letsencrypt_cronjob_daysofmonth: 1,11,21 diff --git a/roles/letsencrypt/tasks/certificates.yml b/roles/letsencrypt/tasks/certificates.yml new file mode 100644 index 0000000000..898c1bde0a --- /dev/null +++ b/roles/letsencrypt/tasks/certificates.yml @@ -0,0 +1,64 @@ +- name: Generate private keys + shell: openssl genrsa 4096 > {{ letsencrypt_keys_dir }}/{{ item.key }}.key + args: + creates: "{{ letsencrypt_keys_dir }}/{{ item.key }}.key" + when: site_uses_letsencrypt + with_dict: "{{ wordpress_sites }}" + tags: [letsencrypt_keys] + +- name: Ensure correct permissions on private keys + file: + path: "{{ letsencrypt_keys_dir }}/{{ item.key }}.key" + mode: 0600 + when: site_uses_letsencrypt + with_dict: "{{ wordpress_sites }}" + tags: [letsencrypt_keys] + +- name: Generate CSRs for single domain keys + shell: openssl req -new -sha256 -key "{{ letsencrypt_keys_dir }}/{{ item.key }}.key" -subj "/CN={{ item.value.site_hosts[0] }}" > {{ acme_tiny_data_directory }}/csrs/{{ item.key }}.csr + args: + creates: "{{ acme_tiny_data_directory }}/csrs/{{ item.key }}.csr" + when: site_uses_letsencrypt and item.value.site_hosts | length == 1 and not item.value.www_redirect | default(true) + with_dict: "{{ wordpress_sites }}" + tags: [letsencrypt_keys] + +- name: Generate CSRs for multi domain keys or single domain with "reverse www" domains + shell: "openssl req -new -sha256 -key '{{ letsencrypt_keys_dir }}/{{ item.key }}.key' -subj '/' -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf '[SAN]\nsubjectAltName=DNS:{{ item.value.site_hosts | join(',DNS:') }},DNS:{{ item.value.site_hosts | reverse_www | join(',DNS:') }}')) > {{ acme_tiny_data_directory }}/csrs/{{ item.key }}.csr" + args: + executable: /bin/bash + creates: "{{ acme_tiny_data_directory }}/csrs/{{ item.key }}.csr" + when: site_uses_letsencrypt and item.value.www_redirect | default(true) + with_dict: "{{ wordpress_sites }}" + tags: [letsencrypt_keys] + +- name: Generate CSRs for multi domain keys without "reverse www" domains + shell: "openssl req -new -sha256 -key '{{ letsencrypt_keys_dir }}/{{ item.key }}.key' -subj '/' -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf '[SAN]\nsubjectAltName=DNS:{{ item.value.site_hosts | join(',DNS:') }}')) > {{ acme_tiny_data_directory }}/csrs/{{ item.key }}.csr" + args: + executable: /bin/bash + creates: "{{ acme_tiny_data_directory }}/csrs/{{ item.key }}.csr" + when: site_uses_letsencrypt and item.value.site_hosts | length > 1 and not item.value.www_redirect | default(true) + with_dict: "{{ wordpress_sites }}" + tags: [letsencrypt_keys] + +- name: Generate the initial certificate + command: ./renew-certs.py + args: + chdir: "{{ acme_tiny_data_directory }}" + register: generate_initial_cert + changed_when: generate_initial_cert.stdout is defined and 'Created' in generate_initial_cert.stdout + +- name: Disable Nginx site + file: + path: "{{ nginx_path }}/sites-enabled/letsencrypt-{{ item.key }}.conf" + state: absent + with_dict: "{{ wordpress_sites }}" + +- name: Test nginx conf + command: nginx -t + changed_when: false + +- name: Trigger nginx reload + service: + name: nginx + state: reloaded + changed_when: false diff --git a/roles/letsencrypt/tasks/main.yml b/roles/letsencrypt/tasks/main.yml new file mode 100644 index 0000000000..0abf592760 --- /dev/null +++ b/roles/letsencrypt/tasks/main.yml @@ -0,0 +1,14 @@ +- include: setup.yml +- include: nginx.yml +- include: certificates.yml + +- name: Install cronjob for key generation + cron: + cron_file: letsencrypt-certificate-renewal + name: letsencrypt certificate renewal + user: root + job: cd {{ acme_tiny_data_directory }} && ./renew-certs.py + day: "{{ letsencrypt_cronjob_daysofmonth }}" + hour: 4 + minute: 30 + state: present diff --git a/roles/letsencrypt/tasks/nginx.yml b/roles/letsencrypt/tasks/nginx.yml new file mode 100644 index 0000000000..25bdf1a1ba --- /dev/null +++ b/roles/letsencrypt/tasks/nginx.yml @@ -0,0 +1,55 @@ +- name: Check for existing certificates + stat: + path: "{{ letsencrypt_certs_dir }}/{{ item.key }}.cert" + register: letsencrypt_existing_certs + when: site_uses_letsencrypt + with_dict: "{{ wordpress_sites }}" + +- name: Create Nginx sites for challenges + template: + src: nginx-challenge-site.conf.j2 + dest: "{{ nginx_path }}/sites-available/letsencrypt-{{ item.item.key }}.conf" + when: not item | skipped and not item.stat.exists + with_items: "{{ letsencrypt_existing_certs.results }}" + +- name: Enable Nginx site + file: + src: "{{ nginx_path }}/sites-available/letsencrypt-{{ item.item.key }}.conf" + dest: "{{ nginx_path }}/sites-enabled/letsencrypt-{{ item.item.key }}.conf" + state: link + when: not item | skipped and not item.stat.exists + with_items: "{{ letsencrypt_existing_certs.results }}" + +- name: Trigger nginx reload + service: + name: nginx + state: reloaded + changed_when: false + +- name: Create test Acme Challenge file + shell: touch {{ acme_tiny_challenges_directory }}/ping.txt + args: + creates: "{{ acme_tiny_challenges_directory }}/ping.txt" + warn: false + +- name: Test Acme Challenges + command: curl -s -o /dev/null -w "%{http_code}" http://{{ item.1 }}/.well-known/acme-challenge/ping.txt + args: + warn: false + register: letsencrypt_test_challenges + when: item.0.ssl is defined and item.0.ssl.enabled | default(false) and item.0.ssl.provider | default('manual') == 'letsencrypt' + changed_when: false + with_subelements: + - "{{ wordpress_sites }}" + - site_hosts + +- name: Notify of challenge failures + fail: + msg: > + Could not access the challenge file for the domain: {{ item.item.1 }}. + Let's Encrypt requires every domain/host be publicly accessible. + Make sure that a valid DNS record exists for {{ item.item.1 }} and that it points to this server's IP. + If you don't want this domain in your SSL certificate, then remove it from `site_hosts`. + See https://roots.io/trellis/docs/ssl for more details. + when: not item | skipped and item.stdout | int != 200 + with_items: letsencrypt_test_challenges.results diff --git a/roles/letsencrypt/tasks/setup.yml b/roles/letsencrypt/tasks/setup.yml new file mode 100644 index 0000000000..b9e3048f41 --- /dev/null +++ b/roles/letsencrypt/tasks/setup.yml @@ -0,0 +1,56 @@ +- name: Create directories and set permissions + file: + mode: "{{ item.mode | default(omit) }}" + path: "{{ item.path }}" + state: directory + with_items: + - path: "{{ acme_tiny_data_directory }}" + mode: '0700' + - path: "{{ acme_tiny_data_directory }}/csrs" + - path: "{{ acme_tiny_software_directory }}" + - path: "{{ acme_tiny_challenges_directory }}" + - path: "{{ letsencrypt_certs_dir }}" + mode: '0700' + +- name: Clone acme-tiny repository + git: + dest: "{{ acme_tiny_software_directory }}" + repo: "{{ acme_tiny_repo }}" + version: "{{ acme_tiny_commit }}" + accept_hostkey: yes + +- name: Copy Lets Encrypt account key source file + copy: + src: "{{ letsencrypt_account_key_source_file }}" + dest: "{{ letsencrypt_account_key }}" + when: letsencrypt_account_key_source_file is defined + +- name: Copy Lets Encrypt account key source contents + copy: + content: "{{ letsencrypt_account_key_source_content | trim }}" + dest: "{{ letsencrypt_account_key }}" + when: letsencrypt_account_key_source_content is defined + +- name: Generate a new account key + shell: openssl genrsa 4096 > {{ letsencrypt_account_key }} + args: + creates: "{{ letsencrypt_account_key }}" + register: generate_account_key + when: letsencrypt_account_key_source_content is not defined and letsencrypt_account_key_source_file is not defined + +- name: Generate certificate renewal script + template: + src: renew-certs.py + dest: "{{ acme_tiny_data_directory }}/renew-certs.py" + mode: 0700 + +- name: Download intermediate certificate + get_url: + url: "{{ letsencrypt_intermediate_cert_url }}" + dest: "{{ letsencrypt_intermediate_cert_path }}" + sha256sum: "{{ letsencrypt_intermediate_cert_sha256sum }}" + +- name: Create Nginx conf for challenges location + template: + src: acme-challenge-location.conf.j2 + dest: "{{ nginx_path }}/acme-challenge-location.conf" diff --git a/roles/letsencrypt/templates/acme-challenge-location.conf.j2 b/roles/letsencrypt/templates/acme-challenge-location.conf.j2 new file mode 100644 index 0000000000..1a30ce0d7a --- /dev/null +++ b/roles/letsencrypt/templates/acme-challenge-location.conf.j2 @@ -0,0 +1,4 @@ +location ^~ /.well-known/acme-challenge/ { + alias {{ acme_tiny_challenges_directory }}/; + try_files $uri =404; +} diff --git a/roles/letsencrypt/templates/nginx-challenge-site.conf.j2 b/roles/letsencrypt/templates/nginx-challenge-site.conf.j2 new file mode 100644 index 0000000000..aead863f7e --- /dev/null +++ b/roles/letsencrypt/templates/nginx-challenge-site.conf.j2 @@ -0,0 +1,11 @@ +server { + listen 80; + + {% if item.item.value.www_redirect | default(true) -%} + server_name {{ item.item.value.site_hosts | join(' ') }} {{ item.item.value.site_hosts | reverse_www | join(' ') }}; + {% else -%} + server_name {{ item.item.value.site_hosts | join(' ') }}; + {% endif %} + + include acme-challenge-location.conf; +} diff --git a/roles/letsencrypt/templates/renew-certs.py b/roles/letsencrypt/templates/renew-certs.py new file mode 100644 index 0000000000..06a01b61fb --- /dev/null +++ b/roles/letsencrypt/templates/renew-certs.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python + +import os +import sys +import time + +from subprocess import CalledProcessError, check_output, STDOUT + +certs_dir = '{{ letsencrypt_certs_dir }}' +failed = False +sites = {{ wordpress_sites }} +sites = (k for k, v in sites.items() if 'ssl' in v and v['ssl'].get('enabled', False) and v['ssl'].get('provider', 'manual') == 'letsencrypt') + +for site in sites: + cert_path = os.path.join(certs_dir, site + '.cert') + bundled_cert_path = os.path.join(certs_dir, site + '-bundled.cert') + + if os.access(cert_path, os.F_OK): + stat = os.stat(cert_path) + print 'Certificate file ' + cert_path + ' already exists' + + if time.time() - stat.st_mtime < {{ letsencrypt_min_renewal_age }} * 86400: + print ' The certificate is younger than {{ letsencrypt_min_renewal_age }} days. Not creating a new certificate.\n' + continue + + print 'Generating certificate for ' + site + + cmd = ('/usr/bin/env python {{ acme_tiny_software_directory }}/acme_tiny.py ' + '--ca {{ letsencrypt_ca }} ' + '--account-key {{ letsencrypt_account_key }} ' + '--csr {{ acme_tiny_data_directory }}/csrs/{0}.csr ' + '--acme-dir {{ acme_tiny_challenges_directory }}' + ).format(site) + + try: + cert = check_output(cmd, stderr=STDOUT, shell=True) + except CalledProcessError as e: + failed = True + print 'Error while generating certificate for ' + site + print e.output + else: + with open(cert_path, 'w') as cert_file: + cert_file.write(cert) + + with open('{{ letsencrypt_intermediate_cert_path }}') as intermediate_cert_file: + intermediate_cert = intermediate_cert_file.read() + + with open(bundled_cert_path, 'w') as bundled_file: + bundled_file.write(''.join([cert, intermediate_cert])) + + print 'Created certificate for ' + site + +if failed: + sys.exit(1) diff --git a/roles/nginx/defaults/main.yml b/roles/nginx/defaults/main.yml index a53ab382ae..d0d01b30a4 100644 --- a/roles/nginx/defaults/main.yml +++ b/roles/nginx/defaults/main.yml @@ -3,6 +3,7 @@ nginx_path: /etc/nginx nginx_logs_root: /var/log/nginx nginx_user: www-data nginx_fastcgi_buffers: 8 8k +nginx_ssl_path: "{{ nginx_path }}/ssl" # HSTS defaults nginx_hsts_max_age: 31536000 diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml index a06b55a573..916b56539b 100644 --- a/roles/nginx/tasks/main.yml +++ b/roles/nginx/tasks/main.yml @@ -12,7 +12,8 @@ - name: Create SSL directory file: - dest: "{{ nginx_path }}/ssl" + mode: 0700 + path: "{{ nginx_path }}/ssl" state: directory - name: Generate strong unique Diffie-Hellman group. diff --git a/roles/users/tasks/main.yml b/roles/users/tasks/main.yml index 147531bff9..9745bbf28a 100644 --- a/roles/users/tasks/main.yml +++ b/roles/users/tasks/main.yml @@ -47,7 +47,7 @@ user: "{{ item.0.name }}" key: "{{ item.1 }}" with_subelements: - - users | default([]) + - "{{ users | default([]) }}" - keys - name: Check whether Ansible can connect as admin_user diff --git a/roles/wordpress-setup/tasks/main.yml b/roles/wordpress-setup/tasks/main.yml index 97bc1e92b2..150abd9cc3 100644 --- a/roles/wordpress-setup/tasks/main.yml +++ b/roles/wordpress-setup/tasks/main.yml @@ -3,8 +3,6 @@ tags: wordpress-setup-database - include: self-signed-certificate.yml tags: wordpress-setup-self-signed-certificate -- include: nginx.yml - tags: wordpress-setup-nginx - name: Create web root file: @@ -24,6 +22,9 @@ state: directory with_dict: "{{ wordpress_sites }}" +- include: nginx.yml + tags: wordpress-setup-nginx + - name: Setup WP system cron cron: name: "{{ item.key }} WordPress cron" diff --git a/roles/wordpress-setup/tasks/nginx.yml b/roles/wordpress-setup/tasks/nginx.yml index 632b459a87..60c00767d6 100644 --- a/roles/wordpress-setup/tasks/nginx.yml +++ b/roles/wordpress-setup/tasks/nginx.yml @@ -2,22 +2,22 @@ - name: Copy SSL cert copy: src: "{{ item.value.ssl.cert }}" - dest: /etc/nginx/ssl/{{ item.value.ssl.cert | basename }} + dest: "{{ nginx_ssl_path }}/{{ item.value.ssl.cert | basename }}" mode: 0640 with_dict: "{{ wordpress_sites }}" - when: item.value.ssl.enabled and item.value.ssl.cert is defined | default(False) + when: item.value.ssl.enabled and item.value.ssl.cert is defined - name: Copy SSL key copy: src: "{{ item.value.ssl.key }}" - dest: /etc/nginx/ssl/{{ item.value.ssl.key | basename }} + dest: "{{ nginx_ssl_path }}/{{ item.value.ssl.key | basename }}" mode: 0600 with_dict: "{{ wordpress_sites }}" - when: item.value.ssl.enabled and item.value.ssl.key is defined | default(False) + when: item.value.ssl.enabled and item.value.ssl.key is defined - name: Create includes.d directories file: - path: "/etc/nginx/includes.d/{{ item }}" + path: "{{ nginx_path }}/includes.d/{{ item }}" state: directory mode: 0755 with_items: "{{ wordpress_sites.keys() }}" @@ -26,7 +26,7 @@ - name: Template files out to includes.d template: src: "includes.d/{{ item }}" - dest: "/etc/nginx/includes.d/{{ item[:-3] }}" + dest: "{{ nginx_path }}/includes.d/{{ item[:-3] }}" with_lines: "cd {{ role_path }}/templates/includes.d && find {{ wordpress_sites.keys() | join(' ') }} -type f -name \\*.conf.j2 2>/dev/null || :" register: nginx_includes_managed notify: reload nginx @@ -50,16 +50,25 @@ - name: Create WordPress configuration for Nginx template: src: "wordpress-site.conf.j2" - dest: "/etc/nginx/sites-available/{{ item.key }}.conf" + dest: "{{ nginx_path }}/sites-available/{{ item.key }}.conf" with_dict: "{{ wordpress_sites }}" notify: reload nginx - name: Enable WordPress site file: - src: "/etc/nginx/sites-available/{{ item.key }}.conf" - dest: "/etc/nginx/sites-enabled/{{ item.key }}.conf" + src: "{{ nginx_path }}/sites-available/{{ item.key }}.conf" + dest: "{{ nginx_path }}/sites-enabled/{{ item.key }}.conf" owner: root group: root state: link with_dict: "{{ wordpress_sites }}" - notify: reload nginx + +- name: test nginx conf + command: nginx -t + changed_when: false + +- name: trigger nginx reload + service: + name: nginx + state: reloaded + changed_when: false diff --git a/roles/wordpress-setup/tasks/self-signed-certificate.yml b/roles/wordpress-setup/tasks/self-signed-certificate.yml index 81089f3b85..9dda3e33d5 100644 --- a/roles/wordpress-setup/tasks/self-signed-certificate.yml +++ b/roles/wordpress-setup/tasks/self-signed-certificate.yml @@ -1,28 +1,13 @@ --- -- name: Get existing self-signed certificates - shell: find . -name "*_self_signed.pem" - args: - chdir: /etc/nginx/ssl/ - register: self_signed_certs - changed_when: false - -- name: Get self-signed certificates domains - shell: openssl x509 -noout -subject -in {{ item | quote }} | sed -n "/^subject/s/^.*CN=//p" - register: self_signed_domains - args: - chdir: /etc/nginx/ssl/ - with_items: "{{ self_signed_certs.stdout_lines }}" - changed_when: false - - name: Generate self-signed certificates shell: > openssl req -subj "/CN={{ item.value.site_hosts | first }}" -new -newkey rsa:2048 -days 3650 -nodes -x509 -sha256 - -keyout {{ item.key }}_self_signed.key -out {{ item.key }}_self_signed.pem + -keyout {{ item.key }}.key -out {{ item.key }}.cert args: - chdir: /etc/nginx/ssl/ + chdir: "{{ nginx_path }}/ssl" + creates: "{{ item.key }}.*" with_dict: "{{ wordpress_sites }}" - when: > - item.value.ssl.enabled and item.value.site_hosts | first - not in self_signed_domains.results | default([]) | map(attribute='stdout') | list - notify: reload nginx + when: item.value.ssl.enabled and item.value.ssl.provider | default('manual') == 'self-signed' + notify: + - reload nginx diff --git a/roles/wordpress-setup/templates/wordpress-site.conf.j2 b/roles/wordpress-setup/templates/wordpress-site.conf.j2 index 4be6bdfd0a..1f3c941264 100644 --- a/roles/wordpress-setup/templates/wordpress-site.conf.j2 +++ b/roles/wordpress-setup/templates/wordpress-site.conf.j2 @@ -1,7 +1,7 @@ # {{ ansible_managed }} server { - {% if item.value.ssl is defined and item.value.ssl.enabled | default(false) %} + {% if item.value.ssl is defined and item.value.ssl.enabled | default(false) -%} listen 443 ssl http2; {% else -%} listen 80; @@ -32,7 +32,7 @@ server { add_header Fastcgi-Cache $upstream_cache_status; - {% if item.value.ssl is defined and item.value.ssl.enabled | default(false) %} + {% if item.value.ssl is defined and item.value.ssl.enabled | default(false) -%} include h5bp/directive-only/ssl.conf; include h5bp/directive-only/ssl-stapling.conf; @@ -44,15 +44,17 @@ server { {% set hsts_preload = item.value.ssl.hsts_preload | default(nginx_hsts_preload) | ternary('preload', None) %} add_header Strict-Transport-Security "max-age={{ [hsts_max_age, hsts_include_subdomains, hsts_preload] | reject('none') | join('; ') }}"; - {% if item.value.ssl.cert is defined and item.value.ssl.key is defined -%} - ssl_certificate /etc/nginx/ssl/{{ item.value.ssl.cert | basename }}; - ssl_trusted_certificate /etc/nginx/ssl/{{ item.value.ssl.cert | basename }}; - ssl_certificate_key /etc/nginx/ssl/{{ item.value.ssl.key | basename }}; - {% else -%} - ssl_certificate /etc/nginx/ssl/{{ item.key }}_self_signed.pem; - ssl_trusted_certificate /etc/nginx/ssl/{{ item.key }}_self_signed.pem; - ssl_certificate_key /etc/nginx/ssl/{{ item.key }}_self_signed.key; - {% endif -%} + {% if item.value.ssl.provider | default('manual') == 'manual' and item.value.ssl.cert is defined and item.value.ssl.key is defined -%} + ssl_certificate {{ nginx_path }}/ssl/{{ item.value.ssl.cert | basename }}; + ssl_certificate_key {{ nginx_path }}/ssl/{{ item.value.ssl.key | basename }}; + {%- elif item.value.ssl.provider | default('manual') == 'letsencrypt' -%} + ssl_certificate {{ nginx_path }}/ssl/letsencrypt/{{ item.key }}-bundled.cert; + ssl_certificate_key {{ nginx_path }}/ssl/letsencrypt/{{ item.key }}.key; + {%- elif item.value.ssl.provider | default('manual') == 'self-signed' -%} + ssl_certificate {{ nginx_path }}/ssl/{{ item.key }}.cert; + ssl_trusted_certificate {{ nginx_path }}/ssl/{{ item.key }}.cert; + ssl_certificate_key {{ nginx_path }}/ssl/{{ item.key }}.key; + {%- endif -%} {% endif %} include includes.d/{{ item.key }}/*.conf; @@ -95,8 +97,22 @@ server { {% if item.value.ssl is defined and item.value.ssl.enabled | default(False) %} server { listen 80; + + {% if item.value.www_redirect | default(true) -%} server_name {{ item.value.site_hosts | join(' ') }} {{ item.value.site_hosts | reverse_www | join(' ') }}; + {% else -%} + server_name {{ item.value.site_hosts | join(' ') }}; + {% endif %} + + {% if item.value.ssl.provider | default('manual') == 'letsencrypt' -%} + include acme-challenge-location.conf; + + location / { + return 301 https://$host$request_uri; + } + {% else %} return 301 https://$host$request_uri; + {% endif -%} } {% endif %} diff --git a/server.yml b/server.yml index b9e6f6968a..f9dc2e3c09 100644 --- a/server.yml +++ b/server.yml @@ -28,4 +28,5 @@ - { role: logrotate, tags: [logrotate] } - { role: composer, tags: [composer] } - { role: wp-cli, tags: [wp-cli] } + - { role: letsencrypt, tags: [letsencrypt], when: letsencrypt_enabled } - { role: wordpress-setup, tags: [wordpress, wordpress-setup] } From ec9b11558fcb311866e89646c85ce41e8ddbfb6c Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Tue, 22 Mar 2016 19:12:14 -0400 Subject: [PATCH 07/52] Add #521 to CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e065995d9f..f8581c1d6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* Fix #520 - Disable MariaDB binary logging by default ([#521](https://github.com/roots/trellis/pull/521)) * Let's Encrypt integration ([#518](https://github.com/roots/trellis/pull/518)) * Improve Git repo format validation [#516](https://github.com/roots/trellis/pull/516) * Fix #505 - Git ignore \*.retry file From 00c4e72287315f23f4503200dd14fc98fd3cd688 Mon Sep 17 00:00:00 2001 From: Andreas Mokros Date: Tue, 22 Mar 2016 02:42:31 +0100 Subject: [PATCH 08/52] Fix some bare vars in deploy role and updated WP-CLI version --- roles/deploy/tasks/build.yml | 6 +++--- roles/deploy/tasks/share.yml | 6 +++--- roles/wp-cli/defaults/main.yml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/roles/deploy/tasks/build.yml b/roles/deploy/tasks/build.yml index 2211def10f..38f20d9974 100644 --- a/roles/deploy/tasks/build.yml +++ b/roles/deploy/tasks/build.yml @@ -7,17 +7,17 @@ src: "{{ item.src }}" dest: "{{ deploy_helper.new_release_path }}/{{ item.dest }}" mode: "{{ item.mode | default('0644') }}" - with_items: project_templates + with_items: "{{ project_templates }}" - name: Check if project folders exist stat: path: "{{ deploy_helper.current_path }}/{{ item }}" register: project_folder_paths - with_items: project_copy_folders + with_items: "{{ project_copy_folders }}" - name: Copy project folders command: cp -rp {{ deploy_helper.current_path }}/{{ item.item }} {{ deploy_helper.new_release_path }} - with_items: project_folder_paths.results + with_items: "{{ project_folder_paths.results }}" when: item.stat.exists - include: "{{ deploy_build_after | default('../hooks/example.yml') }}" diff --git a/roles/deploy/tasks/share.yml b/roles/deploy/tasks/share.yml index 49dbacb85a..aa855834eb 100644 --- a/roles/deploy/tasks/share.yml +++ b/roles/deploy/tasks/share.yml @@ -7,20 +7,20 @@ path: "{{ deploy_helper.shared_path }}/{{ item.src }}" state: "{{ item.type | default('directory') }}" mode: "{{ item.mode | default('0755') }}" - with_items: project_shared_children + with_items: "{{ project_shared_children }}" - name: Ensure shared paths are absent file: path: "{{ deploy_helper.new_release_path }}/{{ item.path }}" state: absent - with_items: project_shared_children + with_items: "{{ project_shared_children }}" - name: Create shared symlinks file: path: "{{ deploy_helper.new_release_path }}/{{ item.path }}" src: "{{ deploy_helper.shared_path }}/{{ item.src }}" state: link - with_items: project_shared_children + with_items: "{{ project_shared_children }}" - include: "{{ deploy_share_after | default('../hooks/example.yml') }}" tags: deploy-share-after diff --git a/roles/wp-cli/defaults/main.yml b/roles/wp-cli/defaults/main.yml index eea5dde5eb..456dbaa2ed 100644 --- a/roles/wp-cli/defaults/main.yml +++ b/roles/wp-cli/defaults/main.yml @@ -1,4 +1,4 @@ wp_cli_bin_path: /usr/bin/wp -wp_cli_phar_url: "https://github.com/wp-cli/wp-cli/releases/download/v0.21.1/wp-cli-0.21.1.phar" +wp_cli_phar_url: "https://github.com/wp-cli/wp-cli/releases/download/v0.23.0/wp-cli-0.23.0.phar" wp_cli_completion_url: "https://raw.githubusercontent.com/wp-cli/wp-cli/master/utils/wp-completion.bash" wp_cli_completion_path: /etc/bash_completion.d From dec6bbc5ccf7fab99fa795e5d30fc6f3ccb3b11e Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Thu, 24 Mar 2016 17:22:13 -0400 Subject: [PATCH 09/52] `reverse_www` filter improvements * `enabled` option - lets you use an Ansible var to enable/disable it * `append` option - append reversed hosts or only return reversed hosts * Ignore subdomains - `www` should not be added for subdomains --- CHANGELOG.md | 1 + filter_plugins/trellis_filters.py | 34 ++++++++++++------- roles/letsencrypt/tasks/certificates.yml | 13 ++----- .../templates/nginx-challenge-site.conf.j2 | 8 +---- .../templates/wordpress-site.conf.j2 | 10 ++---- 5 files changed, 29 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8581c1d6f..7ce7814c00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* `reverse_www` filter improvements (ignore subdomains) ([#525](https://github.com/roots/trellis/pull/525)) * Fix #520 - Disable MariaDB binary logging by default ([#521](https://github.com/roots/trellis/pull/521)) * Let's Encrypt integration ([#518](https://github.com/roots/trellis/pull/518)) * Improve Git repo format validation [#516](https://github.com/roots/trellis/pull/516) diff --git a/filter_plugins/trellis_filters.py b/filter_plugins/trellis_filters.py index 91c5485e83..8eca6de8d1 100644 --- a/filter_plugins/trellis_filters.py +++ b/filter_plugins/trellis_filters.py @@ -7,26 +7,36 @@ from ansible import errors from ansible.compat.six import string_types -def reverse_www(value): +def reverse_www(hosts, enabled=True, append=True): ''' Add or remove www subdomain ''' - # Check if value is a list and parse each item - if isinstance(value, (list, tuple, types.GeneratorType)): - values = [] - for item in value: - values.append(reverse_www(item)) - return values + if not enabled: + return hosts + + # Check if hosts is a list and parse each host + if isinstance(hosts, (list, tuple, types.GeneratorType)): + reversed_hosts = [reverse_www(host) for host in hosts] + + if append: + return list(set(hosts + reversed_hosts)) + else: + return reversed_hosts # Add or remove www - elif isinstance(value, string_types): - if value.startswith('www.'): - return value[4:] + elif isinstance(hosts, string_types): + host = hosts + + if len(host.split('.')) > 2: + return host + + if host.startswith('www.'): + return host[4:] else: - return 'www.{0}'.format(value) + return 'www.{0}'.format(host) # Handle invalid input type else: - raise errors.AnsibleFilterError('The reverse_www filter expects a string or list of strings, got ' + repr(value)) + raise errors.AnsibleFilterError('The reverse_www filter expects a string or list of strings, got ' + repr(hosts)) class FilterModule(object): diff --git a/roles/letsencrypt/tasks/certificates.yml b/roles/letsencrypt/tasks/certificates.yml index 898c1bde0a..c0ce8eb5ad 100644 --- a/roles/letsencrypt/tasks/certificates.yml +++ b/roles/letsencrypt/tasks/certificates.yml @@ -22,8 +22,8 @@ with_dict: "{{ wordpress_sites }}" tags: [letsencrypt_keys] -- name: Generate CSRs for multi domain keys or single domain with "reverse www" domains - shell: "openssl req -new -sha256 -key '{{ letsencrypt_keys_dir }}/{{ item.key }}.key' -subj '/' -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf '[SAN]\nsubjectAltName=DNS:{{ item.value.site_hosts | join(',DNS:') }},DNS:{{ item.value.site_hosts | reverse_www | join(',DNS:') }}')) > {{ acme_tiny_data_directory }}/csrs/{{ item.key }}.csr" +- name: Generate CSRs for multiple domain keys + shell: "openssl req -new -sha256 -key '{{ letsencrypt_keys_dir }}/{{ item.key }}.key' -subj '/' -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf '[SAN]\nsubjectAltName=DNS:{{ item.value.site_hosts | reverse_www(enabled=item.value.www_redirect | default(true)) | join(',DNS:') }}')) > {{ acme_tiny_data_directory }}/csrs/{{ item.key }}.csr" args: executable: /bin/bash creates: "{{ acme_tiny_data_directory }}/csrs/{{ item.key }}.csr" @@ -31,15 +31,6 @@ with_dict: "{{ wordpress_sites }}" tags: [letsencrypt_keys] -- name: Generate CSRs for multi domain keys without "reverse www" domains - shell: "openssl req -new -sha256 -key '{{ letsencrypt_keys_dir }}/{{ item.key }}.key' -subj '/' -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf '[SAN]\nsubjectAltName=DNS:{{ item.value.site_hosts | join(',DNS:') }}')) > {{ acme_tiny_data_directory }}/csrs/{{ item.key }}.csr" - args: - executable: /bin/bash - creates: "{{ acme_tiny_data_directory }}/csrs/{{ item.key }}.csr" - when: site_uses_letsencrypt and item.value.site_hosts | length > 1 and not item.value.www_redirect | default(true) - with_dict: "{{ wordpress_sites }}" - tags: [letsencrypt_keys] - - name: Generate the initial certificate command: ./renew-certs.py args: diff --git a/roles/letsencrypt/templates/nginx-challenge-site.conf.j2 b/roles/letsencrypt/templates/nginx-challenge-site.conf.j2 index aead863f7e..7b80c271ce 100644 --- a/roles/letsencrypt/templates/nginx-challenge-site.conf.j2 +++ b/roles/letsencrypt/templates/nginx-challenge-site.conf.j2 @@ -1,11 +1,5 @@ server { listen 80; - - {% if item.item.value.www_redirect | default(true) -%} - server_name {{ item.item.value.site_hosts | join(' ') }} {{ item.item.value.site_hosts | reverse_www | join(' ') }}; - {% else -%} - server_name {{ item.item.value.site_hosts | join(' ') }}; - {% endif %} - + server_name {{ item.item.value.site_hosts | reverse_www(enabled=item.item.value.www_redirect | default(true)) | join(' ') }}; include acme-challenge-location.conf; } diff --git a/roles/wordpress-setup/templates/wordpress-site.conf.j2 b/roles/wordpress-setup/templates/wordpress-site.conf.j2 index 1f3c941264..ec016c6702 100644 --- a/roles/wordpress-setup/templates/wordpress-site.conf.j2 +++ b/roles/wordpress-setup/templates/wordpress-site.conf.j2 @@ -94,15 +94,11 @@ server { } } -{% if item.value.ssl is defined and item.value.ssl.enabled | default(False) %} +{% if item.value.ssl is defined and item.value.ssl.enabled | default(false) %} server { listen 80; - {% if item.value.www_redirect | default(true) -%} - server_name {{ item.value.site_hosts | join(' ') }} {{ item.value.site_hosts | reverse_www | join(' ') }}; - {% else -%} - server_name {{ item.value.site_hosts | join(' ') }}; - {% endif %} + server_name {{ item.value.site_hosts | reverse_www(enabled=item.value.www_redirect | default(true)) | join(' ') }}; {% if item.value.ssl.provider | default('manual') == 'letsencrypt' -%} include acme-challenge-location.conf; @@ -124,7 +120,7 @@ server { listen 80; {% endif -%} - server_name {{ host | reverse_www }}; + server_name {{ host | reverse_www(append=false) }}; return 301 $scheme://{{ host }}$request_uri; } {% endfor %} From c0bb060b1f32c026003d24a71d2386b8e5feaa59 Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Thu, 24 Mar 2016 17:21:17 -0400 Subject: [PATCH 10/52] Add and use custom `test_challenges` module --- CHANGELOG.md | 1 + roles/letsencrypt/library/test_challenges.py | 82 ++++++++++++++++++++ roles/letsencrypt/tasks/nginx.yml | 23 +++--- 3 files changed, 93 insertions(+), 13 deletions(-) create mode 100644 roles/letsencrypt/library/test_challenges.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ce7814c00..28ce8e6e7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* Improve Let's Encrypt challenge pre-flight tests ([#526](https://github.com/roots/trellis/pull/526)) * `reverse_www` filter improvements (ignore subdomains) ([#525](https://github.com/roots/trellis/pull/525)) * Fix #520 - Disable MariaDB binary logging by default ([#521](https://github.com/roots/trellis/pull/521)) * Let's Encrypt integration ([#518](https://github.com/roots/trellis/pull/518)) diff --git a/roles/letsencrypt/library/test_challenges.py b/roles/letsencrypt/library/test_challenges.py new file mode 100644 index 0000000000..8c075cc5a8 --- /dev/null +++ b/roles/letsencrypt/library/test_challenges.py @@ -0,0 +1,82 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import socket +from httplib import HTTPConnection, HTTPException + +DOCUMENTATION = ''' +--- +module: test_challenges +short_description: Tests Let's Encrypt web server challenges +description: + - The M(test_challenges) module verifies a list of hosts can access acme challenges for Let's Encrypt. +options: + hosts: + description: + - A list of hostnames/domains to test. + required: true + default: null + type: list + file: + description: + - The dummy filename in the URL to test. + required: no + default: ping.txt + path: + description: + - The path to the challenges in the URL. + required: no + default: /.well-known/acme-challenge +author: + - Scott Walkinshaw +''' + +EXAMPLES = ''' +# Example from Ansible Playbooks. +- test_challenges: + hosts: + - example.com + - www.example.com + - www.mydomain.com +''' + +def get_status(host, path, file): + try: + conn = HTTPConnection(host) + conn.request('HEAD', '/{0}/{1}'.format(path, file)) + res = conn.getresponse() + except (HTTPException, socket.timeout, socket.error): + return 0 + else: + return res.status + +def main(): + module = AnsibleModule( + argument_spec = dict( + file = dict(default='ping.txt'), + hosts = dict(required=True, type='list'), + path = dict(default='.well-known/acme-challenge') + ) + ) + + hosts = module.params['hosts'] + path = module.params['path'] + file = module.params['file'] + + failed_hosts = [] + + for host in hosts: + status = get_status(host, path, file) + if int(status) != 200: + failed_hosts.append(host) + + rc = int(len(failed_hosts) > 0) + + module.exit_json( + changed=False, + rc=rc, + failed_hosts=failed_hosts + ) + +from ansible.module_utils.basic import * +main() diff --git a/roles/letsencrypt/tasks/nginx.yml b/roles/letsencrypt/tasks/nginx.yml index 25bdf1a1ba..f1c87642ea 100644 --- a/roles/letsencrypt/tasks/nginx.yml +++ b/roles/letsencrypt/tasks/nginx.yml @@ -33,23 +33,20 @@ warn: false - name: Test Acme Challenges - command: curl -s -o /dev/null -w "%{http_code}" http://{{ item.1 }}/.well-known/acme-challenge/ping.txt - args: - warn: false + test_challenges: + hosts: "{{ item.value.site_hosts | reverse_www(enabled=item.value.www_redirect | default(true)) }}" register: letsencrypt_test_challenges - when: item.0.ssl is defined and item.0.ssl.enabled | default(false) and item.0.ssl.provider | default('manual') == 'letsencrypt' - changed_when: false - with_subelements: - - "{{ wordpress_sites }}" - - site_hosts + ignore_errors: true + when: item.value.ssl is defined and item.value.ssl.enabled | default(false) and item.value.ssl.provider | default('manual') == 'letsencrypt' + with_dict: "{{ wordpress_sites }}" - name: Notify of challenge failures fail: msg: > - Could not access the challenge file for the domain: {{ item.item.1 }}. + Could not access the challenge file for the hosts/domains: {{ item.failed_hosts | join(', ') }}. Let's Encrypt requires every domain/host be publicly accessible. - Make sure that a valid DNS record exists for {{ item.item.1 }} and that it points to this server's IP. - If you don't want this domain in your SSL certificate, then remove it from `site_hosts`. + Make sure that a valid DNS record exists for {{ item.failed_hosts | join(', ') }} and that they point to this server's IP. + If you don't want these domains in your SSL certificate, then remove them from `site_hosts`. See https://roots.io/trellis/docs/ssl for more details. - when: not item | skipped and item.stdout | int != 200 - with_items: letsencrypt_test_challenges.results + when: letsencrypt_test_challenges | failed + with_items: "{{ letsencrypt_test_challenges.results }}" From c83a886ec49a985e7e607f0fd112329ebbc79d0e Mon Sep 17 00:00:00 2001 From: Matthew Henkler Date: Fri, 25 Mar 2016 11:09:05 -0500 Subject: [PATCH 11/52] Add WinNFSD option for better Windows performance using Virtualbox --- Vagrantfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Vagrantfile b/Vagrantfile index 78e689da48..ccb6799b06 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -54,7 +54,7 @@ Vagrant.configure('2') do |config| fail_with_message "vagrant-hostmanager missing, please install the plugin with this command:\nvagrant plugin install vagrant-hostmanager" end - if Vagrant::Util::Platform.windows? + if Vagrant::Util::Platform.windows? and !Vagrant.has_plugin? 'vagrant-winnfsd' wordpress_sites.each_pair do |name, site| config.vm.synced_folder local_site_path(site), remote_site_path(name), owner: 'vagrant', group: 'www-data', mount_options: ['dmode=776', 'fmode=775'] end From c017338f1f7a5e99298ea49b3de89aef7c53812f Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Fri, 25 Mar 2016 14:53:36 -0400 Subject: [PATCH 12/52] wordpress_sites improvements * Add to_env filter * Use combine + to_env for better .env templates * Remove the need for wp_env and set automatically based on env * Set disable_wp_cron by default --- CHANGELOG.md | 1 + filter_plugins/trellis_filters.py | 4 ++++ group_vars/all/main.yml | 4 ++++ group_vars/development/main.yml | 1 + group_vars/development/wordpress_sites.yml | 3 --- group_vars/production/wordpress_sites.yml | 2 -- group_vars/staging/wordpress_sites.yml | 2 -- roles/deploy/defaults/main.yml | 2 +- roles/deploy/templates/env.j2 | 7 +------ roles/wordpress-install/tasks/main.yml | 8 ++++---- roles/wordpress-install/templates/env.j2 | 7 +------ roles/wordpress-setup/tasks/main.yml | 2 +- roles/wordpress-setup/templates/wordpress-site.conf.j2 | 2 +- 13 files changed, 19 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28ce8e6e7f..5dfd3345a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* Simplify and improve `wordpress_sites` with better defaults ([#528](https://github.com/roots/trellis/pull/528)) * Improve Let's Encrypt challenge pre-flight tests ([#526](https://github.com/roots/trellis/pull/526)) * `reverse_www` filter improvements (ignore subdomains) ([#525](https://github.com/roots/trellis/pull/525)) * Fix #520 - Disable MariaDB binary logging by default ([#521](https://github.com/roots/trellis/pull/521)) diff --git a/filter_plugins/trellis_filters.py b/filter_plugins/trellis_filters.py index 8eca6de8d1..ca68a4d55f 100644 --- a/filter_plugins/trellis_filters.py +++ b/filter_plugins/trellis_filters.py @@ -38,6 +38,9 @@ def reverse_www(hosts, enabled=True, append=True): else: raise errors.AnsibleFilterError('The reverse_www filter expects a string or list of strings, got ' + repr(hosts)) +def to_env(dict_value): + envs = ["{0}='{1}'".format(key.upper(), value) for key, value in dict_value.iteritems()] + return "\n".join(envs) class FilterModule(object): ''' Trellis jinja2 filters ''' @@ -45,4 +48,5 @@ class FilterModule(object): def filters(self): return { 'reverse_www': reverse_www, + 'to_env': to_env, } diff --git a/group_vars/all/main.yml b/group_vars/all/main.yml index 53cf117a83..132e2664e8 100644 --- a/group_vars/all/main.yml +++ b/group_vars/all/main.yml @@ -6,3 +6,7 @@ default_timezone: Etc/UTC www_root: /srv/www ip_whitelist: - "{{ lookup('pipe', 'curl -4 -s https://api.ipify.org') }}" + +wordpress_env_defaults: + wp_env: "{{ env }}" + disable_wp_cron: true diff --git a/group_vars/development/main.yml b/group_vars/development/main.yml index 672d3de409..d8ba31e48d 100644 --- a/group_vars/development/main.yml +++ b/group_vars/development/main.yml @@ -1,3 +1,4 @@ +env: development ferm_enabled: false mysql_root_password: "{{ vault_mysql_root_password }}" # Define this variable in group_vars/development/vault.yml sudoer_passwords: "{{ vault_sudoer_passwords }}" # Define this variable in group_vars/development/vault.yml diff --git a/group_vars/development/wordpress_sites.yml b/group_vars/development/wordpress_sites.yml index 6efe3b1094..7f3822a9d1 100644 --- a/group_vars/development/wordpress_sites.yml +++ b/group_vars/development/wordpress_sites.yml @@ -4,7 +4,6 @@ wordpress_sites: site_hosts: - example.dev local_path: ../site # path targeting local Bedrock site directory (relative to Ansible root) - site_install: true site_title: Example Site admin_user: admin # admin_password: (defined in group_vars/development/vault.yml) @@ -20,10 +19,8 @@ wordpress_sites: enabled: false duration: 30s env: - disable_wp_cron: true wp_home: http://example.dev wp_siteurl: http://example.dev/wp - wp_env: development db_name: example_dev db_user: example_dbuser # db_password: (defined in group_vars/development/vault.yml) diff --git a/group_vars/production/wordpress_sites.yml b/group_vars/production/wordpress_sites.yml index 67373a20fc..3401cb96d4 100644 --- a/group_vars/production/wordpress_sites.yml +++ b/group_vars/production/wordpress_sites.yml @@ -17,10 +17,8 @@ wordpress_sites: enabled: false duration: 30s env: - disable_wp_cron: true wp_home: http://example.com wp_siteurl: http://example.com/wp - wp_env: production db_name: example_prod db_user: example_dbuser # Define the following variables in group_vars/production/vault.yml diff --git a/group_vars/staging/wordpress_sites.yml b/group_vars/staging/wordpress_sites.yml index e1711f587b..807f77e150 100644 --- a/group_vars/staging/wordpress_sites.yml +++ b/group_vars/staging/wordpress_sites.yml @@ -17,10 +17,8 @@ wordpress_sites: enabled: false duration: 30s env: - disable_wp_cron: true wp_home: http://staging.example.com wp_siteurl: http://staging.example.com/wp - wp_env: staging db_name: example_staging db_user: example_dbuser # Define the following variables in group_vars/staging/vault.yml diff --git a/roles/deploy/defaults/main.yml b/roles/deploy/defaults/main.yml index eb93c6fc4e..3974dedaf4 100644 --- a/roles/deploy/defaults/main.yml +++ b/roles/deploy/defaults/main.yml @@ -39,4 +39,4 @@ project_shared_children: # project_environment: # WP_ENV: "production" project_environment: - WP_ENV: "{{ project.env.wp_env }}" + WP_ENV: "{{ env }}" diff --git a/roles/deploy/templates/env.j2 b/roles/deploy/templates/env.j2 index 6b58b634ad..10df4ca54f 100644 --- a/roles/deploy/templates/env.j2 +++ b/roles/deploy/templates/env.j2 @@ -1,6 +1 @@ -{% for key, value in project.env.iteritems() %} -{{ key | upper }}="{{ value }}" -{% endfor %} -{% for key, value in vault_wordpress_sites[site].env.iteritems() %} -{{ key | upper }}="{{ value }}" -{% endfor %} +{{ wordpress_env_defaults | combine(item.value.env, vault_wordpress_sites[item.key].env) | to_env }} diff --git a/roles/wordpress-install/tasks/main.yml b/roles/wordpress-install/tasks/main.yml index 5b45db6714..41bf9a2c22 100644 --- a/roles/wordpress-install/tasks/main.yml +++ b/roles/wordpress-install/tasks/main.yml @@ -13,8 +13,8 @@ - name: Copy .env file into web root command: rsync -ac --info=NAME /tmp/{{ item.key }}.env {{ www_root }}/{{ item.key }}/current/.env with_dict: "{{ wordpress_sites }}" - register: env - changed_when: env.stdout == "{{ item.key }}.env" + register: env_file + changed_when: env_file.stdout == "{{ item.key }}.env" - name: Install Dependencies with Composer command: composer install @@ -36,7 +36,7 @@ chdir: "{{ www_root }}/{{ item.key }}/current/" register: wp_install_results with_dict: "{{ wordpress_sites }}" - when: item.value.site_install == True and (item.value.multisite.enabled | default(False) == False) + when: item.value.site_install | default(true) and not item.value.multisite.enabled | default(false) changed_when: "'WordPress is already installed.' not in wp_install_results.stdout" - name: Setup Permalink Structure @@ -61,5 +61,5 @@ chdir: "{{ www_root }}/{{ item.key }}/current/" register: wp_install_results with_dict: "{{ wordpress_sites }}" - when: item.value.site_install == True and (item.value.multisite.enabled | default(False) == True) + when: item.value.site_install | default(true) and item.value.multisite.enabled | default(false) changed_when: "'The network already exists.' not in wp_install_results.stdout" diff --git a/roles/wordpress-install/templates/env.j2 b/roles/wordpress-install/templates/env.j2 index 1839576a3f..10df4ca54f 100644 --- a/roles/wordpress-install/templates/env.j2 +++ b/roles/wordpress-install/templates/env.j2 @@ -1,6 +1 @@ -{% for key, value in item.value.env.iteritems() %} -{{ key | upper }}="{{ value }}" -{% endfor %} -{% for key, value in vault_wordpress_sites[item.key].env.iteritems() %} -{{ key | upper }}="{{ value }}" -{% endfor %} +{{ wordpress_env_defaults | combine(item.value.env, vault_wordpress_sites[item.key].env) | to_env }} diff --git a/roles/wordpress-setup/tasks/main.yml b/roles/wordpress-setup/tasks/main.yml index 150abd9cc3..137c7238de 100644 --- a/roles/wordpress-setup/tasks/main.yml +++ b/roles/wordpress-setup/tasks/main.yml @@ -33,4 +33,4 @@ job: "curl -k -s {{ item.value.env.wp_siteurl }}/wp-cron.php > /dev/null 2>&1" cron_file: "wordpress-{{ item.key | replace('.', '_') }}" with_dict: "{{ wordpress_sites }}" - when: item.value.env.disable_wp_cron | default(false) and not item.value.multisite.enabled | default(false) + when: item.value.env.disable_wp_cron | default(true) and not item.value.multisite.enabled | default(false) diff --git a/roles/wordpress-setup/templates/wordpress-site.conf.j2 b/roles/wordpress-setup/templates/wordpress-site.conf.j2 index ec016c6702..111d528b9e 100644 --- a/roles/wordpress-setup/templates/wordpress-site.conf.j2 +++ b/roles/wordpress-setup/templates/wordpress-site.conf.j2 @@ -16,7 +16,7 @@ server { charset utf-8; - {% if item.value.env.wp_env == 'development' -%} + {% if env == 'development' -%} # See Virtualbox section at http://wiki.nginx.org/Pitfalls sendfile off; {%- endif %} From 6e41078987635992d5557fdcd6be4cc102f43dff Mon Sep 17 00:00:00 2001 From: Matthew Henkler Date: Sat, 26 Mar 2016 13:53:58 -0500 Subject: [PATCH 13/52] Fix deploy env template to use valid ansible vars --- roles/deploy/templates/env.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/deploy/templates/env.j2 b/roles/deploy/templates/env.j2 index 10df4ca54f..b2e557c1f8 100644 --- a/roles/deploy/templates/env.j2 +++ b/roles/deploy/templates/env.j2 @@ -1 +1 @@ -{{ wordpress_env_defaults | combine(item.value.env, vault_wordpress_sites[item.key].env) | to_env }} +{{ wordpress_env_defaults | combine(project.env, vault_wordpress_sites[site].env) | to_env }} From baffeb7684f74ef2aa39ac0c2e2d1bb2d71cc7fb Mon Sep 17 00:00:00 2001 From: Phil Nelson Date: Sat, 26 Mar 2016 14:36:55 -0700 Subject: [PATCH 14/52] Update CHANGELOG with #523, #527, #530 --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dfd3345a5..784dcf25d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,13 @@ ### HEAD +* Fix deploy env template to use valid ansible vars ([#530](https://github.com/roots/trellis/pull/530)) * Simplify and improve `wordpress_sites` with better defaults ([#528](https://github.com/roots/trellis/pull/528)) +* Allow option for WinNFSD sync folder provider on Windows ([#527](https://github.com/roots/trellis/pull/527)) * Improve Let's Encrypt challenge pre-flight tests ([#526](https://github.com/roots/trellis/pull/526)) * `reverse_www` filter improvements (ignore subdomains) ([#525](https://github.com/roots/trellis/pull/525)) +* Fix deprecation warnings on deploy, use current stable WP-CLI ([#523](https://github.com/roots/trellis/pull/523)) * Fix #520 - Disable MariaDB binary logging by default ([#521](https://github.com/roots/trellis/pull/521)) * Let's Encrypt integration ([#518](https://github.com/roots/trellis/pull/518)) -* Improve Git repo format validation [#516](https://github.com/roots/trellis/pull/516) +* Improve Git repo format validation ([#516](https://github.com/roots/trellis/pull/516)) * Fix #505 - Git ignore \*.retry file * Fix Ansible deprecations for bare variables ([#510](https://github.com/roots/trellis/pull/510)) * Fixes #508 - update php-xdebug config file path ([#509](https://github.com/roots/trellis/pull/509)) From 6fbe67f883b12fb3d4def0b4773a2015d29b8e75 Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Fri, 25 Mar 2016 22:17:20 -0400 Subject: [PATCH 15/52] Better defaults for db_name and db_user Automatically generate proper defaults for `db_name` and `db_user` so they don't need to be manually defined under most circumstances. --- CHANGELOG.md | 1 + filter_plugins/trellis_filters.py | 7 ++++++- group_vars/all/main.yml | 4 ---- group_vars/development/wordpress_sites.yml | 2 -- group_vars/production/wordpress_sites.yml | 2 -- group_vars/staging/wordpress_sites.yml | 2 -- roles/deploy/defaults/main.yml | 6 ++++++ roles/letsencrypt/tasks/nginx.yml | 2 +- roles/wordpress-install/defaults/main.yml | 5 +++++ roles/wordpress-setup/defaults/main.yml | 2 ++ roles/wordpress-setup/tasks/database.yml | 10 +++++----- 11 files changed, 26 insertions(+), 17 deletions(-) create mode 100644 roles/wordpress-install/defaults/main.yml create mode 100644 roles/wordpress-setup/defaults/main.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 784dcf25d7..3939db7e9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* Supply better defaults for `db_name` and `db_user` ([#529](https://github.com/roots/trellis/pull/529)) * Fix deploy env template to use valid ansible vars ([#530](https://github.com/roots/trellis/pull/530)) * Simplify and improve `wordpress_sites` with better defaults ([#528](https://github.com/roots/trellis/pull/528)) * Allow option for WinNFSD sync folder provider on Windows ([#527](https://github.com/roots/trellis/pull/527)) diff --git a/filter_plugins/trellis_filters.py b/filter_plugins/trellis_filters.py index ca68a4d55f..5d9e675367 100644 --- a/filter_plugins/trellis_filters.py +++ b/filter_plugins/trellis_filters.py @@ -39,9 +39,13 @@ def reverse_www(hosts, enabled=True, append=True): raise errors.AnsibleFilterError('The reverse_www filter expects a string or list of strings, got ' + repr(hosts)) def to_env(dict_value): - envs = ["{0}='{1}'".format(key.upper(), value) for key, value in dict_value.iteritems()] + envs = ["{0}='{1}'".format(key.upper(), value) for key, value in sorted(dict_value.items())] return "\n".join(envs) +def underscore(value): + ''' Convert dots to underscore in a string ''' + return value.replace('.', '_') + class FilterModule(object): ''' Trellis jinja2 filters ''' @@ -49,4 +53,5 @@ def filters(self): return { 'reverse_www': reverse_www, 'to_env': to_env, + 'underscore': underscore, } diff --git a/group_vars/all/main.yml b/group_vars/all/main.yml index 132e2664e8..53cf117a83 100644 --- a/group_vars/all/main.yml +++ b/group_vars/all/main.yml @@ -6,7 +6,3 @@ default_timezone: Etc/UTC www_root: /srv/www ip_whitelist: - "{{ lookup('pipe', 'curl -4 -s https://api.ipify.org') }}" - -wordpress_env_defaults: - wp_env: "{{ env }}" - disable_wp_cron: true diff --git a/group_vars/development/wordpress_sites.yml b/group_vars/development/wordpress_sites.yml index 7f3822a9d1..e5cec18a46 100644 --- a/group_vars/development/wordpress_sites.yml +++ b/group_vars/development/wordpress_sites.yml @@ -21,6 +21,4 @@ wordpress_sites: env: wp_home: http://example.dev wp_siteurl: http://example.dev/wp - db_name: example_dev - db_user: example_dbuser # db_password: (defined in group_vars/development/vault.yml) diff --git a/group_vars/production/wordpress_sites.yml b/group_vars/production/wordpress_sites.yml index 3401cb96d4..ce74f58684 100644 --- a/group_vars/production/wordpress_sites.yml +++ b/group_vars/production/wordpress_sites.yml @@ -19,8 +19,6 @@ wordpress_sites: env: wp_home: http://example.com wp_siteurl: http://example.com/wp - db_name: example_prod - db_user: example_dbuser # Define the following variables in group_vars/production/vault.yml # db_password: # auth_key: diff --git a/group_vars/staging/wordpress_sites.yml b/group_vars/staging/wordpress_sites.yml index 807f77e150..e506b5dbb0 100644 --- a/group_vars/staging/wordpress_sites.yml +++ b/group_vars/staging/wordpress_sites.yml @@ -19,8 +19,6 @@ wordpress_sites: env: wp_home: http://staging.example.com wp_siteurl: http://staging.example.com/wp - db_name: example_staging - db_user: example_dbuser # Define the following variables in group_vars/staging/vault.yml # db_password: # auth_key: diff --git a/roles/deploy/defaults/main.yml b/roles/deploy/defaults/main.yml index 3974dedaf4..4568266462 100644 --- a/roles/deploy/defaults/main.yml +++ b/roles/deploy/defaults/main.yml @@ -40,3 +40,9 @@ project_shared_children: # WP_ENV: "production" project_environment: WP_ENV: "{{ env }}" + +wordpress_env_defaults: + db_name: "{{ site | underscore }}_{{ env }}" + db_user: "{{ site | underscore }}" + disable_wp_cron: true + wp_env: "{{ env }}" diff --git a/roles/letsencrypt/tasks/nginx.yml b/roles/letsencrypt/tasks/nginx.yml index f1c87642ea..69f8bbaa5f 100644 --- a/roles/letsencrypt/tasks/nginx.yml +++ b/roles/letsencrypt/tasks/nginx.yml @@ -37,7 +37,7 @@ hosts: "{{ item.value.site_hosts | reverse_www(enabled=item.value.www_redirect | default(true)) }}" register: letsencrypt_test_challenges ignore_errors: true - when: item.value.ssl is defined and item.value.ssl.enabled | default(false) and item.value.ssl.provider | default('manual') == 'letsencrypt' + when: site_uses_letsencrypt with_dict: "{{ wordpress_sites }}" - name: Notify of challenge failures diff --git a/roles/wordpress-install/defaults/main.yml b/roles/wordpress-install/defaults/main.yml new file mode 100644 index 0000000000..47a96401bf --- /dev/null +++ b/roles/wordpress-install/defaults/main.yml @@ -0,0 +1,5 @@ +wordpress_env_defaults: + db_name: "{{ item.key | underscore }}_{{ env }}" + db_user: "{{ item.key | underscore }}" + disable_wp_cron: true + wp_env: "{{ env }}" diff --git a/roles/wordpress-setup/defaults/main.yml b/roles/wordpress-setup/defaults/main.yml new file mode 100644 index 0000000000..6e27a07818 --- /dev/null +++ b/roles/wordpress-setup/defaults/main.yml @@ -0,0 +1,2 @@ +db_name: "{{ item.value.env.db_name | default(item.key + '_' + env) | underscore }}" +db_user: "{{ item.value.env.db_user | default(item.key) | underscore }}" diff --git a/roles/wordpress-setup/tasks/database.yml b/roles/wordpress-setup/tasks/database.yml index fe4eef658d..42a905e97a 100644 --- a/roles/wordpress-setup/tasks/database.yml +++ b/roles/wordpress-setup/tasks/database.yml @@ -1,7 +1,7 @@ --- - name: Create database of sites mysql_db: - name: "{{ item.value.env.db_name | default(item.key) }}" + name: "{{ db_name }}" state: present login_host: "{{ item.value.env.db_host | default('localhost') }}" login_user: "{{ mysql_root_user }}" @@ -11,10 +11,10 @@ - name: Create/assign database user to db and grant permissions mysql_user: - name: "{{ item.value.env.db_user }}" + name: "{{ db_user }}" password: "{{ vault_wordpress_sites[item.key].env.db_password }}" append_privs: yes - priv: "{{ item.value.env.db_name | default(item.key) }}.*:ALL" + priv: "{{ db_name }}.*:ALL" state: present login_host: "{{ item.value.env.db_host | default('localhost') }}" login_user: "{{ mysql_root_user }}" @@ -31,11 +31,11 @@ - name: Import database mysql_db: - name: "{{ item.value.env.db_name | default(item.key) }}" + name: "{{ db_name }}" state: import target: "/tmp/{{ item.value.db_import | basename }}" login_host: "{{ item.value.env.db_host | default('localhost') }}" - login_user: "{{ item.value.env.db_user }}" + login_user: "{{ db_user }}" login_password: "{{ vault_wordpress_sites[item.key].env.db_password }}" with_dict: "{{ wordpress_sites }}" when: item.value.db_import | default(False) From ace4bcd0fae4fef4ebdb9579b7cb5d55cabd0711 Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Mon, 28 Mar 2016 13:43:04 -0400 Subject: [PATCH 16/52] Let's Encrypt: use new X3 intermediate certificate LE recently "upgraded" to their new 2nd-gen X3 authority which has better Windows XP support. This also means a new X3 intermediate certificate. This was causing certificate chain issues since the authority was X3 and the intermediate cert was X1. Now they match and everything is good in the world again. --- CHANGELOG.md | 1 + roles/letsencrypt/defaults/main.yml | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3939db7e9b..6e21956529 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* Switch to Let's Encrypt X3 intermediate certificate and fix chain issues ([#534](https://github.com/roots/trellis/pull/534)) * Supply better defaults for `db_name` and `db_user` ([#529](https://github.com/roots/trellis/pull/529)) * Fix deploy env template to use valid ansible vars ([#530](https://github.com/roots/trellis/pull/530)) * Simplify and improve `wordpress_sites` with better defaults ([#528](https://github.com/roots/trellis/pull/528)) diff --git a/roles/letsencrypt/defaults/main.yml b/roles/letsencrypt/defaults/main.yml index 8bbb5c38a1..57e54fc41e 100644 --- a/roles/letsencrypt/defaults/main.yml +++ b/roles/letsencrypt/defaults/main.yml @@ -27,9 +27,9 @@ letsencrypt_ca: 'https://acme-v01.api.letsencrypt.org' letsencrypt_account_key: '{{ acme_tiny_data_directory }}/account.key' -letsencrypt_intermediate_cert_path: /etc/ssl/certs/lets-encrypt-x1-cross-signed.pem -letsencrypt_intermediate_cert_url: 'https://letsencrypt.org/certs/lets-encrypt-x1-cross-signed.pem' -letsencrypt_intermediate_cert_sha256sum: '6c0a324bb803e9d66b8986ea2085bb9d6bdfe33f5c04a03a3f7024f4aa8e7a2d' +letsencrypt_intermediate_cert_path: /etc/ssl/certs/lets-encrypt-x3-cross-signed.pem +letsencrypt_intermediate_cert_url: 'https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem' +letsencrypt_intermediate_cert_sha256sum: 'e446c5e9dbef9d09ac9f7027c034602492437a05ff6c40011d7235fca639c79a' letsencrypt_keys_dir: "{{ nginx_ssl_path }}/letsencrypt" letsencrypt_certs_dir: "{{ nginx_ssl_path }}/letsencrypt" From 85eebc0e5d706ac3f01da33f4971ffcbcbd45aae Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Mon, 28 Mar 2016 13:23:41 -0400 Subject: [PATCH 17/52] Automatically set `wp_home` and `wp_siteurl` Using the "dynamic" HTTP_HOST/SERVER_NAME is a good enough default. In the majority of cases users should not have to worry about setting these variables and overriding them. This also allows for a more seamless transition to HTTPS since there's one less place to configure or potentially forget. It also just reduces the chances of misconfiguration in general and a mismatch of values (site hosts and wp home/siteurl). --- CHANGELOG.md | 1 + deploy.yml | 9 +++++++++ group_vars/all/main.yml | 11 +++++++++++ group_vars/development/vault.yml | 1 + group_vars/development/wordpress_sites.yml | 13 ++----------- group_vars/production/vault.yml | 1 + group_vars/production/wordpress_sites.yml | 19 +++---------------- group_vars/staging/vault.yml | 1 + group_vars/staging/wordpress_sites.yml | 20 +++----------------- roles/deploy/defaults/main.yml | 6 ------ roles/deploy/templates/env.j2 | 2 +- roles/wordpress-install/defaults/main.yml | 5 ----- roles/wordpress-install/tasks/main.yml | 4 ++-- roles/wordpress-install/templates/env.j2 | 2 +- roles/wordpress-setup/defaults/main.yml | 2 -- roles/wordpress-setup/tasks/database.yml | 20 ++++++++++---------- roles/wordpress-setup/tasks/main.yml | 4 ++-- 17 files changed, 48 insertions(+), 73 deletions(-) delete mode 100644 roles/wordpress-install/defaults/main.yml delete mode 100644 roles/wordpress-setup/defaults/main.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e21956529..c7fb1f0c33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* Automatically set `wp_home` and `wp_siteurl` variables ([#533](https://github.com/roots/trellis/pull/533)) * Switch to Let's Encrypt X3 intermediate certificate and fix chain issues ([#534](https://github.com/roots/trellis/pull/534)) * Supply better defaults for `db_name` and `db_user` ([#529](https://github.com/roots/trellis/pull/529)) * Fix deploy env template to use valid ansible vars ([#530](https://github.com/roots/trellis/pull/530)) diff --git a/deploy.yml b/deploy.yml index e110aca79f..70e24a4805 100644 --- a/deploy.yml +++ b/deploy.yml @@ -13,6 +13,15 @@ deploy_finalize_after: "{{ playbook_dir }}/roles/deploy/hooks/finalize-after.yml" project: "{{ wordpress_sites[site] }}" project_root: "{{ www_root }}/{{ site }}" + wordpress_env_defaults: + db_host: localhost + db_name: "{{ site | underscore }}_{{ env }}" + db_user: "{{ site | underscore }}" + disable_wp_cron: true + wp_env: "{{ env }}" + wp_home: "{{ project.ssl.enabled | default(false) | ternary('https', 'http') }}://${HTTP_HOST}" + wp_siteurl: "${WP_HOME}/wp" + site_env: "{{ wordpress_env_defaults | combine(project.env | default({}), vault_wordpress_sites[site].env) }}" pre_tasks: - name: Ensure site is valid diff --git a/group_vars/all/main.yml b/group_vars/all/main.yml index 53cf117a83..b3b08266fa 100644 --- a/group_vars/all/main.yml +++ b/group_vars/all/main.yml @@ -6,3 +6,14 @@ default_timezone: Etc/UTC www_root: /srv/www ip_whitelist: - "{{ lookup('pipe', 'curl -4 -s https://api.ipify.org') }}" + +wordpress_env_defaults: + db_host: localhost + db_name: "{{ item.key | underscore }}_{{ env }}" + db_user: "{{ item.key | underscore }}" + disable_wp_cron: true + wp_env: "{{ env }}" + wp_home: "{{ item.value.ssl.enabled | default(false) | ternary('https', 'http') }}://${HTTP_HOST}" + wp_siteurl: "${WP_HOME}/wp" + +site_env: "{{ wordpress_env_defaults | combine(item.value.env | default({}), vault_wordpress_sites[item.key].env) }}" diff --git a/group_vars/development/vault.yml b/group_vars/development/vault.yml index d1d2f5ea34..a2bead061f 100644 --- a/group_vars/development/vault.yml +++ b/group_vars/development/vault.yml @@ -6,6 +6,7 @@ vault_sudoer_passwords: admin: $6$rounds=100000$JUkj1d3hCa6uFp6R$3rZ8jImyCpTP40e4I5APx7SbBvDCM8fB6GP/IGOrsk/GEUTUhl1i/Q2JNOpj9ashLpkgaCxqMqbFKdZdmAh26/ # Variables to accompany `group_vars/development/wordpress_sites.yml` +# Note: the site name (`example.com`) must match up with the site name in the above file. vault_wordpress_sites: example.com: admin_password: admin diff --git a/group_vars/development/wordpress_sites.yml b/group_vars/development/wordpress_sites.yml index e5cec18a46..639ac36236 100644 --- a/group_vars/development/wordpress_sites.yml +++ b/group_vars/development/wordpress_sites.yml @@ -1,24 +1,15 @@ # Documentation: https://roots.io/trellis/docs/local-development-setup/ +# Define accompanying passwords/secrets in group_vars/development/vault.yml + wordpress_sites: example.com: site_hosts: - example.dev local_path: ../site # path targeting local Bedrock site directory (relative to Ansible root) - site_title: Example Site - admin_user: admin - # admin_password: (defined in group_vars/development/vault.yml) admin_email: admin@example.dev - initial_permalink_structure: /%postname%/ # applied only at time of WP install and when `site_install: true` multisite: enabled: false - subdomains: false ssl: enabled: false - provider: self-signed cache: enabled: false - duration: 30s - env: - wp_home: http://example.dev - wp_siteurl: http://example.dev/wp - # db_password: (defined in group_vars/development/vault.yml) diff --git a/group_vars/production/vault.yml b/group_vars/production/vault.yml index d797dc2aa2..fda32ba22e 100644 --- a/group_vars/production/vault.yml +++ b/group_vars/production/vault.yml @@ -6,6 +6,7 @@ vault_sudoer_passwords: admin: $6$rounds=100000$JUkj1d3hCa6uFp6R$3rZ8jImyCpTP40e4I5APx7SbBvDCM8fB6GP/IGOrsk/GEUTUhl1i/Q2JNOpj9ashLpkgaCxqMqbFKdZdmAh26/ # Variables to accompany `group_vars/production/wordpress_sites.yml` +# Note: the site name (`example.com`) must match up with the site name in the above file. vault_wordpress_sites: example.com: env: diff --git a/group_vars/production/wordpress_sites.yml b/group_vars/production/wordpress_sites.yml index ce74f58684..884d280994 100644 --- a/group_vars/production/wordpress_sites.yml +++ b/group_vars/production/wordpress_sites.yml @@ -1,31 +1,18 @@ # Documentation: https://roots.io/trellis/docs/remote-server-setup/ +# Define accompanying passwords/secrets in group_vars/production/vault.yml + wordpress_sites: example.com: site_hosts: - example.com local_path: ../site # path targeting local Bedrock site directory (relative to Ansible root) repo: git@github.com:example/example.com.git # replace with your Git repo URL - branch: master repo_subtree_path: site # relative path to your Bedrock/WP directory in your repo + branch: master multisite: enabled: false - subdomains: false ssl: enabled: false provider: letsencrypt cache: enabled: false - duration: 30s - env: - wp_home: http://example.com - wp_siteurl: http://example.com/wp - # Define the following variables in group_vars/production/vault.yml - # db_password: - # auth_key: - # secure_auth_key: - # logged_in_key: - # nonce_key: - # auth_salt: - # secure_auth_salt: - # logged_in_salt: - # nonce_salt: diff --git a/group_vars/staging/vault.yml b/group_vars/staging/vault.yml index 0c47d669d4..4948cbd0a2 100644 --- a/group_vars/staging/vault.yml +++ b/group_vars/staging/vault.yml @@ -6,6 +6,7 @@ vault_sudoer_passwords: admin: $6$rounds=100000$JUkj1d3hCa6uFp6R$3rZ8jImyCpTP40e4I5APx7SbBvDCM8fB6GP/IGOrsk/GEUTUhl1i/Q2JNOpj9ashLpkgaCxqMqbFKdZdmAh26/ # Variables to accompany `group_vars/staging/wordpress_sites.yml` +# Note: the site name (`example.com`) must match up with the site name in the above file. vault_wordpress_sites: example.com: env: diff --git a/group_vars/staging/wordpress_sites.yml b/group_vars/staging/wordpress_sites.yml index e506b5dbb0..23d40c30c9 100644 --- a/group_vars/staging/wordpress_sites.yml +++ b/group_vars/staging/wordpress_sites.yml @@ -1,31 +1,17 @@ # Documentation: https://roots.io/trellis/docs/remote-server-setup/ +# Define accompanying passwords/secrets in group_vars/staging/vault.yml + wordpress_sites: example.com: site_hosts: - staging.example.com local_path: ../site # path targeting local Bedrock site directory (relative to Ansible root) repo: git@github.com:example/example.com.git # replace with your Git repo URL - branch: master repo_subtree_path: site # relative path to your Bedrock/WP directory in your repo + branch: master multisite: enabled: false - subdomains: false ssl: enabled: false - provider: letsencrypt cache: enabled: false - duration: 30s - env: - wp_home: http://staging.example.com - wp_siteurl: http://staging.example.com/wp - # Define the following variables in group_vars/staging/vault.yml - # db_password: - # auth_key: - # secure_auth_key: - # logged_in_key: - # nonce_key: - # auth_salt: - # secure_auth_salt: - # logged_in_salt: - # nonce_salt: diff --git a/roles/deploy/defaults/main.yml b/roles/deploy/defaults/main.yml index 4568266462..3974dedaf4 100644 --- a/roles/deploy/defaults/main.yml +++ b/roles/deploy/defaults/main.yml @@ -40,9 +40,3 @@ project_shared_children: # WP_ENV: "production" project_environment: WP_ENV: "{{ env }}" - -wordpress_env_defaults: - db_name: "{{ site | underscore }}_{{ env }}" - db_user: "{{ site | underscore }}" - disable_wp_cron: true - wp_env: "{{ env }}" diff --git a/roles/deploy/templates/env.j2 b/roles/deploy/templates/env.j2 index b2e557c1f8..6bd95bfdba 100644 --- a/roles/deploy/templates/env.j2 +++ b/roles/deploy/templates/env.j2 @@ -1 +1 @@ -{{ wordpress_env_defaults | combine(project.env, vault_wordpress_sites[site].env) | to_env }} +{{ site_env | to_env }} diff --git a/roles/wordpress-install/defaults/main.yml b/roles/wordpress-install/defaults/main.yml deleted file mode 100644 index 47a96401bf..0000000000 --- a/roles/wordpress-install/defaults/main.yml +++ /dev/null @@ -1,5 +0,0 @@ -wordpress_env_defaults: - db_name: "{{ item.key | underscore }}_{{ env }}" - db_user: "{{ item.key | underscore }}" - disable_wp_cron: true - wp_env: "{{ env }}" diff --git a/roles/wordpress-install/tasks/main.yml b/roles/wordpress-install/tasks/main.yml index 41bf9a2c22..cd89e48bdd 100644 --- a/roles/wordpress-install/tasks/main.yml +++ b/roles/wordpress-install/tasks/main.yml @@ -27,7 +27,7 @@ - name: Install WP command: wp core install --allow-root - --url="{{ item.value.env.wp_home }}" + --url="{{ site_env.wp_home }}" --title="{{ item.value.site_title | default(item.key) }}" --admin_user="{{ item.value.admin_user }}" --admin_password="{{ vault_wordpress_sites[item.key].admin_password }}" @@ -50,7 +50,7 @@ - name: Install WP Multisite command: wp core multisite-install --allow-root - --url="{{ item.value.env.wp_home }}" + --url="{{ site_env.wp_home }}" --base="{{ item.value.multisite.base_path | default('/') }}" --subdomains="{{ item.value.multisite.subdomains | default('false') }}" --title="{{ item.value.site_title | default(item.key) }}" diff --git a/roles/wordpress-install/templates/env.j2 b/roles/wordpress-install/templates/env.j2 index 10df4ca54f..6bd95bfdba 100644 --- a/roles/wordpress-install/templates/env.j2 +++ b/roles/wordpress-install/templates/env.j2 @@ -1 +1 @@ -{{ wordpress_env_defaults | combine(item.value.env, vault_wordpress_sites[item.key].env) | to_env }} +{{ site_env | to_env }} diff --git a/roles/wordpress-setup/defaults/main.yml b/roles/wordpress-setup/defaults/main.yml deleted file mode 100644 index 6e27a07818..0000000000 --- a/roles/wordpress-setup/defaults/main.yml +++ /dev/null @@ -1,2 +0,0 @@ -db_name: "{{ item.value.env.db_name | default(item.key + '_' + env) | underscore }}" -db_user: "{{ item.value.env.db_user | default(item.key) | underscore }}" diff --git a/roles/wordpress-setup/tasks/database.yml b/roles/wordpress-setup/tasks/database.yml index 42a905e97a..8f8238e88b 100644 --- a/roles/wordpress-setup/tasks/database.yml +++ b/roles/wordpress-setup/tasks/database.yml @@ -1,9 +1,9 @@ --- - name: Create database of sites mysql_db: - name: "{{ db_name }}" + name: "{{ site_env.db_name }}" state: present - login_host: "{{ item.value.env.db_host | default('localhost') }}" + login_host: "{{ site_env.db_host }}" login_user: "{{ mysql_root_user }}" login_password: "{{ mysql_root_password }}" with_dict: "{{ wordpress_sites }}" @@ -11,12 +11,12 @@ - name: Create/assign database user to db and grant permissions mysql_user: - name: "{{ db_user }}" - password: "{{ vault_wordpress_sites[item.key].env.db_password }}" + name: "{{ site_env.db_user }}" + password: "{{ site_env.db_password }}" append_privs: yes - priv: "{{ db_name }}.*:ALL" + priv: "{{ site_env.db_name }}.*:ALL" state: present - login_host: "{{ item.value.env.db_host | default('localhost') }}" + login_host: "{{ site_env.db_host }}" login_user: "{{ mysql_root_user }}" login_password: "{{ mysql_root_password }}" with_dict: "{{ wordpress_sites }}" @@ -31,12 +31,12 @@ - name: Import database mysql_db: - name: "{{ db_name }}" + name: "{{ site_env.db_name }}" state: import target: "/tmp/{{ item.value.db_import | basename }}" - login_host: "{{ item.value.env.db_host | default('localhost') }}" - login_user: "{{ db_user }}" - login_password: "{{ vault_wordpress_sites[item.key].env.db_password }}" + login_host: "{{ site_env.db_host }}" + login_user: "{{ site_env.db_user }}" + login_password: "{{ site_env.db_password }}" with_dict: "{{ wordpress_sites }}" when: item.value.db_import | default(False) notify: reload nginx diff --git a/roles/wordpress-setup/tasks/main.yml b/roles/wordpress-setup/tasks/main.yml index 137c7238de..2d4fcdff61 100644 --- a/roles/wordpress-setup/tasks/main.yml +++ b/roles/wordpress-setup/tasks/main.yml @@ -30,7 +30,7 @@ name: "{{ item.key }} WordPress cron" minute: "*/15" user: "{{ web_user }}" - job: "curl -k -s {{ item.value.env.wp_siteurl }}/wp-cron.php > /dev/null 2>&1" + job: "curl -k -s {{ site_env.wp_siteurl }}/wp-cron.php > /dev/null 2>&1" cron_file: "wordpress-{{ item.key | replace('.', '_') }}" with_dict: "{{ wordpress_sites }}" - when: item.value.env.disable_wp_cron | default(true) and not item.value.multisite.enabled | default(false) + when: site_env.disable_wp_cron and not item.value.multisite.enabled | default(false) From 2e8d985a299e15243cc00fb2115631d968ab1ccf Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Mon, 28 Mar 2016 22:49:29 -0400 Subject: [PATCH 18/52] Update some documentation notes --- group_vars/development/wordpress_sites.yml | 2 ++ group_vars/production/wordpress_sites.yml | 1 + group_vars/staging/wordpress_sites.yml | 2 ++ hosts/production | 4 ++-- hosts/staging | 4 ++-- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/group_vars/development/wordpress_sites.yml b/group_vars/development/wordpress_sites.yml index 639ac36236..148cfdde91 100644 --- a/group_vars/development/wordpress_sites.yml +++ b/group_vars/development/wordpress_sites.yml @@ -1,4 +1,5 @@ # Documentation: https://roots.io/trellis/docs/local-development-setup/ +# `wordpress_sites` options: https://roots.io/trellis/docs/wordpress-sites # Define accompanying passwords/secrets in group_vars/development/vault.yml wordpress_sites: @@ -11,5 +12,6 @@ wordpress_sites: enabled: false ssl: enabled: false + provider: self-signed cache: enabled: false diff --git a/group_vars/production/wordpress_sites.yml b/group_vars/production/wordpress_sites.yml index 884d280994..04bb0d2210 100644 --- a/group_vars/production/wordpress_sites.yml +++ b/group_vars/production/wordpress_sites.yml @@ -1,4 +1,5 @@ # Documentation: https://roots.io/trellis/docs/remote-server-setup/ +# `wordpress_sites` options: https://roots.io/trellis/docs/wordpress-sites # Define accompanying passwords/secrets in group_vars/production/vault.yml wordpress_sites: diff --git a/group_vars/staging/wordpress_sites.yml b/group_vars/staging/wordpress_sites.yml index 23d40c30c9..d9f5146e68 100644 --- a/group_vars/staging/wordpress_sites.yml +++ b/group_vars/staging/wordpress_sites.yml @@ -1,4 +1,5 @@ # Documentation: https://roots.io/trellis/docs/remote-server-setup/ +# `wordpress_sites` options: https://roots.io/trellis/docs/wordpress-sites # Define accompanying passwords/secrets in group_vars/staging/vault.yml wordpress_sites: @@ -13,5 +14,6 @@ wordpress_sites: enabled: false ssl: enabled: false + provider: letsencrypt cache: enabled: false diff --git a/hosts/production b/hosts/production index 2d24f15265..f922c4c74a 100644 --- a/hosts/production +++ b/hosts/production @@ -2,7 +2,7 @@ # List each machine only once per [group], even if it will host multiple sites. [production] -your_server_ip +your_server_hostname [web] -your_server_ip +your_server_hostname diff --git a/hosts/staging b/hosts/staging index daa4c70c46..012b254be1 100644 --- a/hosts/staging +++ b/hosts/staging @@ -2,7 +2,7 @@ # List each machine only once per [group], even if it will host multiple sites. [staging] -your_server_ip +your_server_hostname [web] -your_server_ip +your_server_hostname From fc44e94d6f174792b00833d79a89dd92d4b1daf8 Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Tue, 29 Mar 2016 10:26:25 -0400 Subject: [PATCH 19/52] Fix two missing defaults --- roles/wordpress-install/tasks/main.yml | 4 ++-- roles/wordpress-setup/templates/wordpress-site.conf.j2 | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/roles/wordpress-install/tasks/main.yml b/roles/wordpress-install/tasks/main.yml index cd89e48bdd..35657931f0 100644 --- a/roles/wordpress-install/tasks/main.yml +++ b/roles/wordpress-install/tasks/main.yml @@ -29,7 +29,7 @@ --allow-root --url="{{ site_env.wp_home }}" --title="{{ item.value.site_title | default(item.key) }}" - --admin_user="{{ item.value.admin_user }}" + --admin_user="{{ item.value.admin_user | default('admin') }}" --admin_password="{{ vault_wordpress_sites[item.key].admin_password }}" --admin_email="{{ item.value.admin_email }}" args: @@ -54,7 +54,7 @@ --base="{{ item.value.multisite.base_path | default('/') }}" --subdomains="{{ item.value.multisite.subdomains | default('false') }}" --title="{{ item.value.site_title | default(item.key) }}" - --admin_user="{{ item.value.admin_user }}" + --admin_user="{{ item.value.admin_user | default('admin') }}" --admin_password="{{ vault_wordpress_sites[item.key].admin_password }}" --admin_email="{{ item.value.admin_email }}" args: diff --git a/roles/wordpress-setup/templates/wordpress-site.conf.j2 b/roles/wordpress-setup/templates/wordpress-site.conf.j2 index 111d528b9e..391530ee92 100644 --- a/roles/wordpress-setup/templates/wordpress-site.conf.j2 +++ b/roles/wordpress-setup/templates/wordpress-site.conf.j2 @@ -7,7 +7,7 @@ server { listen 80; {% endif %} - server_name {% for host in item.value.site_hosts %} {{ host }} {% if item.value.multisite.subdomains %} *.{{ host }} {% endif %} {% endfor %}; + server_name {% for host in item.value.site_hosts %} {{ host }} {% if item.value.multisite.subdomains | default(false) %} *.{{ host }} {% endif %} {% endfor %}; access_log {{ www_root }}/{{ item.key }}/logs/access.log; error_log {{ www_root }}/{{ item.key }}/logs/error.log; From 249dfdd409fc48451b4cbb5aa52f58a0ff02707f Mon Sep 17 00:00:00 2001 From: Phil Nelson Date: Tue, 29 Mar 2016 21:50:01 -0700 Subject: [PATCH 20/52] Move modules and plugins to lib/trellis directory --- CHANGELOG.md | 1 + ansible.cfg | 4 +++- {library => lib/trellis/modules}/deploy_helper.py | 0 .../trellis/plugins/filter/filters.py | 0 4 files changed, 4 insertions(+), 1 deletion(-) rename {library => lib/trellis/modules}/deploy_helper.py (100%) rename filter_plugins/trellis_filters.py => lib/trellis/plugins/filter/filters.py (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7fb1f0c33..3aaae2ef8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* Move modules and plugins to `lib/trellis` directory ([#538](https://github.com/roots/trellis/pull/538)) * Automatically set `wp_home` and `wp_siteurl` variables ([#533](https://github.com/roots/trellis/pull/533)) * Switch to Let's Encrypt X3 intermediate certificate and fix chain issues ([#534](https://github.com/roots/trellis/pull/534)) * Supply better defaults for `db_name` and `db_user` ([#529](https://github.com/roots/trellis/pull/529)) diff --git a/ansible.cfg b/ansible.cfg index f598ae869c..76c14027e6 100644 --- a/ansible.cfg +++ b/ansible.cfg @@ -1,7 +1,9 @@ [defaults] -roles_path = vendor/roles +filter_plugins = ~/.ansible/plugins/filter_plugins/:/usr/share/ansible_plugins/filter_plugins:lib/trellis/plugins/filter force_handlers = True inventory = hosts +library = /usr/share/ansible:lib/trellis/modules +roles_path = vendor/roles [ssh_connection] ssh_args = -o ForwardAgent=yes -o ControlMaster=auto -o ControlPersist=60s diff --git a/library/deploy_helper.py b/lib/trellis/modules/deploy_helper.py similarity index 100% rename from library/deploy_helper.py rename to lib/trellis/modules/deploy_helper.py diff --git a/filter_plugins/trellis_filters.py b/lib/trellis/plugins/filter/filters.py similarity index 100% rename from filter_plugins/trellis_filters.py rename to lib/trellis/plugins/filter/filters.py From bbaea65336f8b5ce8871bebf5e6d1b511c232e28 Mon Sep 17 00:00:00 2001 From: swaincreates Date: Fri, 1 Apr 2016 14:33:21 -0400 Subject: [PATCH 21/52] Add https template to be included in wordpress-site.conf for SSL blocks, including the redirect --- roles/wordpress-setup/templates/https.conf.j2 | 22 +++++++++++++++ .../templates/wordpress-site.conf.j2 | 27 +++---------------- 2 files changed, 26 insertions(+), 23 deletions(-) create mode 100644 roles/wordpress-setup/templates/https.conf.j2 diff --git a/roles/wordpress-setup/templates/https.conf.j2 b/roles/wordpress-setup/templates/https.conf.j2 new file mode 100644 index 0000000000..edc908c7a8 --- /dev/null +++ b/roles/wordpress-setup/templates/https.conf.j2 @@ -0,0 +1,22 @@ +include h5bp/directive-only/ssl.conf; +include h5bp/directive-only/ssl-stapling.conf; + +ssl_dhparam /etc/nginx/ssl/dhparams.pem; +ssl_buffer_size 1400; # 1400 bytes to fit in one MTU + +{% set hsts_max_age = item.value.ssl.hsts_max_age | default(nginx_hsts_max_age) %} +{% set hsts_include_subdomains = item.value.ssl.hsts_include_subdomains | default(nginx_hsts_include_subdomains) | ternary('includeSubdomains', None) %} +{% set hsts_preload = item.value.ssl.hsts_preload | default(nginx_hsts_preload) | ternary('preload', None) %} +add_header Strict-Transport-Security "max-age={{ [hsts_max_age, hsts_include_subdomains, hsts_preload] | reject('none') | join('; ') }}"; + +{% if item.value.ssl.provider | default('manual') == 'manual' and item.value.ssl.cert is defined and item.value.ssl.key is defined -%} + ssl_certificate {{ nginx_path }}/ssl/{{ item.value.ssl.cert | basename }}; + ssl_certificate_key {{ nginx_path }}/ssl/{{ item.value.ssl.key | basename }}; +{%- elif item.value.ssl.provider | default('manual') == 'letsencrypt' -%} + ssl_certificate {{ nginx_path }}/ssl/letsencrypt/{{ item.key }}-bundled.cert; + ssl_certificate_key {{ nginx_path }}/ssl/letsencrypt/{{ item.key }}.key; +{%- elif item.value.ssl.provider | default('manual') == 'self-signed' -%} + ssl_certificate {{ nginx_path }}/ssl/{{ item.key }}.cert; + ssl_trusted_certificate {{ nginx_path }}/ssl/{{ item.key }}.cert; + ssl_certificate_key {{ nginx_path }}/ssl/{{ item.key }}.key; +{%- endif -%} \ No newline at end of file diff --git a/roles/wordpress-setup/templates/wordpress-site.conf.j2 b/roles/wordpress-setup/templates/wordpress-site.conf.j2 index 391530ee92..2aa538e16a 100644 --- a/roles/wordpress-setup/templates/wordpress-site.conf.j2 +++ b/roles/wordpress-setup/templates/wordpress-site.conf.j2 @@ -33,28 +33,7 @@ server { add_header Fastcgi-Cache $upstream_cache_status; {% if item.value.ssl is defined and item.value.ssl.enabled | default(false) -%} - include h5bp/directive-only/ssl.conf; - include h5bp/directive-only/ssl-stapling.conf; - - ssl_dhparam /etc/nginx/ssl/dhparams.pem; - ssl_buffer_size 1400; # 1400 bytes to fit in one MTU - - {% set hsts_max_age = item.value.ssl.hsts_max_age | default(nginx_hsts_max_age) %} - {% set hsts_include_subdomains = item.value.ssl.hsts_include_subdomains | default(nginx_hsts_include_subdomains) | ternary('includeSubdomains', None) %} - {% set hsts_preload = item.value.ssl.hsts_preload | default(nginx_hsts_preload) | ternary('preload', None) %} - add_header Strict-Transport-Security "max-age={{ [hsts_max_age, hsts_include_subdomains, hsts_preload] | reject('none') | join('; ') }}"; - - {% if item.value.ssl.provider | default('manual') == 'manual' and item.value.ssl.cert is defined and item.value.ssl.key is defined -%} - ssl_certificate {{ nginx_path }}/ssl/{{ item.value.ssl.cert | basename }}; - ssl_certificate_key {{ nginx_path }}/ssl/{{ item.value.ssl.key | basename }}; - {%- elif item.value.ssl.provider | default('manual') == 'letsencrypt' -%} - ssl_certificate {{ nginx_path }}/ssl/letsencrypt/{{ item.key }}-bundled.cert; - ssl_certificate_key {{ nginx_path }}/ssl/letsencrypt/{{ item.key }}.key; - {%- elif item.value.ssl.provider | default('manual') == 'self-signed' -%} - ssl_certificate {{ nginx_path }}/ssl/{{ item.key }}.cert; - ssl_trusted_certificate {{ nginx_path }}/ssl/{{ item.key }}.cert; - ssl_certificate_key {{ nginx_path }}/ssl/{{ item.key }}.key; - {%- endif -%} + {{ lookup('template', 'https.conf.j2') }} {% endif %} include includes.d/{{ item.key }}/*.conf; @@ -116,6 +95,8 @@ server { server { {% if item.value.ssl is defined and item.value.ssl.enabled | default(false) -%} listen 443 ssl http2; + + {{ lookup('template', 'https.conf.j2') }} {% else -%} listen 80; {% endif -%} @@ -123,4 +104,4 @@ server { server_name {{ host | reverse_www(append=false) }}; return 301 $scheme://{{ host }}$request_uri; } -{% endfor %} +{% endfor %} \ No newline at end of file From 6c95e33cd8b9d7e51298b00002f3191a5dce53d4 Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Fri, 1 Apr 2016 18:06:28 -0400 Subject: [PATCH 22/52] Fix #489 - Add $realpath_root to fastcgi_cache_key This acts as an automatic cache buster when a deploy happens. `$realpath_root` contains the real/un-symlinked path to the web root. Example: `/srv/www/example.com/releases/20160101` instead of `/srv/www/example.com/current` This will prevent some known bugs where a cached page refers to a now non-existent "revved" asset like Sage generates (a CSS or JS file with a hash in the filename). --- CHANGELOG.md | 1 + roles/nginx/templates/nginx.conf.j2 | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3aaae2ef8b..6fc6e86efa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* Fix #489 - Add $realpath_root to fastcgi_cache_key ([#542](https://github.com/roots/trellis/pull/542)) * Move modules and plugins to `lib/trellis` directory ([#538](https://github.com/roots/trellis/pull/538)) * Automatically set `wp_home` and `wp_siteurl` variables ([#533](https://github.com/roots/trellis/pull/533)) * Switch to Let's Encrypt X3 intermediate certificate and fix chain issues ([#534](https://github.com/roots/trellis/pull/534)) diff --git a/roles/nginx/templates/nginx.conf.j2 b/roles/nginx/templates/nginx.conf.j2 index ab823c8f07..e50b250549 100644 --- a/roles/nginx/templates/nginx.conf.j2 +++ b/roles/nginx/templates/nginx.conf.j2 @@ -32,7 +32,6 @@ error_log {{ nginx_logs_root }}/error.log warn; pid /run/nginx.pid; http { - # Hide nginx version information. server_tokens off; @@ -41,7 +40,7 @@ http { fastcgi_cache_path {{ nginx_cache_path }} levels=1:2 keys_zone=wordpress:{{ nginx_cache_key_storage_size }} max_size={{ nginx_cache_size }} inactive={{ nginx_cache_inactive }}; fastcgi_cache_use_stale updating error timeout invalid_header http_500; fastcgi_cache_lock on; - fastcgi_cache_key $scheme$host$request_uri$request_method; + fastcgi_cache_key $realpath_root$scheme$host$request_uri$request_method; fastcgi_ignore_headers Cache-Control Expires Set-Cookie; fastcgi_pass_header Set-Cookie; fastcgi_pass_header Cookie; From 2f631e2878f7e2046e8faa8cbf45d62e52e31929 Mon Sep 17 00:00:00 2001 From: Phil Nelson Date: Fri, 1 Apr 2016 23:42:16 -0700 Subject: [PATCH 23/52] Handle skipped items in "Notify of challenge failures" --- roles/letsencrypt/tasks/nginx.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/letsencrypt/tasks/nginx.yml b/roles/letsencrypt/tasks/nginx.yml index 69f8bbaa5f..e11ff2ade2 100644 --- a/roles/letsencrypt/tasks/nginx.yml +++ b/roles/letsencrypt/tasks/nginx.yml @@ -48,5 +48,5 @@ Make sure that a valid DNS record exists for {{ item.failed_hosts | join(', ') }} and that they point to this server's IP. If you don't want these domains in your SSL certificate, then remove them from `site_hosts`. See https://roots.io/trellis/docs/ssl for more details. - when: letsencrypt_test_challenges | failed + when: not item | skipped and letsencrypt_test_challenges | failed with_items: "{{ letsencrypt_test_challenges.results }}" From acb55e4a1c5ddc6ed8a21b7dfffff2b0d3bfe91d Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Sat, 2 Apr 2016 12:09:46 -0400 Subject: [PATCH 24/52] Skip setting permalink for multisite --- CHANGELOG.md | 1 + roles/wordpress-install/tasks/main.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fc6e86efa..d1305e76d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* Skip setting permalink for multisite installs ([#546](https://github.com/roots/trellis/pull/546)) * Fix #489 - Add $realpath_root to fastcgi_cache_key ([#542](https://github.com/roots/trellis/pull/542)) * Move modules and plugins to `lib/trellis` directory ([#538](https://github.com/roots/trellis/pull/538)) * Automatically set `wp_home` and `wp_siteurl` variables ([#533](https://github.com/roots/trellis/pull/533)) diff --git a/roles/wordpress-install/tasks/main.yml b/roles/wordpress-install/tasks/main.yml index 35657931f0..fedfc97eb4 100644 --- a/roles/wordpress-install/tasks/main.yml +++ b/roles/wordpress-install/tasks/main.yml @@ -45,7 +45,7 @@ args: chdir: "{{ www_root }}/{{ item.key }}/current/" with_dict: "{{ wordpress_sites }}" - when: wp_install_results | changed + when: wp_install_results | changed and not wp_install_results | skipped - name: Install WP Multisite command: wp core multisite-install From aec670bf22681f81d100c9dbdc6b502c19dab779 Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Fri, 1 Apr 2016 18:34:30 -0400 Subject: [PATCH 25/52] Fix #482 - Multisite is-installed deploy check Optionally add the `--network` option to properly check if a site is installed after deploys for both normal and multisite installs. Ref: http://wp-cli.org/commands/core/is-installed/ --- CHANGELOG.md | 1 + roles/deploy/hooks/finalize-after.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1305e76d5..374644516f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* Fix #482 - Multisite is-installed deploy check ([#543](https://github.com/roots/trellis/pull/543)) * Skip setting permalink for multisite installs ([#546](https://github.com/roots/trellis/pull/546)) * Fix #489 - Add $realpath_root to fastcgi_cache_key ([#542](https://github.com/roots/trellis/pull/542)) * Move modules and plugins to `lib/trellis` directory ([#538](https://github.com/roots/trellis/pull/538)) diff --git a/roles/deploy/hooks/finalize-after.yml b/roles/deploy/hooks/finalize-after.yml index 9f88591f55..017bef2cc5 100644 --- a/roles/deploy/hooks/finalize-after.yml +++ b/roles/deploy/hooks/finalize-after.yml @@ -1,6 +1,6 @@ --- - name: WordPress Installed? - command: wp core is-installed + command: wp core is-installed {{ project.multisite.enabled | default(false) | ternary('--network', '') }} args: chdir: "{{ deploy_helper.new_release_path }}" register: wp_installed From a4a73957a05a1d3dac7cce10dda55a0ec7e099e6 Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Sat, 2 Apr 2016 19:27:12 -0400 Subject: [PATCH 26/52] Suppress sudo warning --- roles/deploy/hooks/finalize-after.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/roles/deploy/hooks/finalize-after.yml b/roles/deploy/hooks/finalize-after.yml index 017bef2cc5..6be3b71bdd 100644 --- a/roles/deploy/hooks/finalize-after.yml +++ b/roles/deploy/hooks/finalize-after.yml @@ -34,3 +34,4 @@ shell: sudo service php7.0-fpm reload args: chdir: "{{ deploy_helper.new_release_path }}" + warn: false From 293dd97191e5922868e4110e2b664fcfd211686f Mon Sep 17 00:00:00 2001 From: Phil Nelson Date: Thu, 31 Mar 2016 22:36:03 -0700 Subject: [PATCH 27/52] Add plugin to pretty print Ansible msg output New 'stdout_callback' `output.py` augments Ansible's `default.py`. Displays `msg` attribute, interpreting newline characters and applying textwrap. --- CHANGELOG.md | 1 + ansible.cfg | 2 + lib/trellis/__init__.py | 3 + lib/trellis/plugins/callback/output.py | 97 ++++++++++++++++++++++++++ lib/trellis/utils/__init__.py | 3 + lib/trellis/utils/output.py | 52 ++++++++++++++ 6 files changed, 158 insertions(+) create mode 100644 lib/trellis/__init__.py create mode 100644 lib/trellis/plugins/callback/output.py create mode 100644 lib/trellis/utils/__init__.py create mode 100644 lib/trellis/utils/output.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 374644516f..c0f29ce005 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* Add plugin to pretty print Ansible msg output ([#544](https://github.com/roots/trellis/pull/544)) * Fix #482 - Multisite is-installed deploy check ([#543](https://github.com/roots/trellis/pull/543)) * Skip setting permalink for multisite installs ([#546](https://github.com/roots/trellis/pull/546)) * Fix #489 - Add $realpath_root to fastcgi_cache_key ([#542](https://github.com/roots/trellis/pull/542)) diff --git a/ansible.cfg b/ansible.cfg index 76c14027e6..50277f9d56 100644 --- a/ansible.cfg +++ b/ansible.cfg @@ -1,4 +1,6 @@ [defaults] +callback_plugins = ~/.ansible/plugins/callback_plugins/:/usr/share/ansible_plugins/callback_plugins:lib/trellis/plugins/callback +stdout_callback = output filter_plugins = ~/.ansible/plugins/filter_plugins/:/usr/share/ansible_plugins/filter_plugins:lib/trellis/plugins/filter force_handlers = True inventory = hosts diff --git a/lib/trellis/__init__.py b/lib/trellis/__init__.py new file mode 100644 index 0000000000..980f84a225 --- /dev/null +++ b/lib/trellis/__init__.py @@ -0,0 +1,3 @@ +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type diff --git a/lib/trellis/plugins/callback/output.py b/lib/trellis/plugins/callback/output.py new file mode 100644 index 0000000000..57ce9a3f2f --- /dev/null +++ b/lib/trellis/plugins/callback/output.py @@ -0,0 +1,97 @@ +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os.path +import sys + +from ansible.plugins.callback.default import CallbackModule as CallbackModule_default + +try: + from trellis.utils.output import display_output +except ImportError: + if sys.path.append(os.path.join(os.getcwd(), 'lib')) in sys.path: raise + sys.path.append(sys.path.append(os.path.join(os.getcwd(), 'lib'))) + from trellis.utils.output import display_output + + +class CallbackModule(CallbackModule_default): + ''' Customizes the default Ansible output ''' + + CALLBACK_VERSION = 2.0 + CALLBACK_TYPE = 'stdout' + CALLBACK_NAME = 'output' + + def __init__(self): + + super(CallbackModule, self).__init__() + + self.action = None + self.first_host = True + self.first_item = True + self.task_failed = False + + def display_host_output(self, result): + if 'results' not in result._result: + display_output(result, self.action, self._display.display, self.first_host and self.first_item, self.task_failed) + self.first_host = False + + def display_item_output(self, result): + display_output(result, self.action, self._display.display, self.first_host and self.first_item, self.task_failed) + self.first_item = False + + def reset_task(self, task): + self.action = task._get_parent_attribute('action') + self.first_host = True + self.first_item = True + self.task_failed = False + + # Display dict key only, instead of full json dump + def replace_item_with_key(self, result): + if not self._display.verbosity: + if 'key' in result._result['item']: + result._result['item'] = result._result['item']['key'] + elif 'item' in result._result['item'] and 'key' in result._result['item']['item']: + result._result['item'] = result._result['item']['item']['key'] + + def v2_runner_on_failed(self, result, ignore_errors=False): + self.task_failed = True + self.display_host_output(result) + super(CallbackModule, self).v2_runner_on_failed(result, ignore_errors) + + def v2_runner_on_ok(self, result): + self.display_host_output(result) + super(CallbackModule, self).v2_runner_on_ok(result) + + def v2_runner_on_skipped(self, result): + self.display_host_output(result) + super(CallbackModule, self).v2_runner_on_skipped(result) + + def v2_runner_on_unreachable(self, result): + self.task_failed = True + self.display_host_output(result) + super(CallbackModule, self).v2_runner_on_unreachable(result) + + def v2_playbook_on_task_start(self, task, is_conditional): + self.reset_task(task) + super(CallbackModule, self).v2_playbook_on_task_start(task, is_conditional) + + def v2_playbook_on_handler_task_start(self, task): + self.reset_task(task) + super(CallbackModule, self).v2_playbook_on_handler_task_start(task) + + def v2_playbook_item_on_ok(self, result): + self.display_item_output(result) + self.replace_item_with_key(result) + super(CallbackModule, self).v2_playbook_item_on_ok(result) + + def v2_playbook_item_on_failed(self, result): + self.task_failed = True + self.display_item_output(result) + self.replace_item_with_key(result) + super(CallbackModule, self).v2_playbook_item_on_failed(result) + + def v2_playbook_item_on_skipped(self, result): + self.display_item_output(result) + self.replace_item_with_key(result) + super(CallbackModule, self).v2_playbook_item_on_skipped(result) diff --git a/lib/trellis/utils/__init__.py b/lib/trellis/utils/__init__.py new file mode 100644 index 0000000000..980f84a225 --- /dev/null +++ b/lib/trellis/utils/__init__.py @@ -0,0 +1,3 @@ +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type diff --git a/lib/trellis/utils/output.py b/lib/trellis/utils/output.py new file mode 100644 index 0000000000..edcc22846b --- /dev/null +++ b/lib/trellis/utils/output.py @@ -0,0 +1,52 @@ +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import textwrap + +from ansible.utils.unicode import to_unicode + +def display_output(result, action, display, first, task_failed=False): + msg = '' + result = result._result + wrap_width = 77 + failed = 'failed' in result or 'unreachable' in result + + # Only display msg if debug module or if failed (some modules have undesired 'msg' on 'ok') + if 'msg' in result and (failed or action == 'debug'): + msg = result.pop('msg', '') + + # Disable Ansible's verbose setting for debug module to avoid the CallbackBase._dump_results() + if '_ansible_verbose_always' in result: + del result['_ansible_verbose_always'] + + # Display additional info when failed + if failed: + items = (item for item in ['module_stderr', 'module_stdout', 'stderr'] if item in result and to_unicode(result[item]) != '') + for item in items: + msg = result[item] if msg == '' else '\n'.join([msg, result.pop(item, '')]) + + # Add blank line between this fail message and the json dump Ansible displays next + msg = '\n'.join([msg, '']) + + # Must pass unicode strings to Display.display() to prevent UnicodeError tracebacks + if isinstance(msg, list): + msg = '\n'.join([to_unicode(x) for x in msg]) + elif not isinstance(msg, unicode): + msg = to_unicode(msg) + + # Wrap text + msg = '\n'.join([textwrap.fill(line, wrap_width, replace_whitespace=False) + for line in msg.splitlines()]) + + # Display msg with horizontal rule between hosts/items + hr = '-' * int(wrap_width*.67) + if msg == '': + if task_failed and not first: + display(hr, 'bright gray') + else: + return + else: + if not first: + display(hr, 'bright gray') + display(msg, 'red' if failed else 'bright purple') From aa13bc416e93fb1b246f1851b2bc70bae202296f Mon Sep 17 00:00:00 2001 From: Phil Nelson Date: Sun, 3 Apr 2016 17:40:37 -0700 Subject: [PATCH 28/52] Display system info with fail output --- Vagrantfile | 4 ++- lib/trellis/plugins/callback/output.py | 13 ++++++++- lib/trellis/utils/output.py | 39 ++++++++++++++++++++++++-- windows.sh | 2 +- 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/Vagrantfile b/Vagrantfile index ccb6799b06..8b5180659c 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -73,6 +73,7 @@ Vagrant.configure('2') do |config| if Vagrant::Util::Platform.windows? config.vm.provision :shell do |sh| sh.path = File.join(ANSIBLE_PATH, 'windows.sh') + sh.args = [Vagrant::VERSION] end else config.vm.provision :ansible do |ansible| @@ -82,9 +83,10 @@ Vagrant.configure('2') do |config| 'development' => ['default'] } + ansible.extra_vars = {'vagrant_version' => Vagrant::VERSION} if vars = ENV['ANSIBLE_VARS'] extra_vars = Hash[vars.split(',').map { |pair| pair.split('=') }] - ansible.extra_vars = extra_vars + ansible.extra_vars.merge(extra_vars) end end end diff --git a/lib/trellis/plugins/callback/output.py b/lib/trellis/plugins/callback/output.py index 57ce9a3f2f..dd4427db7e 100644 --- a/lib/trellis/plugins/callback/output.py +++ b/lib/trellis/plugins/callback/output.py @@ -5,6 +5,7 @@ import os.path import sys +from ansible.parsing.dataloader import DataLoader from ansible.plugins.callback.default import CallbackModule as CallbackModule_default try: @@ -30,10 +31,11 @@ def __init__(self): self.first_host = True self.first_item = True self.task_failed = False + self.vagrant_version = None def display_host_output(self, result): if 'results' not in result._result: - display_output(result, self.action, self._display.display, self.first_host and self.first_item, self.task_failed) + display_output(result, self.action, self._display.display, self.first_host and self.first_item, self.task_failed, self.vagrant_version) self.first_host = False def display_item_output(self, result): @@ -80,6 +82,15 @@ def v2_playbook_on_handler_task_start(self, task): self.reset_task(task) super(CallbackModule, self).v2_playbook_on_handler_task_start(task) + def v2_playbook_on_play_start(self, play): + super(CallbackModule, self).v2_playbook_on_play_start(play) + + # Check for relevant settings or overrides passed via cli --extra-vars + loader = DataLoader() + play_vars = play.get_variable_manager().get_vars(loader=loader, play=play) + if 'vagrant_version' in play_vars: + self.vagrant_version = play_vars['vagrant_version'] + def v2_playbook_item_on_ok(self, result): self.display_item_output(result) self.replace_item_with_key(result) diff --git a/lib/trellis/utils/output.py b/lib/trellis/utils/output.py index edcc22846b..df099eb83b 100644 --- a/lib/trellis/utils/output.py +++ b/lib/trellis/utils/output.py @@ -2,11 +2,41 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type +import os.path +import platform +import re import textwrap +from ansible import __version__ from ansible.utils.unicode import to_unicode -def display_output(result, action, display, first, task_failed=False): +def system(vagrant_version=None): + # Get most recent Trellis CHANGELOG entry + changelog_msg = '' + changelog = os.path.join(os.getcwd(), 'CHANGELOG.md') + + if os.path.isfile(changelog): + with open(changelog) as f: + str = f.read(200) + + # Retrieve release number if it is most recent entry + release = re.search(r'^###\s((?!HEAD).*)', str) + if release is not None: + changelog_msg = '\n Trellis {0}'.format(release.group(1)) + + # Retrieve most recent changelog entry + else: + change = re.search(r'.*\n\*\s*([^\(\n\[]+)', str) + if change is not None: + changelog_msg = '\n Trellis at "{0}"'.format(change.group(1).strip()) + + # Vagrant info, if available + vagrant = ' Vagrant {0};'.format(vagrant_version) if vagrant_version else '' + + # Assemble components and return + return 'System info:\n Ansible {0};{1} {2}{3}'.format(__version__, vagrant, platform.system(), changelog_msg) + +def display_output(result, action, display, first, task_failed=False, vagrant_version=None): msg = '' result = result._result wrap_width = 77 @@ -39,8 +69,13 @@ def display_output(result, action, display, first, task_failed=False): msg = '\n'.join([textwrap.fill(line, wrap_width, replace_whitespace=False) for line in msg.splitlines()]) - # Display msg with horizontal rule between hosts/items + # Display system info and msg, with horizontal rule between hosts/items hr = '-' * int(wrap_width*.67) + + if task_failed and first: + display(system(vagrant_version), 'bright gray') + display(hr, 'bright gray') + if msg == '': if task_failed and not first: display(hr, 'bright gray') diff --git a/windows.sh b/windows.sh index 10d00e7ed3..f899cc4431 100644 --- a/windows.sh +++ b/windows.sh @@ -45,4 +45,4 @@ fi echo "Running Ansible Playbooks" cd ${ANSIBLE_PATH}/ -ansible-playbook dev.yml +ansible-playbook dev.yml -e vagrant_version=$1 From 11065c8fa15dec49e545932a4bec26b5e82fea65 Mon Sep 17 00:00:00 2001 From: Phil Nelson Date: Sun, 3 Apr 2016 14:04:11 -0700 Subject: [PATCH 29/52] Escape salts and keys to avoid templating errors --- CHANGELOG.md | 1 + ansible.cfg | 1 + group_vars/production/vault.yml | 1 - group_vars/staging/vault.yml | 1 - lib/trellis/plugins/vars/vars.py | 22 ++++++++++++++++++++++ 5 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 lib/trellis/plugins/vars/vars.py diff --git a/CHANGELOG.md b/CHANGELOG.md index c0f29ce005..2b7dd8e6dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* Escape salts and keys to avoid templating errors ([#548](https://github.com/roots/trellis/pull/548)) * Add plugin to pretty print Ansible msg output ([#544](https://github.com/roots/trellis/pull/544)) * Fix #482 - Multisite is-installed deploy check ([#543](https://github.com/roots/trellis/pull/543)) * Skip setting permalink for multisite installs ([#546](https://github.com/roots/trellis/pull/546)) diff --git a/ansible.cfg b/ansible.cfg index 50277f9d56..8489f23436 100644 --- a/ansible.cfg +++ b/ansible.cfg @@ -6,6 +6,7 @@ force_handlers = True inventory = hosts library = /usr/share/ansible:lib/trellis/modules roles_path = vendor/roles +vars_plugins = ~/.ansible/plugins/vars_plugins/:/usr/share/ansible_plugins/vars_plugins:lib/trellis/plugins/vars [ssh_connection] ssh_args = -o ForwardAgent=yes -o ControlMaster=auto -o ControlPersist=60s diff --git a/group_vars/production/vault.yml b/group_vars/production/vault.yml index fda32ba22e..642b21d641 100644 --- a/group_vars/production/vault.yml +++ b/group_vars/production/vault.yml @@ -12,7 +12,6 @@ vault_wordpress_sites: env: db_password: example_dbpassword # Generate your keys here: https://api.wordpress.org/secret-key/1.1/salt/ - # These CANNOT contain the characters "{%" or "{{" in succession auth_key: "generateme" secure_auth_key: "generateme" logged_in_key: "generateme" diff --git a/group_vars/staging/vault.yml b/group_vars/staging/vault.yml index 4948cbd0a2..14c5fbc88e 100644 --- a/group_vars/staging/vault.yml +++ b/group_vars/staging/vault.yml @@ -12,7 +12,6 @@ vault_wordpress_sites: env: db_password: example_dbpassword # Generate your keys here: https://api.wordpress.org/secret-key/1.1/salt/ - # These CANNOT contain the characters "{%" or "{{" in succession auth_key: "generateme" secure_auth_key: "generateme" logged_in_key: "generateme" diff --git a/lib/trellis/plugins/vars/vars.py b/lib/trellis/plugins/vars/vars.py new file mode 100644 index 0000000000..3f13a3798d --- /dev/null +++ b/lib/trellis/plugins/vars/vars.py @@ -0,0 +1,22 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +class VarsModule(object): + ''' Creates and modifies host variables ''' + + def __init__(self, inventory): + self.inventory = inventory + self.inventory_basedir = inventory.basedir() + + # Wrap salts and keys variables in {% raw %} to prevent jinja templating errors + def wrap_salts_in_raw(self, host, hostvars): + if 'vault_wordpress_sites' in hostvars: + for name, site in hostvars['vault_wordpress_sites'].iteritems(): + for key, value in site['env'].iteritems(): + if key.endswith(('_key', '_salt')) and not value.startswith(('{% raw', '{%raw')): + hostvars['vault_wordpress_sites'][name]['env'][key] = ''.join(['{% raw %}', value, '{% endraw %}']) + host.vars['vault_wordpress_sites'] = hostvars['vault_wordpress_sites'] + + def get_host_vars(self, host, vault_password=None): + self.wrap_salts_in_raw(host, host.get_group_vars()) + return {} From 3cad79f7ded349127fa0d183402e5c35b10d0f37 Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Tue, 5 Apr 2016 18:01:12 -0400 Subject: [PATCH 30/52] Fix #550 - Properly skip permalink setup for MU --- CHANGELOG.md | 1 + roles/wordpress-install/tasks/main.yml | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b7dd8e6dc..c61e0310e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* Fix #550 - Properly skip permalink setup for MU ([#551](https://github.com/roots/trellis/pull/551)) * Escape salts and keys to avoid templating errors ([#548](https://github.com/roots/trellis/pull/548)) * Add plugin to pretty print Ansible msg output ([#544](https://github.com/roots/trellis/pull/544)) * Fix #482 - Multisite is-installed deploy check ([#543](https://github.com/roots/trellis/pull/543)) diff --git a/roles/wordpress-install/tasks/main.yml b/roles/wordpress-install/tasks/main.yml index fedfc97eb4..cb5999a3b9 100644 --- a/roles/wordpress-install/tasks/main.yml +++ b/roles/wordpress-install/tasks/main.yml @@ -40,12 +40,11 @@ changed_when: "'WordPress is already installed.' not in wp_install_results.stdout" - name: Setup Permalink Structure - command: wp rewrite structure {{ item.value.initial_permalink_structure | default("/%postname%/") }} - --allow-root + command: wp rewrite structure {{ item.value.initial_permalink_structure | default("/%postname%/") }} --allow-root args: chdir: "{{ www_root }}/{{ item.key }}/current/" with_dict: "{{ wordpress_sites }}" - when: wp_install_results | changed and not wp_install_results | skipped + when: wp_install_results | changed and not item.value.multisite.enabled | default(false) - name: Install WP Multisite command: wp core multisite-install From 329ebffc93ac0d88fe0bcc81b19e31e254d1bffe Mon Sep 17 00:00:00 2001 From: Phil Nelson Date: Fri, 8 Apr 2016 11:51:30 -0600 Subject: [PATCH 31/52] Update version check for Ansible 2.x --- lib/trellis/plugins/vars/vars.py | 6 ++++++ roles/common/defaults/main.yml | 2 +- roles/common/tasks/main.yml | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/trellis/plugins/vars/vars.py b/lib/trellis/plugins/vars/vars.py index 3f13a3798d..792a2449d0 100644 --- a/lib/trellis/plugins/vars/vars.py +++ b/lib/trellis/plugins/vars/vars.py @@ -1,6 +1,12 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type +from ansible import __version__ +from ansible.errors import AnsibleError + +if __version__.startswith('1'): + raise AnsibleError('Trellis no longer supports Ansible 1.x. Please upgrade to Ansible 2.x.') + class VarsModule(object): ''' Creates and modifies host variables ''' diff --git a/roles/common/defaults/main.yml b/roles/common/defaults/main.yml index 56167f9a1c..a3bec1c2e6 100644 --- a/roles/common/defaults/main.yml +++ b/roles/common/defaults/main.yml @@ -1,2 +1,2 @@ -minimum_ansible_version: 1.9.2 +minimum_ansible_version: 2.0.0.2 default_timezone: Etc/UTC diff --git a/roles/common/tasks/main.yml b/roles/common/tasks/main.yml index 84fcfa1e75..f75cb2c19b 100644 --- a/roles/common/tasks/main.yml +++ b/roles/common/tasks/main.yml @@ -4,7 +4,8 @@ that: - "{{ ansible_version is defined }}" - "{{ ansible_version.full | version_compare(minimum_ansible_version, '>=') }}" - msg: "Your Ansible version is too old. Trellis require at least {{ minimum_ansible_version }}. Your version is {{ ansible_version.full | default('< 1.6') }}" + msg: "Your Ansible version is too old. Trellis requires at least {{ minimum_ansible_version }}. Your version is {{ ansible_version.full | default('< 1.6') }}" + run_once: true - name: Update Apt apt: From 9dcb906205a4dffcfd6615cc766bd4add4ff31db Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Thu, 7 Apr 2016 18:27:23 -0400 Subject: [PATCH 32/52] Add issue template --- CONTRIBUTING.md => .github/CONTRIBUTING.md | 0 .github/ISSUE_TEMPLATE.md | 63 ++++++++++++++++++++++ 2 files changed, 63 insertions(+) rename CONTRIBUTING.md => .github/CONTRIBUTING.md (100%) create mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/CONTRIBUTING.md b/.github/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to .github/CONTRIBUTING.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..ec6ac6af71 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,63 @@ +## Submit a feature request or bug report + +- [ ] This is a feature request +- [ ] This is a bug report +- [ ] This request isn't a duplicate of an [existing issue](https://github.com/roots/trellis/issues) +- [ ] I've read the [docs](https://roots.io/trellis/docs) and followed them (if applicable) + +Replace any `X` with your information. + +--- + +**What is the current behavior?** + +X + + +**What is the expected or desired behavior?** + +X + +--- + +## Bug report + +(delete this section if not applicable) + +**Please provide steps to reproduce, including full log output:** + +X + +**Please describe your local environment:** + +Ansible version: X + +OS: X + +Vagrant version: X + +**Where did the bug happen? Development or remote servers?** + +X + +**Please provide a repository or your `wordpress_sites` config (if possible):** + +X + +**Is there a related [Discourse](https://discourse.roots.io/) thread or were any utilized (please link them)?** + +X + +--- + +## Feature Request + +(delete this section if not applicable) + +**Please provide use cases for changing the current behavior:** + +X + +**Other relevant information:** + +X From 367580db77865c05a1fecc8f2cbe5ff8b10333af Mon Sep 17 00:00:00 2001 From: Phil Nelson Date: Thu, 7 Apr 2016 19:44:07 -0600 Subject: [PATCH 33/52] Tidy up output.py by moving some methods to utils --- lib/trellis/plugins/callback/output.py | 59 +++++++------------------- lib/trellis/utils/output.py | 36 +++++++++++++--- roles/common/tasks/main.yml | 2 +- 3 files changed, 47 insertions(+), 50 deletions(-) diff --git a/lib/trellis/plugins/callback/output.py b/lib/trellis/plugins/callback/output.py index dd4427db7e..7521d40f9a 100644 --- a/lib/trellis/plugins/callback/output.py +++ b/lib/trellis/plugins/callback/output.py @@ -9,11 +9,11 @@ from ansible.plugins.callback.default import CallbackModule as CallbackModule_default try: - from trellis.utils.output import display_output + from trellis.utils import output as output except ImportError: if sys.path.append(os.path.join(os.getcwd(), 'lib')) in sys.path: raise sys.path.append(sys.path.append(os.path.join(os.getcwd(), 'lib'))) - from trellis.utils.output import display_output + from trellis.utils import output as output class CallbackModule(CallbackModule_default): @@ -24,62 +24,33 @@ class CallbackModule(CallbackModule_default): CALLBACK_NAME = 'output' def __init__(self): - super(CallbackModule, self).__init__() - - self.action = None - self.first_host = True - self.first_item = True - self.task_failed = False - self.vagrant_version = None - - def display_host_output(self, result): - if 'results' not in result._result: - display_output(result, self.action, self._display.display, self.first_host and self.first_item, self.task_failed, self.vagrant_version) - self.first_host = False - - def display_item_output(self, result): - display_output(result, self.action, self._display.display, self.first_host and self.first_item, self.task_failed) - self.first_item = False - - def reset_task(self, task): - self.action = task._get_parent_attribute('action') - self.first_host = True - self.first_item = True - self.task_failed = False - - # Display dict key only, instead of full json dump - def replace_item_with_key(self, result): - if not self._display.verbosity: - if 'key' in result._result['item']: - result._result['item'] = result._result['item']['key'] - elif 'item' in result._result['item'] and 'key' in result._result['item']['item']: - result._result['item'] = result._result['item']['item']['key'] + output.reset_task_info(self) def v2_runner_on_failed(self, result, ignore_errors=False): self.task_failed = True - self.display_host_output(result) + output.display_host(self, result) super(CallbackModule, self).v2_runner_on_failed(result, ignore_errors) def v2_runner_on_ok(self, result): - self.display_host_output(result) + output.display_host(self, result) super(CallbackModule, self).v2_runner_on_ok(result) def v2_runner_on_skipped(self, result): - self.display_host_output(result) + output.display_host(self, result) super(CallbackModule, self).v2_runner_on_skipped(result) def v2_runner_on_unreachable(self, result): self.task_failed = True - self.display_host_output(result) + output.display_host(self, result) super(CallbackModule, self).v2_runner_on_unreachable(result) def v2_playbook_on_task_start(self, task, is_conditional): - self.reset_task(task) + output.reset_task_info(self, task) super(CallbackModule, self).v2_playbook_on_task_start(task, is_conditional) def v2_playbook_on_handler_task_start(self, task): - self.reset_task(task) + output.reset_task_info(self, task) super(CallbackModule, self).v2_playbook_on_handler_task_start(task) def v2_playbook_on_play_start(self, play): @@ -92,17 +63,17 @@ def v2_playbook_on_play_start(self, play): self.vagrant_version = play_vars['vagrant_version'] def v2_playbook_item_on_ok(self, result): - self.display_item_output(result) - self.replace_item_with_key(result) + output.display_item(self, result) + output.replace_item_with_key(self, result) super(CallbackModule, self).v2_playbook_item_on_ok(result) def v2_playbook_item_on_failed(self, result): self.task_failed = True - self.display_item_output(result) - self.replace_item_with_key(result) + output.display_item(self, result) + output.replace_item_with_key(self, result) super(CallbackModule, self).v2_playbook_item_on_failed(result) def v2_playbook_item_on_skipped(self, result): - self.display_item_output(result) - self.replace_item_with_key(result) + output.display_item(self, result) + output.replace_item_with_key(self, result) super(CallbackModule, self).v2_playbook_item_on_skipped(result) diff --git a/lib/trellis/utils/output.py b/lib/trellis/utils/output.py index df099eb83b..68fae1b8c3 100644 --- a/lib/trellis/utils/output.py +++ b/lib/trellis/utils/output.py @@ -36,14 +36,31 @@ def system(vagrant_version=None): # Assemble components and return return 'System info:\n Ansible {0};{1} {2}{3}'.format(__version__, vagrant, platform.system(), changelog_msg) -def display_output(result, action, display, first, task_failed=False, vagrant_version=None): +def reset_task_info(obj, task=None): + obj.action = None if task is None else task._get_parent_attribute('action') + obj.first_host = True + obj.first_item = True + obj.task_failed = False + obj.vagrant_version = None + +# Display dict key only, instead of full json dump +def replace_item_with_key(obj, result): + if not obj._display.verbosity: + if 'key' in result._result['item']: + result._result['item'] = result._result['item']['key'] + elif 'item' in result._result['item'] and 'key' in result._result['item']['item']: + result._result['item'] = result._result['item']['item']['key'] + +def display(obj, result): msg = '' result = result._result + display = obj._display.display wrap_width = 77 + first = obj.first_host and obj.first_item failed = 'failed' in result or 'unreachable' in result # Only display msg if debug module or if failed (some modules have undesired 'msg' on 'ok') - if 'msg' in result and (failed or action == 'debug'): + if 'msg' in result and (failed or obj.action == 'debug'): msg = result.pop('msg', '') # Disable Ansible's verbose setting for debug module to avoid the CallbackBase._dump_results() @@ -72,12 +89,12 @@ def display_output(result, action, display, first, task_failed=False, vagrant_ve # Display system info and msg, with horizontal rule between hosts/items hr = '-' * int(wrap_width*.67) - if task_failed and first: - display(system(vagrant_version), 'bright gray') + if obj.task_failed and first: + display(system(obj.vagrant_version), 'bright gray') display(hr, 'bright gray') if msg == '': - if task_failed and not first: + if obj.task_failed and not first: display(hr, 'bright gray') else: return @@ -85,3 +102,12 @@ def display_output(result, action, display, first, task_failed=False, vagrant_ve if not first: display(hr, 'bright gray') display(msg, 'red' if failed else 'bright purple') + +def display_host(obj, result): + if 'results' not in result._result: + display(obj, result) + obj.first_host = False + +def display_item(obj, result): + display(obj, result) + obj.first_item = False diff --git a/roles/common/tasks/main.yml b/roles/common/tasks/main.yml index f75cb2c19b..e42e70d9db 100644 --- a/roles/common/tasks/main.yml +++ b/roles/common/tasks/main.yml @@ -5,7 +5,7 @@ - "{{ ansible_version is defined }}" - "{{ ansible_version.full | version_compare(minimum_ansible_version, '>=') }}" msg: "Your Ansible version is too old. Trellis requires at least {{ minimum_ansible_version }}. Your version is {{ ansible_version.full | default('< 1.6') }}" - run_once: true + run_once: true - name: Update Apt apt: From aa1c59240842fe7da1276449df780575c7570953 Mon Sep 17 00:00:00 2001 From: Phil Nelson Date: Sat, 2 Apr 2016 11:58:42 -0700 Subject: [PATCH 34/52] Enable color output for Vagrant shell provisioner windows.sh --- Vagrantfile | 1 + ansible.cfg | 2 ++ windows.sh | 3 ++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Vagrantfile b/Vagrantfile index 8b5180659c..37e0878892 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -74,6 +74,7 @@ Vagrant.configure('2') do |config| config.vm.provision :shell do |sh| sh.path = File.join(ANSIBLE_PATH, 'windows.sh') sh.args = [Vagrant::VERSION] + sh.keep_color = true end else config.vm.provision :ansible do |ansible| diff --git a/ansible.cfg b/ansible.cfg index 8489f23436..ecf390629a 100644 --- a/ansible.cfg +++ b/ansible.cfg @@ -2,9 +2,11 @@ callback_plugins = ~/.ansible/plugins/callback_plugins/:/usr/share/ansible_plugins/callback_plugins:lib/trellis/plugins/callback stdout_callback = output filter_plugins = ~/.ansible/plugins/filter_plugins/:/usr/share/ansible_plugins/filter_plugins:lib/trellis/plugins/filter +force_color = True force_handlers = True inventory = hosts library = /usr/share/ansible:lib/trellis/modules +nocows = 1 roles_path = vendor/roles vars_plugins = ~/.ansible/plugins/vars_plugins/:/usr/share/ansible_plugins/vars_plugins:lib/trellis/plugins/vars diff --git a/windows.sh b/windows.sh index f899cc4431..b74c1f764d 100644 --- a/windows.sh +++ b/windows.sh @@ -6,6 +6,7 @@ # @version 1.0 ANSIBLE_PATH="$(find /vagrant -name 'windows.sh' -printf '%h' -quit)" +export PYTHONUNBUFFERED=1 # Create an ssh key if not already created. if [ ! -f ~/.ssh/id_rsa ]; then @@ -31,7 +32,7 @@ fi # Install Ansible and its dependencies if not installed. if [ ! -f /usr/bin/ansible ]; then echo "Adding Ansible repository..." - sudo apt-add-repository ppa:ansible/ansible + sudo apt-add-repository -y ppa:ansible/ansible echo "Updating system..." sudo apt-get -y update echo "Installing Ansible..." From f61ed28639aba90a13013ad8fea6a47609b866d6 Mon Sep 17 00:00:00 2001 From: Phil Nelson Date: Wed, 6 Apr 2016 02:21:59 -0600 Subject: [PATCH 35/52] Fix ansible.cfg and Ansible plugins for moved Vagrantfile --- Vagrantfile | 7 ++++++- lib/trellis/plugins/callback/output.py | 5 +++-- lib/trellis/utils/output.py | 3 ++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Vagrantfile b/Vagrantfile index 8b5180659c..281c88605f 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -9,8 +9,13 @@ memory = 1024 # in MB ANSIBLE_PATH = __dir__ # absolute path to Ansible directory -# Set Ansible roles_path relative to Ansible directory +# Set Ansible paths relative to Ansible directory +ENV['ANSIBLE_CONFIG'] = ANSIBLE_PATH +ENV['ANSIBLE_CALLBACK_PLUGINS'] = "~/.ansible/plugins/callback_plugins/:/usr/share/ansible_plugins/callback_plugins:#{File.join(ANSIBLE_PATH, 'lib/trellis/plugins/callback')}" +ENV['ANSIBLE_FILTER_PLUGINS'] = "~/.ansible/plugins/filter_plugins/:/usr/share/ansible_plugins/filter_plugins:#{File.join(ANSIBLE_PATH, 'lib/trellis/plugins/filter')}" +ENV['ANSIBLE_LIBRARY'] = "/usr/share/ansible:#{File.join(ANSIBLE_PATH, 'lib/trellis/modules')}" ENV['ANSIBLE_ROLES_PATH'] = File.join(ANSIBLE_PATH, 'vendor', 'roles') +ENV['ANSIBLE_VARS_PLUGINS'] = "~/.ansible/plugins/vars_plugins/:/usr/share/ansible_plugins/vars_plugins:#{File.join(ANSIBLE_PATH, 'lib/trellis/plugins/vars')}" config_file = File.join(ANSIBLE_PATH, 'group_vars', 'development', 'wordpress_sites.yml') diff --git a/lib/trellis/plugins/callback/output.py b/lib/trellis/plugins/callback/output.py index 7521d40f9a..3839606336 100644 --- a/lib/trellis/plugins/callback/output.py +++ b/lib/trellis/plugins/callback/output.py @@ -11,8 +11,9 @@ try: from trellis.utils import output as output except ImportError: - if sys.path.append(os.path.join(os.getcwd(), 'lib')) in sys.path: raise - sys.path.append(sys.path.append(os.path.join(os.getcwd(), 'lib'))) + ansible_path = os.getenv('ANSIBLE_CONFIG', os.getcwd()) + if sys.path.append(os.path.join(ansible_path, 'lib')) in sys.path: raise + sys.path.append(sys.path.append(os.path.join(ansible_path, 'lib'))) from trellis.utils import output as output diff --git a/lib/trellis/utils/output.py b/lib/trellis/utils/output.py index 68fae1b8c3..3020a3ed23 100644 --- a/lib/trellis/utils/output.py +++ b/lib/trellis/utils/output.py @@ -13,7 +13,8 @@ def system(vagrant_version=None): # Get most recent Trellis CHANGELOG entry changelog_msg = '' - changelog = os.path.join(os.getcwd(), 'CHANGELOG.md') + ansible_path = os.getenv('ANSIBLE_CONFIG', os.getcwd()) + changelog = os.path.join(ansible_path, 'CHANGELOG.md') if os.path.isfile(changelog): with open(changelog) as f: From fb97e978500ea2f4304be05ea97b4a40fe4f6e92 Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Sun, 10 Apr 2016 20:52:19 -0400 Subject: [PATCH 36/52] 0.9.7 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c61e0310e6..e2e7cf8d42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -### HEAD +### 0.9.7: April 10th, 2016 * Fix #550 - Properly skip permalink setup for MU ([#551](https://github.com/roots/trellis/pull/551)) * Escape salts and keys to avoid templating errors ([#548](https://github.com/roots/trellis/pull/548)) * Add plugin to pretty print Ansible msg output ([#544](https://github.com/roots/trellis/pull/544)) From 1163c87b5a7512c212146b2edeb9aed895347a7a Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Sun, 10 Apr 2016 20:59:33 -0400 Subject: [PATCH 37/52] :cop: Fix ssmtp role tags --- server.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.yml b/server.yml index f9dc2e3c09..5a0827192e 100644 --- a/server.yml +++ b/server.yml @@ -21,7 +21,7 @@ - { role: users, tags: [users] } - { role: sshd, tags: [sshd] } - { role: mariadb, tags: [mariadb], when: not mysql_remote_database } - - { role: ssmtp, tags: [ssmtp mail] } + - { role: ssmtp, tags: [ssmtp, mail] } - { role: php, tags: [php] } - { role: memcached, tags: [memcached] } - { role: nginx, tags: [nginx] } From cd95d3c54febd61df7bb719d57c734a94c795fc6 Mon Sep 17 00:00:00 2001 From: Phil Nelson Date: Sun, 10 Apr 2016 23:41:54 -0600 Subject: [PATCH 38/52] Tidy up sudoer password definitions and comments --- group_vars/all/security.yml | 2 +- group_vars/all/users.yml | 2 +- group_vars/development/main.yml | 1 - group_vars/development/vault.yml | 4 ---- 4 files changed, 2 insertions(+), 7 deletions(-) diff --git a/group_vars/all/security.yml b/group_vars/all/security.yml index 971fe4b99b..240bbdcf7f 100644 --- a/group_vars/all/security.yml +++ b/group_vars/all/security.yml @@ -12,6 +12,6 @@ ferm_input_list: # Documentation: https://roots.io/trellis/docs/security/ # If sshd_permit_root_login: false, admin_user must be in 'users' (`group_vars/all/users.yml`) with sudo group -# and in 'sudoer_passwords' (`group_vars//main.yml`) +# and in 'vault_sudoer_passwords' (`group_vars/staging/vault.yml`, `group_vars/production/vault.yml`) sshd_permit_root_login: true sshd_password_authentication: false diff --git a/group_vars/all/users.yml b/group_vars/all/users.yml index 30a57aa294..20aa232f12 100644 --- a/group_vars/all/users.yml +++ b/group_vars/all/users.yml @@ -1,7 +1,7 @@ # Documentation: https://roots.io/trellis/docs/ssh-keys/ admin_user: admin -# Also define sudoer_passwords in group_vars//main.yml +# Also define 'vault_sudoer_passwords' (`group_vars/staging/vault.yml`, `group_vars/production/vault.yml`) users: - name: "{{ web_user }}" groups: diff --git a/group_vars/development/main.yml b/group_vars/development/main.yml index d8ba31e48d..961e9c8319 100644 --- a/group_vars/development/main.yml +++ b/group_vars/development/main.yml @@ -1,5 +1,4 @@ env: development ferm_enabled: false mysql_root_password: "{{ vault_mysql_root_password }}" # Define this variable in group_vars/development/vault.yml -sudoer_passwords: "{{ vault_sudoer_passwords }}" # Define this variable in group_vars/development/vault.yml web_user: vagrant diff --git a/group_vars/development/vault.yml b/group_vars/development/vault.yml index a2bead061f..0fe55c3b5d 100644 --- a/group_vars/development/vault.yml +++ b/group_vars/development/vault.yml @@ -1,10 +1,6 @@ # Documentation: https://roots.io/trellis/docs/vault/ vault_mysql_root_password: devpw -# Documentation: https://roots.io/trellis/docs/security/ -vault_sudoer_passwords: - admin: $6$rounds=100000$JUkj1d3hCa6uFp6R$3rZ8jImyCpTP40e4I5APx7SbBvDCM8fB6GP/IGOrsk/GEUTUhl1i/Q2JNOpj9ashLpkgaCxqMqbFKdZdmAh26/ - # Variables to accompany `group_vars/development/wordpress_sites.yml` # Note: the site name (`example.com`) must match up with the site name in the above file. vault_wordpress_sites: From f51003988bfecea0ba94a79b5c476d8f82aca284 Mon Sep 17 00:00:00 2001 From: Phil Nelson Date: Tue, 12 Apr 2016 20:10:11 -0600 Subject: [PATCH 39/52] Pass vault cli args to remote-user ping task --- lib/trellis/plugins/vars/vars.py | 14 ++++++++++++++ roles/remote-user/tasks/main.yml | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/trellis/plugins/vars/vars.py b/lib/trellis/plugins/vars/vars.py index 792a2449d0..418699eb33 100644 --- a/lib/trellis/plugins/vars/vars.py +++ b/lib/trellis/plugins/vars/vars.py @@ -7,12 +7,17 @@ if __version__.startswith('1'): raise AnsibleError('Trellis no longer supports Ansible 1.x. Please upgrade to Ansible 2.x.') +# This import will produce Traceback in Ansible 1.x, so place after version check +from __main__ import cli + + class VarsModule(object): ''' Creates and modifies host variables ''' def __init__(self, inventory): self.inventory = inventory self.inventory_basedir = inventory.basedir() + self._options = cli.options if cli else None # Wrap salts and keys variables in {% raw %} to prevent jinja templating errors def wrap_salts_in_raw(self, host, hostvars): @@ -23,6 +28,15 @@ def wrap_salts_in_raw(self, host, hostvars): hostvars['vault_wordpress_sites'][name]['env'][key] = ''.join(['{% raw %}', value, '{% endraw %}']) host.vars['vault_wordpress_sites'] = hostvars['vault_wordpress_sites'] + def cli_args_vault(self): + if self._options.ask_vault_pass: + return '--ask-vault-pass' + elif self._options.vault_password_file: + return '--vault-password-file {0}'.format(self._options.vault_password_file) + else: + return '' + def get_host_vars(self, host, vault_password=None): self.wrap_salts_in_raw(host, host.get_group_vars()) + host.vars['cli_args_vault'] = self.cli_args_vault() return {} diff --git a/roles/remote-user/tasks/main.yml b/roles/remote-user/tasks/main.yml index 28478e1ed5..7d96e5e25c 100644 --- a/roles/remote-user/tasks/main.yml +++ b/roles/remote-user/tasks/main.yml @@ -1,6 +1,6 @@ --- - name: Determine whether to connect as root or admin_user - local_action: command ansible {{ inventory_hostname }} -m ping{{ (inventory_file == None) | ternary('', ' -i ' + inventory_file | string) }} -u root + local_action: command ansible {{ inventory_hostname }} -m ping{{ (inventory_file == None) | ternary('', ' -i ' + inventory_file | string) }} -u root {{ cli_args_vault | default('') }} failed_when: false changed_when: false register: root_status From 1d794d6d59b42d54a5a0bb253af8b4c406a8fb2e Mon Sep 17 00:00:00 2001 From: jameskraus Date: Thu, 14 Apr 2016 15:09:51 -0400 Subject: [PATCH 40/52] Adding dbus to essentials --- roles/common/tasks/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/roles/common/tasks/main.yml b/roles/common/tasks/main.yml index e42e70d9db..a42e9e0ffe 100644 --- a/roles/common/tasks/main.yml +++ b/roles/common/tasks/main.yml @@ -22,6 +22,7 @@ - python-mysqldb - curl - git-core + - dbus - name: Validate timezone variable stat: From 6b33ba9931ea6bb658f08b6f5992c57fe49e3d91 Mon Sep 17 00:00:00 2001 From: Phil Nelson Date: Thu, 14 Apr 2016 23:43:47 -0600 Subject: [PATCH 41/52] Add 'reason' to attr extracted for pretty-print --- lib/trellis/utils/output.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/trellis/utils/output.py b/lib/trellis/utils/output.py index 3020a3ed23..63fdee8f06 100644 --- a/lib/trellis/utils/output.py +++ b/lib/trellis/utils/output.py @@ -70,7 +70,7 @@ def display(obj, result): # Display additional info when failed if failed: - items = (item for item in ['module_stderr', 'module_stdout', 'stderr'] if item in result and to_unicode(result[item]) != '') + items = (item for item in ['reason', 'module_stderr', 'module_stdout', 'stderr'] if item in result and to_unicode(result[item]) != '') for item in items: msg = result[item] if msg == '' else '\n'.join([msg, result.pop(item, '')]) From 9d9c2abed4723dff6ce032593f8490b5955d35b1 Mon Sep 17 00:00:00 2001 From: Phil Nelson Date: Fri, 15 Apr 2016 18:07:40 -0600 Subject: [PATCH 42/52] Enable Let's Encrypt to transition http sites to https --- CHANGELOG.md | 3 +++ roles/common/handlers/main.yml | 4 +-- roles/common/tasks/reload_nginx.yml | 12 +++++++++ roles/letsencrypt/defaults/main.yml | 1 + roles/letsencrypt/tasks/certificates.yml | 13 +++------ roles/letsencrypt/tasks/nginx.yml | 27 ++++++++++--------- roles/letsencrypt/tasks/setup.yml | 5 ---- roles/wordpress-setup/tasks/nginx.yml | 17 +++++------- .../templates/wordpress-site.conf.j2 | 17 ++++++++++-- server.yml | 2 +- 10 files changed, 58 insertions(+), 43 deletions(-) create mode 100644 roles/common/tasks/reload_nginx.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index e2e7cf8d42..2eec0e7904 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### HEAD +* Enable Let's Encrypt to transition http sites to https ([#565](https://github.com/roots/trellis/pull/565)) + ### 0.9.7: April 10th, 2016 * Fix #550 - Properly skip permalink setup for MU ([#551](https://github.com/roots/trellis/pull/551)) * Escape salts and keys to avoid templating errors ([#548](https://github.com/roots/trellis/pull/548)) diff --git a/roles/common/handlers/main.yml b/roles/common/handlers/main.yml index f0abc92939..4f1e2add53 100644 --- a/roles/common/handlers/main.yml +++ b/roles/common/handlers/main.yml @@ -10,6 +10,4 @@ state: reloaded - name: reload nginx - service: - name: nginx - state: reloaded + include: reload_nginx.yml diff --git a/roles/common/tasks/reload_nginx.yml b/roles/common/tasks/reload_nginx.yml new file mode 100644 index 0000000000..f8e32d2890 --- /dev/null +++ b/roles/common/tasks/reload_nginx.yml @@ -0,0 +1,12 @@ +--- +- name: test nginx conf + command: nginx -t + register: nginx_test + changed_when: false + +- name: reload nginx + service: + name: nginx + state: reloaded + when: nginx_test | success + changed_when: false diff --git a/roles/letsencrypt/defaults/main.yml b/roles/letsencrypt/defaults/main.yml index 57e54fc41e..900e70caf5 100644 --- a/roles/letsencrypt/defaults/main.yml +++ b/roles/letsencrypt/defaults/main.yml @@ -1,6 +1,7 @@ sites_using_letsencrypt: "[{% for name, site in wordpress_sites.iteritems() if site.ssl.enabled and site.ssl.provider | default('manual') == 'letsencrypt' %}'{{ name }}',{% endfor %}]" letsencrypt_enabled: "{{ sites_using_letsencrypt | count > 0 }}" site_uses_letsencrypt: "{{ item.value.ssl is defined and item.value.ssl.enabled | default(false) and item.value.ssl.provider | default('manual') == 'letsencrypt' }}" +sites_need_confs: "False in [{% for item in nginx_confs.results if 'stat' in item %}{{ item.stat.exists }},{% endfor %}]" acme_tiny_repo: 'https://github.com/diafygi/acme-tiny.git' acme_tiny_commit: '69a457269a6392ac31b629b4e103e8ea7dd282c9' diff --git a/roles/letsencrypt/tasks/certificates.yml b/roles/letsencrypt/tasks/certificates.yml index c0ce8eb5ad..b74edbcb05 100644 --- a/roles/letsencrypt/tasks/certificates.yml +++ b/roles/letsencrypt/tasks/certificates.yml @@ -37,19 +37,12 @@ chdir: "{{ acme_tiny_data_directory }}" register: generate_initial_cert changed_when: generate_initial_cert.stdout is defined and 'Created' in generate_initial_cert.stdout + notify: reload nginx - name: Disable Nginx site file: path: "{{ nginx_path }}/sites-enabled/letsencrypt-{{ item.key }}.conf" state: absent with_dict: "{{ wordpress_sites }}" - -- name: Test nginx conf - command: nginx -t - changed_when: false - -- name: Trigger nginx reload - service: - name: nginx - state: reloaded - changed_when: false + when: sites_need_confs + notify: reload nginx diff --git a/roles/letsencrypt/tasks/nginx.yml b/roles/letsencrypt/tasks/nginx.yml index e11ff2ade2..564cdf8488 100644 --- a/roles/letsencrypt/tasks/nginx.yml +++ b/roles/letsencrypt/tasks/nginx.yml @@ -1,30 +1,33 @@ -- name: Check for existing certificates +- name: Check for existing Nginx conf per site stat: - path: "{{ letsencrypt_certs_dir }}/{{ item.key }}.cert" - register: letsencrypt_existing_certs + path: "{{ nginx_path }}/sites-enabled/{{ item.key }}.conf" + register: nginx_confs when: site_uses_letsencrypt with_dict: "{{ wordpress_sites }}" -- name: Create Nginx sites for challenges +- name: Create Nginx conf for challenges location + template: + src: acme-challenge-location.conf.j2 + dest: "{{ nginx_path }}/acme-challenge-location.conf" + when: sites_need_confs + +- name: Create needed Nginx confs for challenges template: src: nginx-challenge-site.conf.j2 dest: "{{ nginx_path }}/sites-available/letsencrypt-{{ item.item.key }}.conf" when: not item | skipped and not item.stat.exists - with_items: "{{ letsencrypt_existing_certs.results }}" + with_items: "{{ nginx_confs.results }}" -- name: Enable Nginx site +- name: Enable Nginx sites file: src: "{{ nginx_path }}/sites-available/letsencrypt-{{ item.item.key }}.conf" dest: "{{ nginx_path }}/sites-enabled/letsencrypt-{{ item.item.key }}.conf" state: link when: not item | skipped and not item.stat.exists - with_items: "{{ letsencrypt_existing_certs.results }}" + with_items: "{{ nginx_confs.results }}" -- name: Trigger nginx reload - service: - name: nginx - state: reloaded - changed_when: false +- include: "{{ playbook_dir }}/roles/common/tasks/reload_nginx.yml" + when: sites_need_confs - name: Create test Acme Challenge file shell: touch {{ acme_tiny_challenges_directory }}/ping.txt diff --git a/roles/letsencrypt/tasks/setup.yml b/roles/letsencrypt/tasks/setup.yml index b9e3048f41..49b0886bbd 100644 --- a/roles/letsencrypt/tasks/setup.yml +++ b/roles/letsencrypt/tasks/setup.yml @@ -49,8 +49,3 @@ url: "{{ letsencrypt_intermediate_cert_url }}" dest: "{{ letsencrypt_intermediate_cert_path }}" sha256sum: "{{ letsencrypt_intermediate_cert_sha256sum }}" - -- name: Create Nginx conf for challenges location - template: - src: acme-challenge-location.conf.j2 - dest: "{{ nginx_path }}/acme-challenge-location.conf" diff --git a/roles/wordpress-setup/tasks/nginx.yml b/roles/wordpress-setup/tasks/nginx.yml index 60c00767d6..1ef7378b56 100644 --- a/roles/wordpress-setup/tasks/nginx.yml +++ b/roles/wordpress-setup/tasks/nginx.yml @@ -47,6 +47,12 @@ }}" notify: reload nginx +- name: Create Nginx conf for challenges location + template: + src: "{{ playbook_dir }}/roles/letsencrypt/templates/acme-challenge-location.conf.j2" + dest: "{{ nginx_path }}/acme-challenge-location.conf" + notify: reload nginx + - name: Create WordPress configuration for Nginx template: src: "wordpress-site.conf.j2" @@ -62,13 +68,4 @@ group: root state: link with_dict: "{{ wordpress_sites }}" - -- name: test nginx conf - command: nginx -t - changed_when: false - -- name: trigger nginx reload - service: - name: nginx - state: reloaded - changed_when: false + notify: reload nginx diff --git a/roles/wordpress-setup/templates/wordpress-site.conf.j2 b/roles/wordpress-setup/templates/wordpress-site.conf.j2 index 2aa538e16a..ce4c7c8f8d 100644 --- a/roles/wordpress-setup/templates/wordpress-site.conf.j2 +++ b/roles/wordpress-setup/templates/wordpress-site.conf.j2 @@ -36,6 +36,10 @@ server { {{ lookup('template', 'https.conf.j2') }} {% endif %} + {% if item.value.ssl is not defined or not item.value.ssl.enabled | default(false) -%} + include acme-challenge-location.conf; + {% endif %} + include includes.d/{{ item.key }}/*.conf; include wordpress.conf; @@ -102,6 +106,15 @@ server { {% endif -%} server_name {{ host | reverse_www(append=false) }}; - return 301 $scheme://{{ host }}$request_uri; + + {% if item.value.ssl is not defined or not item.value.ssl.enabled | default(false) -%} + include acme-challenge-location.conf; + + location / { + return 301 $scheme://{{ host }}$request_uri; + } + {% else %} + return 301 $scheme://{{ host }}$request_uri; + {% endif %} } -{% endfor %} \ No newline at end of file +{% endfor %} diff --git a/server.yml b/server.yml index 5a0827192e..c1dcecde9c 100644 --- a/server.yml +++ b/server.yml @@ -29,4 +29,4 @@ - { role: composer, tags: [composer] } - { role: wp-cli, tags: [wp-cli] } - { role: letsencrypt, tags: [letsencrypt], when: letsencrypt_enabled } - - { role: wordpress-setup, tags: [wordpress, wordpress-setup] } + - { role: wordpress-setup, tags: [wordpress, wordpress-setup, letsencrypt] } From 3233b930536748a508841684eea1ae02e2b9bd7f Mon Sep 17 00:00:00 2001 From: Phil Nelson Date: Sat, 16 Apr 2016 12:08:14 -0600 Subject: [PATCH 43/52] Define development default for Let's Encrypt variable --- group_vars/development/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/group_vars/development/main.yml b/group_vars/development/main.yml index 961e9c8319..8d7c15913c 100644 --- a/group_vars/development/main.yml +++ b/group_vars/development/main.yml @@ -1,3 +1,4 @@ +acme_tiny_challenges_directory: "{{ www_root }}/letsencrypt" env: development ferm_enabled: false mysql_root_password: "{{ vault_mysql_root_password }}" # Define this variable in group_vars/development/vault.yml From 3878399eff83c8c04edcec943dc720ce017413bb Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Sat, 16 Apr 2016 20:54:12 -0400 Subject: [PATCH 44/52] Fix #569 Only skip subdomains for non-www domains The logic to skip the reversing functionality for subdomains was also affecting domains starting with `www`. Now it correctly leaves `www` domains alone and only skips other subdomains. --- CHANGELOG.md | 1 + lib/trellis/plugins/filter/filters.py | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2eec0e7904..a534bda746 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* Fix #569 - Only skip subdomains for non-www domains ([#570](https://github.com/roots/trellis/pull/570)) * Enable Let's Encrypt to transition http sites to https ([#565](https://github.com/roots/trellis/pull/565)) ### 0.9.7: April 10th, 2016 diff --git a/lib/trellis/plugins/filter/filters.py b/lib/trellis/plugins/filter/filters.py index 5d9e675367..b76497c453 100644 --- a/lib/trellis/plugins/filter/filters.py +++ b/lib/trellis/plugins/filter/filters.py @@ -26,13 +26,13 @@ def reverse_www(hosts, enabled=True, append=True): elif isinstance(hosts, string_types): host = hosts - if len(host.split('.')) > 2: - return host - if host.startswith('www.'): return host[4:] else: - return 'www.{0}'.format(host) + if len(host.split('.')) > 2: + return host + else: + return 'www.{0}'.format(host) # Handle invalid input type else: From ed6c63587adb3ffbdd60f64644470d9f7d81d13d Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Sun, 17 Apr 2016 14:10:29 -0400 Subject: [PATCH 45/52] Update salts link to roots.io version --- group_vars/production/vault.yml | 2 +- group_vars/staging/vault.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/group_vars/production/vault.yml b/group_vars/production/vault.yml index 642b21d641..7bb5f5e19b 100644 --- a/group_vars/production/vault.yml +++ b/group_vars/production/vault.yml @@ -11,7 +11,7 @@ vault_wordpress_sites: example.com: env: db_password: example_dbpassword - # Generate your keys here: https://api.wordpress.org/secret-key/1.1/salt/ + # Generate your keys here: https://roots.io/salts.html auth_key: "generateme" secure_auth_key: "generateme" logged_in_key: "generateme" diff --git a/group_vars/staging/vault.yml b/group_vars/staging/vault.yml index 14c5fbc88e..edb82a96a7 100644 --- a/group_vars/staging/vault.yml +++ b/group_vars/staging/vault.yml @@ -11,7 +11,7 @@ vault_wordpress_sites: example.com: env: db_password: example_dbpassword - # Generate your keys here: https://api.wordpress.org/secret-key/1.1/salt/ + # Generate your keys here: https://roots.io/salts.html auth_key: "generateme" secure_auth_key: "generateme" logged_in_key: "generateme" From 255c461f1d5d2c1378ce1a71fd0feadae3f49cb6 Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Wed, 20 Apr 2016 20:45:04 -0400 Subject: [PATCH 46/52] Fix #563 - Improve remote databases Previously you had to manually toggle the remote database option via `mysql_remote_database` (which also didn't fully work). Now Trellis automatically detects if the sites are using a remote `db_host` or not (aka not `localhost`) and handles things accordingly. This also installs the mysql-client now regardless. --- CHANGELOG.md | 1 + dev.yml | 2 +- group_vars/all/database.yml | 3 - roles/mariadb/defaults/main.yml | 12 ++- roles/mariadb/tasks/main.yml | 106 ++++++++++++----------- roles/wordpress-setup/defaults/main.yml | 1 + roles/wordpress-setup/tasks/database.yml | 4 +- server.yml | 2 +- 8 files changed, 71 insertions(+), 60 deletions(-) delete mode 100644 group_vars/all/database.yml create mode 100644 roles/wordpress-setup/defaults/main.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index a534bda746..124fd9740f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* Fix #563 - Improve remote databases ([#573](https://github.com/roots/trellis/pull/573)) * Fix #569 - Only skip subdomains for non-www domains ([#570](https://github.com/roots/trellis/pull/570)) * Enable Let's Encrypt to transition http sites to https ([#565](https://github.com/roots/trellis/pull/565)) diff --git a/dev.yml b/dev.yml index 3a521dfc51..b35ef3896f 100644 --- a/dev.yml +++ b/dev.yml @@ -10,7 +10,7 @@ - { role: ferm, tags: [ferm] } - { role: ntp } - { role: sshd, tags: [sshd] } - - { role: mariadb, tags: [mariadb], when: not mysql_remote_database } + - { role: mariadb, tags: [mariadb] } - { role: ssmtp, tags: [ssmtp mail] } - { role: mailhog, tags: [mailhog mail] } - { role: php, tags: [php] } diff --git a/group_vars/all/database.yml b/group_vars/all/database.yml deleted file mode 100644 index 638f435ce8..0000000000 --- a/group_vars/all/database.yml +++ /dev/null @@ -1,3 +0,0 @@ -mariadb_dist: trusty -mysql_remote_database: false -mysql_root_user: root diff --git a/roles/mariadb/defaults/main.yml b/roles/mariadb/defaults/main.yml index 6ebb4267f9..32b3d530d4 100644 --- a/roles/mariadb/defaults/main.yml +++ b/roles/mariadb/defaults/main.yml @@ -1,4 +1,8 @@ -keyserver_fingerprint: "0xcbcb082a1bb943db" -mirror: nyc2.mirrors.digitalocean.com -version: "10.0" -binary_logging_disabled: true +mariadb_binary_logging_disabled: true +mariadb_keyserver_fingerprint: "0xcbcb082a1bb943db" +mariadb_mirror: nyc2.mirrors.digitalocean.com +mariadb_version: "10.0" +mariadb_dist: trusty +mysql_root_user: root + +sites_using_remote_db: "[{% for name, site in wordpress_sites.iteritems() if site.env is defined and site.env.db_host | default('localhost') != 'localhost' %}'{{ name }}',{% endfor %}]" diff --git a/roles/mariadb/tasks/main.yml b/roles/mariadb/tasks/main.yml index 71aaa7661e..a957171a0e 100644 --- a/roles/mariadb/tasks/main.yml +++ b/roles/mariadb/tasks/main.yml @@ -1,7 +1,7 @@ --- - name: Add MariaDB MySQL apt-key apt_key: - url: "http://keyserver.ubuntu.com/pks/lookup?op=get&fingerprint=on&search={{ keyserver_fingerprint }}" + url: "http://keyserver.ubuntu.com/pks/lookup?op=get&fingerprint=on&search={{ mariadb_keyserver_fingerprint }}" state: present - name: Add MariaDB MySQL deb and deb-src @@ -9,60 +9,68 @@ repo: "{{ item }}" state: present with_items: - - "deb http://{{ mirror }}/mariadb/repo/{{ version }}/ubuntu {{ mariadb_dist | default(ansible_distribution_release) }} main" - - "deb-src http://{{ mirror }}/mariadb/repo/{{ version }}/ubuntu {{ mariadb_dist | default(ansible_distribution_release) }} main" + - "deb http://{{ mariadb_mirror }}/mariadb/repo/{{ mariadb_version }}/ubuntu {{ mariadb_dist | default(ansible_distribution_release) }} main" + - "deb-src http://{{ mariadb_mirror }}/mariadb/repo/{{ mariadb_version }}/ubuntu {{ mariadb_dist | default(ansible_distribution_release) }} main" -- name: Install MariaDB MySQL server +- name: Install MySQL client apt: - name: mariadb-server + name: mariadb-client state: present -- name: Disable MariaDB binary logging - template: - src: disable-binary-logging.cnf - dest: /etc/mysql/conf.d - owner: root - group: root - when: binary_logging_disabled +- block: + - name: Install MariaDB MySQL server + apt: + name: mariadb-server + state: present -- name: Restart MariaDB MySQL Server - service: - name: mysql - state: restarted - enabled: true + - name: Disable MariaDB binary logging + template: + src: disable-binary-logging.cnf + dest: /etc/mysql/conf.d + owner: root + group: root + when: mariadb_binary_logging_disabled -- name: Set root user password - mysql_user: - name: root - host: "{{ item }}" - password: "{{ mysql_root_password }}" - check_implicit_admin: yes - state: present - with_items: - - "{{ inventory_hostname }}" - - 127.0.0.1 - - ::1 - - localhost + - name: Restart MariaDB MySQL Server + service: + name: mysql + state: restarted + enabled: true -- name: Copy .my.cnf file with root password credentials. - template: - src: my.cnf.j2 - dest: ~/.my.cnf - owner: root - group: root - mode: 0600 + - name: Set root user password + mysql_user: + name: root + host: "{{ item }}" + password: "{{ mysql_root_password }}" + check_implicit_admin: yes + state: present + with_items: + - "{{ inventory_hostname }}" + - 127.0.0.1 + - ::1 + - localhost -- name: Delete anonymous MySQL server users - mysql_user: - user: "" - host: "{{ item }}" - state: absent - with_items: - - localhost - - "{{ inventory_hostname }}" - - "{{ ansible_hostname }}" + - name: Copy .my.cnf file with root password credentials. + template: + src: my.cnf.j2 + dest: ~/.my.cnf + owner: root + group: root + mode: 0600 + + - name: Delete anonymous MySQL server users + mysql_user: + user: "" + host: "{{ item }}" + state: absent + with_items: + - localhost + - "{{ inventory_hostname }}" + - "{{ ansible_hostname }}" + + - name: Remove the test database + mysql_db: + name: test + state: absent -- name: Remove the test database - mysql_db: - name: test - state: absent + when: not sites_using_remote_db | count diff --git a/roles/wordpress-setup/defaults/main.yml b/roles/wordpress-setup/defaults/main.yml new file mode 100644 index 0000000000..dca1ba5052 --- /dev/null +++ b/roles/wordpress-setup/defaults/main.yml @@ -0,0 +1 @@ +site_uses_local_db: "{{ site_env.db_host == 'localhost' }}" diff --git a/roles/wordpress-setup/tasks/database.yml b/roles/wordpress-setup/tasks/database.yml index 8f8238e88b..63db5b5cbc 100644 --- a/roles/wordpress-setup/tasks/database.yml +++ b/roles/wordpress-setup/tasks/database.yml @@ -7,7 +7,7 @@ login_user: "{{ mysql_root_user }}" login_password: "{{ mysql_root_password }}" with_dict: "{{ wordpress_sites }}" - when: not mysql_remote_database and item.value.db_create | default(True) + when: site_uses_local_db and item.value.db_create | default(True) - name: Create/assign database user to db and grant permissions mysql_user: @@ -20,7 +20,7 @@ login_user: "{{ mysql_root_user }}" login_password: "{{ mysql_root_password }}" with_dict: "{{ wordpress_sites }}" - when: not mysql_remote_database and item.value.db_create | default(True) + when: site_uses_local_db and item.value.db_create | default(True) - name: Copy database dump copy: diff --git a/server.yml b/server.yml index c1dcecde9c..e422a67537 100644 --- a/server.yml +++ b/server.yml @@ -20,7 +20,7 @@ - { role: ntp, tags: [ntp] } - { role: users, tags: [users] } - { role: sshd, tags: [sshd] } - - { role: mariadb, tags: [mariadb], when: not mysql_remote_database } + - { role: mariadb, tags: [mariadb] } - { role: ssmtp, tags: [ssmtp, mail] } - { role: php, tags: [php] } - { role: memcached, tags: [memcached] } From 6fed8198f622d9f4e4d6ad349a1846894ee23384 Mon Sep 17 00:00:00 2001 From: Ben Word Date: Sat, 30 Apr 2016 15:55:11 -0400 Subject: [PATCH 47/52] Update to WP-CLI v0.23.1 --- CHANGELOG.md | 1 + roles/wp-cli/defaults/main.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 124fd9740f..70d627f9b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* Update to WP-CLI v0.23.1 ([#576](https://github.com/roots/trellis/pull/576)) * Fix #563 - Improve remote databases ([#573](https://github.com/roots/trellis/pull/573)) * Fix #569 - Only skip subdomains for non-www domains ([#570](https://github.com/roots/trellis/pull/570)) * Enable Let's Encrypt to transition http sites to https ([#565](https://github.com/roots/trellis/pull/565)) diff --git a/roles/wp-cli/defaults/main.yml b/roles/wp-cli/defaults/main.yml index 456dbaa2ed..60828899ac 100644 --- a/roles/wp-cli/defaults/main.yml +++ b/roles/wp-cli/defaults/main.yml @@ -1,4 +1,4 @@ wp_cli_bin_path: /usr/bin/wp -wp_cli_phar_url: "https://github.com/wp-cli/wp-cli/releases/download/v0.23.0/wp-cli-0.23.0.phar" +wp_cli_phar_url: "https://github.com/wp-cli/wp-cli/releases/download/v0.23.1/wp-cli-0.23.1.phar" wp_cli_completion_url: "https://raw.githubusercontent.com/wp-cli/wp-cli/master/utils/wp-completion.bash" wp_cli_completion_path: /etc/bash_completion.d From b2f2143c41d68a3623a3e7216182ca3902ef7854 Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Sat, 30 Apr 2016 16:08:09 -0400 Subject: [PATCH 48/52] Wrap my.cnf password in quotes Randomly generated passwords can contain certain characters (such as `=`) which cause problems when not within quotes. --- CHANGELOG.md | 1 + roles/mariadb/templates/disable-binary-logging.cnf | 2 ++ roles/mariadb/templates/my.cnf.j2 | 4 +++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70d627f9b0..1e4c3c9116 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* Wrap my.cnf password in quotes ([#577](https://github.com/roots/trellis/pull/577)) * Update to WP-CLI v0.23.1 ([#576](https://github.com/roots/trellis/pull/576)) * Fix #563 - Improve remote databases ([#573](https://github.com/roots/trellis/pull/573)) * Fix #569 - Only skip subdomains for non-www domains ([#570](https://github.com/roots/trellis/pull/570)) diff --git a/roles/mariadb/templates/disable-binary-logging.cnf b/roles/mariadb/templates/disable-binary-logging.cnf index f48416abcd..d427df9794 100644 --- a/roles/mariadb/templates/disable-binary-logging.cnf +++ b/roles/mariadb/templates/disable-binary-logging.cnf @@ -1,2 +1,4 @@ +# {{ ansible_managed }} + [mysqld] skip-log-bin diff --git a/roles/mariadb/templates/my.cnf.j2 b/roles/mariadb/templates/my.cnf.j2 index b63b4e63aa..68da533c8b 100644 --- a/roles/mariadb/templates/my.cnf.j2 +++ b/roles/mariadb/templates/my.cnf.j2 @@ -1,3 +1,5 @@ +# {{ ansible_managed }} + [client] user=root -password={{ mysql_root_password }} +password="{{ mysql_root_password }}" From e191527d28629bbd314fbf7fa7c807a544aaea94 Mon Sep 17 00:00:00 2001 From: Phil Nelson Date: Mon, 2 May 2016 00:26:20 -0700 Subject: [PATCH 49/52] Add connection-related cli options to ping command --- CHANGELOG.md | 1 + lib/trellis/plugins/vars/vars.py | 42 +++++++++++++++++++++++++------- roles/remote-user/tasks/main.yml | 6 ++--- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e4c3c9116..80b2ea6103 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* Add connection-related cli options to ping command ([#578](https://github.com/roots/trellis/pull/578)) * Wrap my.cnf password in quotes ([#577](https://github.com/roots/trellis/pull/577)) * Update to WP-CLI v0.23.1 ([#576](https://github.com/roots/trellis/pull/576)) * Fix #563 - Improve remote databases ([#573](https://github.com/roots/trellis/pull/573)) diff --git a/lib/trellis/plugins/vars/vars.py b/lib/trellis/plugins/vars/vars.py index 418699eb33..ca3814c78d 100644 --- a/lib/trellis/plugins/vars/vars.py +++ b/lib/trellis/plugins/vars/vars.py @@ -7,8 +7,9 @@ if __version__.startswith('1'): raise AnsibleError('Trellis no longer supports Ansible 1.x. Please upgrade to Ansible 2.x.') -# This import will produce Traceback in Ansible 1.x, so place after version check +# These imports will produce Traceback in Ansible 1.x, so place after version check from __main__ import cli +from ansible.compat.six import iteritems class VarsModule(object): @@ -28,15 +29,38 @@ def wrap_salts_in_raw(self, host, hostvars): hostvars['vault_wordpress_sites'][name]['env'][key] = ''.join(['{% raw %}', value, '{% endraw %}']) host.vars['vault_wordpress_sites'] = hostvars['vault_wordpress_sites'] - def cli_args_vault(self): - if self._options.ask_vault_pass: - return '--ask-vault-pass' - elif self._options.vault_password_file: - return '--vault-password-file {0}'.format(self._options.vault_password_file) - else: - return '' + def cli_options_ping(self): + options = [] + + remote_user = getattr(self._options, 'remote_user') + options.append("--user='{0}'".format(remote_user if remote_user else 'root')) + + strings = { + '--connection': 'connection', + '--inventory-file': 'inventory', + '--private-key': 'private_key_file', + '--ssh-common-args': 'ssh_common_args', + '--ssh-extra-args': 'ssh_extra_args', + '--timeout': 'timeout', + '--vault-password-file': 'vault_password_file', + } + + for option,value in strings.iteritems(): + if getattr(self._options, value, False): + options.append("{0}='{1}'".format(option, str(getattr(self._options, value)))) + + booleans = { + '--ask-pass': 'ask_pass', + '--ask-vault-pass': 'ask_vault_pass', + } + + for option,value in booleans.iteritems(): + if getattr(self._options, value, False): + options.append(option) + + return ' '.join(options) def get_host_vars(self, host, vault_password=None): self.wrap_salts_in_raw(host, host.get_group_vars()) - host.vars['cli_args_vault'] = self.cli_args_vault() + host.vars['cli_options_ping'] = self.cli_options_ping() return {} diff --git a/roles/remote-user/tasks/main.yml b/roles/remote-user/tasks/main.yml index 7d96e5e25c..619100e1ac 100644 --- a/roles/remote-user/tasks/main.yml +++ b/roles/remote-user/tasks/main.yml @@ -1,14 +1,14 @@ --- - name: Determine whether to connect as root or admin_user - local_action: command ansible {{ inventory_hostname }} -m ping{{ (inventory_file == None) | ternary('', ' -i ' + inventory_file | string) }} -u root {{ cli_args_vault | default('') }} + local_action: command ansible {{ inventory_hostname }} -m ping {{ cli_options_ping }} failed_when: false changed_when: false register: root_status - name: Set remote user for each host set_fact: - ansible_ssh_user: "{{ root_status | success | ternary('root', admin_user) }}" + ansible_user: "{{ root_status | success | ternary('root', admin_user) }}" - name: Announce which user was selected debug: - msg: "Note: Ansible will attempt connections as user = {{ ansible_ssh_user }}" + msg: "Note: Ansible will attempt connections as user = {{ ansible_user }}" From d0a9c467da26da0899a20eb7b53075925c260858 Mon Sep 17 00:00:00 2001 From: Phil Nelson Date: Fri, 6 May 2016 09:53:22 -0700 Subject: [PATCH 50/52] Pass cli options to user role's admin_user ping test --- lib/trellis/plugins/vars/vars.py | 3 --- roles/remote-user/tasks/main.yml | 4 ++-- roles/users/tasks/main.yml | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/trellis/plugins/vars/vars.py b/lib/trellis/plugins/vars/vars.py index ca3814c78d..6ebfdef178 100644 --- a/lib/trellis/plugins/vars/vars.py +++ b/lib/trellis/plugins/vars/vars.py @@ -32,9 +32,6 @@ def wrap_salts_in_raw(self, host, hostvars): def cli_options_ping(self): options = [] - remote_user = getattr(self._options, 'remote_user') - options.append("--user='{0}'".format(remote_user if remote_user else 'root')) - strings = { '--connection': 'connection', '--inventory-file': 'inventory', diff --git a/roles/remote-user/tasks/main.yml b/roles/remote-user/tasks/main.yml index 619100e1ac..ca651a81c8 100644 --- a/roles/remote-user/tasks/main.yml +++ b/roles/remote-user/tasks/main.yml @@ -1,13 +1,13 @@ --- - name: Determine whether to connect as root or admin_user - local_action: command ansible {{ inventory_hostname }} -m ping {{ cli_options_ping }} + local_action: command ansible {{ inventory_hostname }} -m ping -u {{ ansible_user | default('root') }} {{ cli_options_ping | default('') }} failed_when: false changed_when: false register: root_status - name: Set remote user for each host set_fact: - ansible_user: "{{ root_status | success | ternary('root', admin_user) }}" + ansible_user: "{{ root_status | success | ternary(ansible_user | default('root'), admin_user) }}" - name: Announce which user was selected debug: diff --git a/roles/users/tasks/main.yml b/roles/users/tasks/main.yml index 9745bbf28a..e6a7368176 100644 --- a/roles/users/tasks/main.yml +++ b/roles/users/tasks/main.yml @@ -51,7 +51,7 @@ - keys - name: Check whether Ansible can connect as admin_user - local_action: command ansible {{ inventory_hostname }} -m ping{{ (inventory_file == None) | ternary('', ' -i ' + inventory_file | string) }} -u {{ admin_user }} + local_action: command ansible {{ inventory_hostname }} -m ping -u {{ admin_user }} {{ cli_options_ping | default('') }} failed_when: false changed_when: false become: no From bcb513da245f1bd0b3c87271a7a865006028b41b Mon Sep 17 00:00:00 2001 From: Scott Walkinshaw Date: Wed, 4 May 2016 21:26:13 -0400 Subject: [PATCH 51/52] Require Ansible 2.0.2 and remove deploy_helper The `deploy_helper` module was vendored because we needed a fix which was not included in a stable Ansible yet. 2.0.2.0 includes the fix so we can bump our version requirements and un-vendor the module. --- CHANGELOG.md | 1 + README.md | 2 +- ansible.cfg | 1 - lib/trellis/modules/deploy_helper.py | 475 --------------------------- roles/common/defaults/main.yml | 2 +- 5 files changed, 3 insertions(+), 478 deletions(-) delete mode 100644 lib/trellis/modules/deploy_helper.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 80b2ea6103..ccdf2b892a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### HEAD +* Require Ansible 2.0.2 and remove deploy_helper ([#579](https://github.com/roots/trellis/pull/579)) * Add connection-related cli options to ping command ([#578](https://github.com/roots/trellis/pull/578)) * Wrap my.cnf password in quotes ([#577](https://github.com/roots/trellis/pull/577)) * Update to WP-CLI v0.23.1 ([#576](https://github.com/roots/trellis/pull/576)) diff --git a/README.md b/README.md index 631e21e44b..a4a96f7d2e 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Trellis will configure a server with the following and more: Make sure all dependencies have been installed before moving on: -* [Ansible](http://docs.ansible.com/ansible/intro_installation.html#latest-releases-via-pip) >= 2.0.0.2 +* [Ansible](http://docs.ansible.com/ansible/intro_installation.html#latest-releases-via-pip) >= 2.0.2 * [Virtualbox](https://www.virtualbox.org/wiki/Downloads) >= 4.3.10 * [Vagrant](http://www.vagrantup.com/downloads.html) >= 1.5.4 * [vagrant-bindfs](https://github.com/gael-ian/vagrant-bindfs#installation) >= 0.3.1 (Windows users may skip this) diff --git a/ansible.cfg b/ansible.cfg index ecf390629a..63876eae63 100644 --- a/ansible.cfg +++ b/ansible.cfg @@ -5,7 +5,6 @@ filter_plugins = ~/.ansible/plugins/filter_plugins/:/usr/share/ansible_plugins/f force_color = True force_handlers = True inventory = hosts -library = /usr/share/ansible:lib/trellis/modules nocows = 1 roles_path = vendor/roles vars_plugins = ~/.ansible/plugins/vars_plugins/:/usr/share/ansible_plugins/vars_plugins:lib/trellis/plugins/vars diff --git a/lib/trellis/modules/deploy_helper.py b/lib/trellis/modules/deploy_helper.py deleted file mode 100644 index ebf5b54a8b..0000000000 --- a/lib/trellis/modules/deploy_helper.py +++ /dev/null @@ -1,475 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2014, Jasper N. Brouwer -# (c) 2014, Ramon de la Fuente -# -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -DOCUMENTATION = ''' ---- -module: deploy_helper -version_added: "2.0" -author: "Ramon de la Fuente (@ramondelafuente)" -short_description: Manages some of the steps common in deploying projects. -description: - - The Deploy Helper manages some of the steps common in deploying software. - It creates a folder structure, manages a symlink for the current release - and cleans up old releases. - - "Running it with the C(state=query) or C(state=present) will return the C(deploy_helper) fact. - C(project_path), whatever you set in the path parameter, - C(current_path), the path to the symlink that points to the active release, - C(releases_path), the path to the folder to keep releases in, - C(shared_path), the path to the folder to keep shared resources in, - C(unfinished_filename), the file to check for to recognize unfinished builds, - C(previous_release), the release the 'current' symlink is pointing to, - C(previous_release_path), the full path to the 'current' symlink target, - C(new_release), either the 'release' parameter or a generated timestamp, - C(new_release_path), the path to the new release folder (not created by the module)." - -options: - path: - required: True - aliases: ['dest'] - description: - - the root path of the project. Alias I(dest). - Returned in the C(deploy_helper.project_path) fact. - - state: - required: False - choices: [ present, finalize, absent, clean, query ] - default: present - description: - - the state of the project. - C(query) will only gather facts, - C(present) will create the project I(root) folder, and in it the I(releases) and I(shared) folders, - C(finalize) will remove the unfinished_filename file, create a symlink to the newly - deployed release and optionally clean old releases, - C(clean) will remove failed & old releases, - C(absent) will remove the project folder (synonymous to the M(file) module with C(state=absent)) - - release: - required: False - default: None - description: - - the release version that is being deployed. Defaults to a timestamp format %Y%m%d%H%M%S (i.e. '20141119223359'). - This parameter is optional during C(state=present), but needs to be set explicitly for C(state=finalize). - You can use the generated fact C(release={{ deploy_helper.new_release }}). - - releases_path: - required: False - default: releases - description: - - the name of the folder that will hold the releases. This can be relative to C(path) or absolute. - Returned in the C(deploy_helper.releases_path) fact. - - shared_path: - required: False - default: shared - description: - - the name of the folder that will hold the shared resources. This can be relative to C(path) or absolute. - If this is set to an empty string, no shared folder will be created. - Returned in the C(deploy_helper.shared_path) fact. - - current_path: - required: False - default: current - description: - - the name of the symlink that is created when the deploy is finalized. Used in C(finalize) and C(clean). - Returned in the C(deploy_helper.current_path) fact. - - unfinished_filename: - required: False - default: DEPLOY_UNFINISHED - description: - - the name of the file that indicates a deploy has not finished. All folders in the releases_path that - contain this file will be deleted on C(state=finalize) with clean=True, or C(state=clean). This file is - automatically deleted from the I(new_release_path) during C(state=finalize). - - clean: - required: False - default: True - description: - - Whether to run the clean procedure in case of C(state=finalize). - - keep_releases: - required: False - default: 5 - description: - - the number of old releases to keep when cleaning. Used in C(finalize) and C(clean). Any unfinished builds - will be deleted first, so only correct releases will count. The current version will not count. - -notes: - - Facts are only returned for C(state=query) and C(state=present). If you use both, you should pass any overridden - parameters to both calls, otherwise the second call will overwrite the facts of the first one. - - When using C(state=clean), the releases are ordered by I(creation date). You should be able to switch to a - new naming strategy without problems. - - Because of the default behaviour of generating the I(new_release) fact, this module will not be idempotent - unless you pass your own release name with C(release). Due to the nature of deploying software, this should not - be much of a problem. -''' - -EXAMPLES = ''' - -# General explanation, starting with an example folder structure for a project: - -root: - releases: - - 20140415234508 - - 20140415235146 - - 20140416082818 - - shared: - - sessions - - uploads - - current: -> releases/20140416082818 - - -The 'releases' folder holds all the available releases. A release is a complete build of the application being -deployed. This can be a clone of a repository for example, or a sync of a local folder on your filesystem. -Having timestamped folders is one way of having distinct releases, but you could choose your own strategy like -git tags or commit hashes. - -During a deploy, a new folder should be created in the releases folder and any build steps required should be -performed. Once the new build is ready, the deploy procedure is 'finalized' by replacing the 'current' symlink -with a link to this build. - -The 'shared' folder holds any resource that is shared between releases. Examples of this are web-server -session files, or files uploaded by users of your application. It's quite common to have symlinks from a release -folder pointing to a shared/subfolder, and creating these links would be automated as part of the build steps. - -The 'current' symlink points to one of the releases. Probably the latest one, unless a deploy is in progress. -The web-server's root for the project will go through this symlink, so the 'downtime' when switching to a new -release is reduced to the time it takes to switch the link. - -To distinguish between successful builds and unfinished ones, a file can be placed in the folder of the release -that is currently in progress. The existence of this file will mark it as unfinished, and allow an automated -procedure to remove it during cleanup. - - -# Typical usage: -- name: Initialize the deploy root and gather facts - deploy_helper: path=/path/to/root -- name: Clone the project to the new release folder - git: repo=git://foosball.example.org/path/to/repo.git dest={{ deploy_helper.new_release_path }} version=v1.1.1 -- name: Add an unfinished file, to allow cleanup on successful finalize - file: path={{ deploy_helper.new_release_path }}/{{ deploy_helper.unfinished_filename }} state=touch -- name: Perform some build steps, like running your dependency manager for example - composer: command=install working_dir={{ deploy_helper.new_release_path }} -- name: Create some folders in the shared folder - file: path='{{ deploy_helper.shared_path }}/{{ item }}' state=directory - with_items: ['sessions', 'uploads'] -- name: Add symlinks from the new release to the shared folder - file: path='{{ deploy_helper.new_release_path }}/{{ item.path }}' - src='{{ deploy_helper.shared_path }}/{{ item.src }}' - state=link - with_items: - - { path: "app/sessions", src: "sessions" } - - { path: "web/uploads", src: "uploads" } -- name: Finalize the deploy, removing the unfinished file and switching the symlink - deploy_helper: path=/path/to/root release={{ deploy_helper.new_release }} state=finalize - -# Retrieving facts before running a deploy -- name: Run 'state=query' to gather facts without changing anything - deploy_helper: path=/path/to/root state=query -# Remember to set the 'release' parameter when you actually call 'state=present' later -- name: Initialize the deploy root - deploy_helper: path=/path/to/root release={{ deploy_helper.new_release }} state=present - -# all paths can be absolute or relative (to the 'path' parameter) -- deploy_helper: path=/path/to/root - releases_path=/var/www/project/releases - shared_path=/var/www/shared - current_path=/var/www/active - -# Using your own naming strategy for releases (a version tag in this case): -- deploy_helper: path=/path/to/root release=v1.1.1 state=present -- deploy_helper: path=/path/to/root release={{ deploy_helper.new_release }} state=finalize - -# Using a different unfinished_filename: -- deploy_helper: path=/path/to/root - unfinished_filename=README.md - release={{ deploy_helper.new_release }} - state=finalize - -# Postponing the cleanup of older builds: -- deploy_helper: path=/path/to/root release={{ deploy_helper.new_release }} state=finalize clean=False -- deploy_helper: path=/path/to/root state=clean -# Or running the cleanup ahead of the new deploy -- deploy_helper: path=/path/to/root state=clean -- deploy_helper: path=/path/to/root state=present - -# Keeping more old releases: -- deploy_helper: path=/path/to/root release={{ deploy_helper.new_release }} state=finalize keep_releases=10 -# Or, if you use 'clean=false' on finalize: -- deploy_helper: path=/path/to/root state=clean keep_releases=10 - -# Removing the entire project root folder -- deploy_helper: path=/path/to/root state=absent - -# Debugging the facts returned by the module -- deploy_helper: path=/path/to/root -- debug: var=deploy_helper - -''' - -class DeployHelper(object): - - def __init__(self, module): - module.params['path'] = os.path.expanduser(module.params['path']) - - self.module = module - self.file_args = module.load_file_common_arguments(module.params) - - self.clean = module.params['clean'] - self.current_path = module.params['current_path'] - self.keep_releases = module.params['keep_releases'] - self.path = module.params['path'] - self.release = module.params['release'] - self.releases_path = module.params['releases_path'] - self.shared_path = module.params['shared_path'] - self.state = module.params['state'] - self.unfinished_filename = module.params['unfinished_filename'] - - def gather_facts(self): - current_path = os.path.join(self.path, self.current_path) - releases_path = os.path.join(self.path, self.releases_path) - if self.shared_path: - shared_path = os.path.join(self.path, self.shared_path) - else: - shared_path = None - - previous_release, previous_release_path = self._get_last_release(current_path) - - if not self.release and (self.state == 'query' or self.state == 'present'): - self.release = time.strftime("%Y%m%d%H%M%S") - - new_release_path = os.path.join(releases_path, self.release) - - return { - 'project_path': self.path, - 'current_path': current_path, - 'releases_path': releases_path, - 'shared_path': shared_path, - 'previous_release': previous_release, - 'previous_release_path': previous_release_path, - 'new_release': self.release, - 'new_release_path': new_release_path, - 'unfinished_filename': self.unfinished_filename - } - - def delete_path(self, path): - if not os.path.lexists(path): - return False - - if not os.path.isdir(path): - self.module.fail_json(msg="%s exists but is not a directory" % path) - - if not self.module.check_mode: - try: - shutil.rmtree(path, ignore_errors=False) - except Exception, e: - self.module.fail_json(msg="rmtree failed: %s" % str(e)) - - return True - - def create_path(self, path): - changed = False - - if not os.path.lexists(path): - changed = True - if not self.module.check_mode: - os.makedirs(path) - - elif not os.path.isdir(path): - self.module.fail_json(msg="%s exists but is not a directory" % path) - - changed += self.module.set_directory_attributes_if_different(self._get_file_args(path), changed) - - return changed - - def check_link(self, path): - if os.path.lexists(path): - if not os.path.islink(path): - self.module.fail_json(msg="%s exists but is not a symbolic link" % path) - - def create_link(self, source, link_name): - changed = False - - if os.path.islink(link_name): - norm_link = os.path.normpath(os.path.realpath(link_name)) - norm_source = os.path.normpath(os.path.realpath(source)) - if norm_link == norm_source: - changed = False - else: - changed = True - if not self.module.check_mode: - if not os.path.lexists(source): - self.module.fail_json(msg="the symlink target %s doesn't exists" % source) - tmp_link_name = link_name + '.' + self.unfinished_filename - if os.path.islink(tmp_link_name): - os.unlink(tmp_link_name) - os.symlink(source, tmp_link_name) - os.rename(tmp_link_name, link_name) - else: - changed = True - if not self.module.check_mode: - os.symlink(source, link_name) - - return changed - - def remove_unfinished_file(self, new_release_path): - changed = False - unfinished_file_path = os.path.join(new_release_path, self.unfinished_filename) - if os.path.lexists(unfinished_file_path): - changed = True - if not self.module.check_mode: - os.remove(unfinished_file_path) - - return changed - - def remove_unfinished_builds(self, releases_path): - changes = 0 - - for release in os.listdir(releases_path): - if os.path.isfile(os.path.join(releases_path, release, self.unfinished_filename)): - if self.module.check_mode: - changes += 1 - else: - changes += self.delete_path(os.path.join(releases_path, release)) - - return changes - - def remove_unfinished_link(self, path): - changed = False - - tmp_link_name = os.path.join(path, self.release + '.' + self.unfinished_filename) - if not self.module.check_mode and os.path.exists(tmp_link_name): - changed = True - os.remove(tmp_link_name) - - return changed - - def cleanup(self, releases_path, reserve_version): - changes = 0 - - if os.path.lexists(releases_path): - releases = [ f for f in os.listdir(releases_path) if os.path.isdir(os.path.join(releases_path,f)) ] - try: - releases.remove(reserve_version) - except ValueError: - pass - - if not self.module.check_mode: - releases.sort( key=lambda x: os.path.getctime(os.path.join(releases_path,x)), reverse=True) - for release in releases[self.keep_releases:]: - changes += self.delete_path(os.path.join(releases_path, release)) - elif len(releases) > self.keep_releases: - changes += (len(releases) - self.keep_releases) - - return changes - - def _get_file_args(self, path): - file_args = self.file_args.copy() - file_args['path'] = path - return file_args - - def _get_last_release(self, current_path): - previous_release = None - previous_release_path = None - - if os.path.lexists(current_path): - previous_release_path = os.path.realpath(current_path) - previous_release = os.path.basename(previous_release_path) - - return previous_release, previous_release_path - -def main(): - - module = AnsibleModule( - argument_spec = dict( - path = dict(aliases=['dest'], required=True, type='str'), - release = dict(required=False, type='str', default=None), - releases_path = dict(required=False, type='str', default='releases'), - shared_path = dict(required=False, type='str', default='shared'), - current_path = dict(required=False, type='str', default='current'), - keep_releases = dict(required=False, type='int', default=5), - clean = dict(required=False, type='bool', default=True), - unfinished_filename = dict(required=False, type='str', default='DEPLOY_UNFINISHED'), - state = dict(required=False, choices=['present', 'absent', 'clean', 'finalize', 'query'], default='present') - ), - add_file_common_args = True, - supports_check_mode = True - ) - - deploy_helper = DeployHelper(module) - facts = deploy_helper.gather_facts() - - result = { - 'state': deploy_helper.state - } - - changes = 0 - - if deploy_helper.state == 'query': - result['ansible_facts'] = { 'deploy_helper': facts } - - elif deploy_helper.state == 'present': - deploy_helper.check_link(facts['current_path']) - changes += deploy_helper.create_path(facts['project_path']) - changes += deploy_helper.create_path(facts['releases_path']) - if deploy_helper.shared_path: - changes += deploy_helper.create_path(facts['shared_path']) - - result['ansible_facts'] = { 'deploy_helper': facts } - - elif deploy_helper.state == 'finalize': - if not deploy_helper.release: - module.fail_json(msg="'release' is a required parameter for state=finalize (try the 'deploy_helper.new_release' fact)") - if deploy_helper.keep_releases <= 0: - module.fail_json(msg="'keep_releases' should be at least 1") - - changes += deploy_helper.remove_unfinished_file(facts['new_release_path']) - changes += deploy_helper.create_link(facts['new_release_path'], facts['current_path']) - if deploy_helper.clean: - changes += deploy_helper.remove_unfinished_link(facts['project_path']) - changes += deploy_helper.remove_unfinished_builds(facts['releases_path']) - changes += deploy_helper.cleanup(facts['releases_path'], facts['new_release']) - - elif deploy_helper.state == 'clean': - changes += deploy_helper.remove_unfinished_link(facts['project_path']) - changes += deploy_helper.remove_unfinished_builds(facts['releases_path']) - changes += deploy_helper.cleanup(facts['releases_path'], facts['new_release']) - - elif deploy_helper.state == 'absent': - # destroy the facts - result['ansible_facts'] = { 'deploy_helper': [] } - changes += deploy_helper.delete_path(facts['project_path']) - - if changes > 0: - result['changed'] = True - else: - result['changed'] = False - - module.exit_json(**result) - - -# import module snippets -from ansible.module_utils.basic import * - -if __name__ == '__main__': - main() diff --git a/roles/common/defaults/main.yml b/roles/common/defaults/main.yml index a3bec1c2e6..2f60766019 100644 --- a/roles/common/defaults/main.yml +++ b/roles/common/defaults/main.yml @@ -1,2 +1,2 @@ -minimum_ansible_version: 2.0.0.2 +minimum_ansible_version: 2.0.2.0 default_timezone: Etc/UTC From f1c4fb72009906244f512e82bb02c2f9435dbed0 Mon Sep 17 00:00:00 2001 From: Boston Dell-Vandenberg Date: Sat, 14 May 2016 13:47:06 -0400 Subject: [PATCH 52/52] Add Nginx variable to set fastcgi_buffer_size (#586) --- roles/nginx/defaults/main.yml | 1 + roles/nginx/templates/nginx.conf.j2 | 1 + 2 files changed, 2 insertions(+) diff --git a/roles/nginx/defaults/main.yml b/roles/nginx/defaults/main.yml index d0d01b30a4..dbfdacd430 100644 --- a/roles/nginx/defaults/main.yml +++ b/roles/nginx/defaults/main.yml @@ -3,6 +3,7 @@ nginx_path: /etc/nginx nginx_logs_root: /var/log/nginx nginx_user: www-data nginx_fastcgi_buffers: 8 8k +nginx_fastcgi_buffer_size: 4k nginx_ssl_path: "{{ nginx_path }}/ssl" # HSTS defaults diff --git a/roles/nginx/templates/nginx.conf.j2 b/roles/nginx/templates/nginx.conf.j2 index e50b250549..474c916497 100644 --- a/roles/nginx/templates/nginx.conf.j2 +++ b/roles/nginx/templates/nginx.conf.j2 @@ -37,6 +37,7 @@ http { # Setup the fastcgi cache. fastcgi_buffers {{ nginx_fastcgi_buffers }}; + fastcgi_buffer_size {{ nginx_fastcgi_buffer_size }}; fastcgi_cache_path {{ nginx_cache_path }} levels=1:2 keys_zone=wordpress:{{ nginx_cache_key_storage_size }} max_size={{ nginx_cache_size }} inactive={{ nginx_cache_inactive }}; fastcgi_cache_use_stale updating error timeout invalid_header http_500; fastcgi_cache_lock on;