├── .editorconfig ├── .gitignore ├── README.md ├── group_vars └── all ├── hosts.example ├── roles ├── backup │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── templates │ │ ├── awscli_config.j2 │ │ └── site-backup ├── common │ ├── handlers │ │ └── main.yml │ └── tasks │ │ └── main.yml ├── letsencrypt │ ├── handlers │ │ └── main.yml │ └── tasks │ │ └── main.yml ├── mariadb │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── templates │ │ └── .my.cnf ├── memcached │ ├── handlers │ │ └── main.yml │ └── tasks │ │ └── main.yml ├── newrelic │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── templates │ │ ├── newrelic.ini │ │ └── nrsysmond.cfg ├── nginx │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── templates │ │ ├── logrotate │ │ ├── nginx.conf │ │ ├── wordpress │ │ └── wordpress-ssl ├── php-fpm │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── templates │ │ └── wordpress.conf ├── phpmyadmin │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── templates │ │ └── phpmyadmin ├── postfix │ ├── handlers │ │ └── main.yml │ └── tasks │ │ └── main.yml ├── redis │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── templates │ │ └── redis.conf ├── sftp │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── templates │ │ └── sshd_config ├── ufw │ └── tasks │ │ └── main.yml ├── webgrind │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── templates │ │ ├── config.php │ │ └── webgrind └── wordpress │ ├── tasks │ └── main.yml │ └── templates │ ├── advanced-cache.php │ ├── object-cache.php │ └── wp-config.php └── site.yml /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | hosts -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WordPress Ansible Playbook 2 | 3 | - Requires Ansible 1.2 or newer 4 | - Expects Ubuntu 14.04 5 | 6 | This playbook deploys an optimized setup for WordPress using MariaDB, nginx, PHP FPM, and Memcached. 7 | 8 | ## Optional Roles 9 | 10 | * Backup - This role will backup your files and database to Amazon S3 11 | * Letsencrypt - This role is a work in progress. It setups LetsEncrypt for generating SSL certificates. 12 | * New Relic - This role sets up PHP and server apps within New Relic. 13 | * SFTP - This role creates a default SFTP user and sets it's home directory to be the web root. 14 | 15 | ## Example Hosts file 16 | 17 | `hosts.example` contains an example host. Just substitute your information, and you should be good to go. 18 | -------------------------------------------------------------------------------- /group_vars/all: -------------------------------------------------------------------------------- 1 | --- 2 | wp_version: 4.4 3 | 4 | wp_db_name: wordpress 5 | wp_db_user: wordpress 6 | wp_db_password: secret 7 | 8 | mysql_port: 3306 9 | 10 | server_hostname: example.com 11 | dev_server_hostname: dev.example.com 12 | remote_user: root 13 | admin_email: email@email.com 14 | 15 | awscli_user: ubuntu 16 | awscli_group: ubuntu 17 | awscli_bucket: backups 18 | 19 | timezone: America/New_York 20 | 21 | newrelic_loglevel: info 22 | newrelic_logfile: /var/log/newrelic/nrsysmond.log 23 | newrelic_proxy: False 24 | newrelic_ssl: "false" 25 | newrelic_ssl_ca_bundle: False 26 | newrelic_ssl_ca_path: False 27 | newrelic_pidfile: /var/run/newrelic/nrsysmond.pid 28 | newrelic_collector_host: collector.newrelic.com 29 | newrelic_timeout: 30 30 | newrelic_license_key: XXXXXXXXXXXXX 31 | newrelic_appname: example 32 | -------------------------------------------------------------------------------- /hosts.example: -------------------------------------------------------------------------------- 1 | [mysite] 2 | mysite.com 3 | 4 | [mysite:vars] 5 | server_hostname=mysite.com 6 | remote_user=ubuntu 7 | dev_server_hostname=dev.mysite.com 8 | wp_db_password=XXXXXXXXXXXXXXXXXX 9 | awscli_aws_access_key_id=XXXXXXXXXXXXXXXXXX 10 | awscli_aws_secret_access_key=XXXXXXXXXXXXXXXXXX 11 | awscli_region=us-west-2 12 | -------------------------------------------------------------------------------- /roles/backup/handlers/main.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlovett1/wordpress-ansible-playbook/f4a5fd158b346aac830c72744a10b26807c53496/roles/backup/handlers/main.yml -------------------------------------------------------------------------------- /roles/backup/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install awscli 3 | pip: name=awscli state=latest 4 | sudo: yes 5 | 6 | - name: Create .aws dir under user home for awscli config 7 | file: 8 | path=/home/{{ awscli_user }}/.aws 9 | state=directory 10 | owner={{ awscli_user }} 11 | group={{ awscli_group }} 12 | mode=0755 13 | 14 | 15 | - name: Copy awscli config to vm using templates 16 | template: 17 | src=awscli_config.j2 18 | dest=/home/{{ awscli_user }}/.aws/config 19 | owner={{ awscli_user }} 20 | group={{ awscli_group }} 21 | mode=0644 22 | 23 | - name: Copy backup script to bin 24 | template: 25 | src="site-backup" 26 | dest="/usr/local/bin/site-backup" 27 | owner={{ awscli_user }} 28 | group={{ awscli_group }} 29 | mode=0700 30 | 31 | - name: Add line to crontab for backup 32 | cron: name="Site Backup" minute="0" hour="3" job="bash /usr/local/bin/site-backup > ~/backup" user={{ awscli_user }} 33 | -------------------------------------------------------------------------------- /roles/backup/templates/awscli_config.j2: -------------------------------------------------------------------------------- 1 | [default] 2 | aws_access_key_id = {{ awscli_aws_access_key_id }} 3 | aws_secret_access_key = {{ awscli_aws_secret_access_key }} 4 | 5 | region = {{ awscli_region }} -------------------------------------------------------------------------------- /roles/backup/templates/site-backup: -------------------------------------------------------------------------------- 1 | AWS_CONFIG_FILE=/home/ubuntu/.aws/config 2 | 3 | rm /tmp/db-backup-*.sql 4 | rm /tmp/files-backup-*.tar.gz 5 | 6 | DOW=$(date +%u) 7 | 8 | /usr/local/bin/wp --allow-root --path=/var/www/html/wordpress db export "/tmp/db-backup-$DOW.sql" 9 | 10 | tar -zcvf "/tmp/files-backup-$DOW.tar.gz" /var/www/html/wordpress 11 | 12 | /usr/local/bin/aws s3 cp "/tmp/db-backup-$DOW.sql" s3://{{ aws_bucket }}/{{ server_hostname }}/ 13 | 14 | /usr/local/bin/aws s3 cp "/tmp/files-backup-$DOW.tar.gz" s3://{{ aws_bucket }}/{{ server_hostname }}/ 15 | 16 | rm /tmp/db-backup-*.sql 17 | rm /tmp/files-backup-*.tar.gz 18 | 19 | echo "Files and database backup" 20 | -------------------------------------------------------------------------------- /roles/common/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: update timezone 3 | command: dpkg-reconfigure --frontend noninteractive tzdata -------------------------------------------------------------------------------- /roles/common/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: apt-get update hack 3 | shell: apt-get update 4 | 5 | - name: Install utilities 6 | apt: name={{item}} state=latest 7 | ignore_errors: yes 8 | with_items: 9 | - vim 10 | - git 11 | - subversion 12 | - unzip 13 | - python-pip 14 | - python-dev 15 | - build-essential 16 | 17 | - name: Set timezone variables 18 | copy: content="{{ timezone }}" 19 | dest=/etc/timezone 20 | owner=root 21 | group=root 22 | mode=0644 23 | backup=yes 24 | notify: 25 | - update timezone 26 | -------------------------------------------------------------------------------- /roles/letsencrypt/handlers/main.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlovett1/wordpress-ansible-playbook/f4a5fd158b346aac830c72744a10b26807c53496/roles/letsencrypt/handlers/main.yml -------------------------------------------------------------------------------- /roles/letsencrypt/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Download and install certbot 3 | get_url: url=https://dl.eff.org/certbot-auto dest=/usr/local/bin/certbot-auto mode=0770 4 | 5 | - name: Add line to crontab for auto renewal 6 | cron: name="cert renew" minute="0" hour="*/12" job="/usr/local/bin/certbot-auto renew" user=root -------------------------------------------------------------------------------- /roles/mariadb/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart mariadb 3 | service: name=mysql state=restarted 4 | -------------------------------------------------------------------------------- /roles/mariadb/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install MariaDB package key 3 | apt_key: keyserver=keyserver.ubuntu.com id=0xcbcb082a1bb943db 4 | 5 | - name: Add MariaDB source 6 | apt_repository: repo='deb [arch=amd64,i386,ppc64el] http://mirror.klaus-uwe.me/mariadb/repo/10.1/ubuntu trusty main' state=present 7 | 8 | - name: Install MariaDB package 9 | apt: name={{item}} state=latest 10 | with_items: 11 | - mariadb-server 12 | - python-mysqldb 13 | 14 | - name: Start MariaDB Service 15 | service: name=mysql state=started enabled=yes 16 | 17 | # localhost needs to be the last item for idempotency 18 | - name: Update mysql root password for all root accounts 19 | mysql_user: name=root host={{ item }} password={{ mysql_root_password }} 20 | with_items: 21 | - "{{ ansible_hostname }}" 22 | - 127.0.0.1 23 | - ::1 24 | - localhost 25 | 26 | - name: Copy .my.cnf file with root password credentials 27 | template: src=.my.cnf dest=/root/.my.cnf owner=root mode=0600 28 | 29 | - name: Delete anonymous MySQL server user for $server_hostname 30 | action: mysql_user user="" host="{{ server_hostname }}" state="absent" 31 | 32 | - name: Delete anonymous MySQL server user for localhost 33 | action: mysql_user user="" state="absent" 34 | 35 | - name: Remove the MySQL test database 36 | action: mysql_db db=test state=absent 37 | 38 | - name: Download MySQL Tuner 39 | get_url: url=https://raw.githubusercontent.com/major/MySQLTuner-perl/master/mysqltuner.pl dest=/home/{{ remote_user }} 40 | -------------------------------------------------------------------------------- /roles/mariadb/templates/.my.cnf: -------------------------------------------------------------------------------- 1 | [client] 2 | user=root 3 | password={{ mysql_root_password }} -------------------------------------------------------------------------------- /roles/memcached/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart memcached 3 | service: name=memcached state=restarted 4 | notify: restart php5-fpm -------------------------------------------------------------------------------- /roles/memcached/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install memcached 3 | apt: name=memcached state=latest 4 | 5 | - name: Install php memcache 6 | apt: name=php5-memcache state=latest 7 | notify: restart php5-fpm -------------------------------------------------------------------------------- /roles/newrelic/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart new relic 3 | service: 4 | name: newrelic-sysmond 5 | state: restarted 6 | 7 | - name: run new relic installer 8 | shell: NR_INSTALL_SILENT=true newrelic-install install 9 | notify: 10 | - restart php-fpm 11 | - restart nginx 12 | 13 | - name: restart php-fpm 14 | shell: service php7.0-fpm restart 15 | 16 | - name: restart nginx 17 | service: name=nginx state=restarted enabled=yes 18 | 19 | -------------------------------------------------------------------------------- /roles/newrelic/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Set newrelic_loaded_os_family 3 | set_fact: 4 | newrelic_loaded_os_family: "{{ ansible_os_family }}" 5 | 6 | - name: Add New Relic repo key 7 | apt_key: 8 | url: https://download.newrelic.com/548C16BF.gpg 9 | id: 548C16BF 10 | state: present 11 | 12 | - name: Add New Relic repository 13 | copy: 14 | content: "deb http://apt.newrelic.com/debian/ newrelic non-free" 15 | dest: /etc/apt/sources.list.d/newrelic.list 16 | owner: root 17 | group: root 18 | mode: 0644 19 | register: newrelic_repo 20 | 21 | - name: apt-get update 22 | apt: 23 | update_cache: yes 24 | when: newrelic_repo.changed 25 | 26 | - name: Install New Relic Sysmond 27 | apt: 28 | pkg: newrelic-sysmond 29 | update_cache: yes 30 | cache_valid_time: 86400 31 | state: latest 32 | notify: 33 | - restart new relic 34 | 35 | 36 | - name: Configure New Relic Sysmond 37 | template: 38 | src: nrsysmond.cfg 39 | dest: /etc/newrelic/nrsysmond.cfg 40 | group: newrelic 41 | owner: root 42 | mode: 0640 43 | notify: 44 | - restart new relic 45 | 46 | - name: Ensure New Relic Sysmond is started and enabled 47 | service: 48 | name: newrelic-sysmond 49 | state: started 50 | enabled: yes 51 | 52 | # PHP time 53 | 54 | - name: Install New Relic PHP Agent 55 | apt: 56 | pkg: newrelic-php5 57 | state: latest 58 | notify: 59 | - run new relic installer 60 | 61 | - name: Configure New Relic PHP Agent 62 | template: 63 | src: newrelic.ini 64 | dest: "/etc/php/7.0/fpm/conf.d/newrelic.ini" 65 | group: root 66 | owner: root 67 | mode: 0640 68 | notify: 69 | - run new relic installer 70 | 71 | -------------------------------------------------------------------------------- /roles/newrelic/templates/newrelic.ini: -------------------------------------------------------------------------------- 1 | ; 2 | ; This file contains the various settings for the New Relic PHP agent. There 3 | ; are many options, all of which are described in detail at the following URL: 4 | ; https://newrelic.com/docs/php/php-agent-phpini-settings 5 | ; 6 | 7 | ; If you use a full path to the extension you insulate yourself from the 8 | ; extension directory changing if you change PHP installations or versions. 9 | ; If you do not use an absolute path then the file must be installed in the 10 | ; active configuration's extension directory. 11 | extension = "newrelic.so" 12 | 13 | [newrelic] 14 | ; 15 | ; Setting: newrelic.enabled 16 | ; Type : boolean 17 | ; Scope : per-directory 18 | ; Default: true 19 | ; Info : Enable or disable the agent. Please note that you cannot globally 20 | ; disable the agent and then selectively enable it on a per-directory 21 | ; basis. If you disable the agent in the global INI file then the 22 | ; agent will not initialize at all. However, you can selectively 23 | ; disable the agent on a per-directory basis. 24 | ; 25 | ;newrelic.enabled = true 26 | 27 | ; 28 | ; Setting: newrelic.license 29 | ; Type : string 30 | ; Scope : per-directory 31 | ; Default: none 32 | ; Info : Sets the New Relic license key to use. This can vary from directory 33 | ; to directory if you are running a multi-tenant system. By special 34 | ; dispensation if you upgraded from a previous version of the agent 35 | ; where the license key was set in the daemon, the installation and 36 | ; upgrade script will have preserved your license key from the file 37 | ; /etc/newrelic/newrelic.cfg, but ONLY if you installed via rpm/yum 38 | ; or dpkg. The key is saved in /etc/newrelic/upgrade_please.key 39 | ; and the agent will look for that file if you do not specify a valid 40 | ; license here. 41 | ; It is *STRONGLY* recommended that you set the license key in your 42 | ; INI file(s) and do not rely on the key file being present. Also 43 | ; please note that even if you are not letting the agent start the 44 | ; daemon and are still using newrelic.cfg (see below) the license 45 | ; keyword in that file is no longer obeyed. Instead the agent will 46 | ; use the preserved value of that license from the key file. 47 | ; Once you have updated your INI files to contain the license we 48 | ; urge you to remove /etc/newrelic/upgrade_please.key in order to 49 | ; eliminate the potential for confusion about exactly where the key 50 | ; is coming from. 51 | ; 52 | newrelic.license = "{{ newrelic_license_key }}" 53 | 54 | ; 55 | ; Setting: newrelic.logfile 56 | ; Type : string 57 | ; Scope : system 58 | ; Default: none 59 | ; Info : Sets the name of the file to send log messages to. 60 | ; 61 | newrelic.logfile = "/var/log/newrelic/php_agent.log" 62 | 63 | ; 64 | ; Setting: newrelic.loglevel 65 | ; Type : string 66 | ; Scope : system 67 | ; Default: "info" 68 | ; Info : Sets the level of detail to include in the log file. You should 69 | ; rarely need to change this from the default, and usually only under 70 | ; the guidance of technical support. 71 | ; Must be one of the following values: 72 | ; always, error, warning, info, verbose, debug, verbosedebug 73 | ; 74 | ;newrelic.loglevel = "info" 75 | 76 | ; 77 | ; Setting: newrelic.appname 78 | ; Type : string 79 | ; Scope : per-directory 80 | ; Default: "PHP Application" 81 | ; Info : Sets the name of the application that metrics will be reported into. 82 | ; This can in fact be a list of up to 3 application names, each of 83 | ; which must be separated by a semi-colon. The first name in any such 84 | ; list is considered the 'primary' application name and must be unique 85 | ; for each account / license key. 86 | ; 87 | newrelic.appname = "{{ newrelic_appname }}" 88 | 89 | ; 90 | ; Beginning with version 3.0 of the agent, the daemon can be automatically 91 | ; started by the agent. There is no need to start the daemon before starting 92 | ; Apache or PHP-FPM. All of the newrelic.daemon.* settings are options that 93 | ; control the behavior of the daemon. These settings are converted into the 94 | ; appropriate command line options when the agent starts the daemon. This is 95 | ; now the preferred method of starting the daemon. There are still usage cases 96 | ; (such as using a single daemon for serving multiple Apache instances) where 97 | ; you may want to start the daemon via it's init script, but for most users, 98 | ; this is the best place to configure and start the daemon. 99 | ; 100 | ; The agent will only launch the daemon if one isn't already running. Also 101 | ; note that the agent will NOT stop the daemon once it has started. If you 102 | ; want control over exactly when the daemon starts and stops you can still 103 | ; achieve that by creating a daemon configuration file (located by default at 104 | ; /etc/newrelic/newrelic.cfg) and running the chkconfig or equivalent command. 105 | ; Please see the newrelic.cfg template file for details. That template file 106 | ; is located at /usr/lib/newrelic-php5/scripts/newrelic.cfg.template. 107 | ; 108 | ; Also please note that the options here and in newrelic.cfg are identical, 109 | ; except that in this file they are preceded with "newrelic.daemon.". 110 | ; 111 | 112 | ; 113 | ; Setting: newrelic.daemon.logfile 114 | ; Type : string 115 | ; Scope : system 116 | ; Default: none 117 | ; Info : Sets the name of the file to send daemon log messages to. 118 | ; 119 | newrelic.daemon.logfile = "/var/log/newrelic/newrelic-daemon.log" 120 | 121 | ; 122 | ; Setting: newrelic.daemon.loglevel 123 | ; Type : string 124 | ; Scope : system 125 | ; Default: "info" 126 | ; Info : Sets the level of detail to include in the daemon log. You should 127 | ; rarely need to change this from the default, and usually only under 128 | ; the guidance of technical support. 129 | ; Must be one of the following values: 130 | ; always, error, warning, info, verbose, debug, verbosedebug 131 | ; 132 | ;newrelic.daemon.loglevel = "info" 133 | 134 | ; 135 | ; Setting: newrelic.daemon.port 136 | ; Type : string or integer 137 | ; Scope : system 138 | ; Default: /tmp/.newrelic.sock 139 | ; Info : Sets how the agent and daemon communicate. How this is set can impact 140 | ; performance. The default is to use a UNIX-domain socket located at 141 | ; /tmp/.newrelic.sock. If you want to use UNIX domain sockets then 142 | ; this value must begin with a "/". If you set this to an integer 143 | ; value in the range 1-65534, then this will instruct the agent to 144 | ; use a normal TCP socket on the port specified. This may be easier 145 | ; to use if you are using a chroot environment. 146 | ; 147 | ;newrelic.daemon.port = "/tmp/.newrelic.sock" 148 | 149 | ; 150 | ; Setting: newrelic.daemon.ssl 151 | ; Type : boolean 152 | ; Scope : system 153 | ; Default: true (as of version 3.6) 154 | ; Info : Sets whether or not communication with New Relic data collectors 155 | ; should use a secure HTTP connection or not. 156 | ; 157 | ;newrelic.daemon.ssl = true 158 | 159 | ; 160 | ; Setting: newrelic.daemon.proxy 161 | ; Type : string 162 | ; Scope : system 163 | ; Default: none 164 | ; Info : Sets the host and user credentials to use as an egress proxy. This 165 | ; is only used if your site requires a proxy in order to access 166 | ; external servers on the internet, in this case the New Relic data 167 | ; collection servers. This is expressed in one of the following forms: 168 | ; hostname 169 | ; hostname:port 170 | ; user@hostname 171 | ; user@hostname:port 172 | ; user:password@hostname 173 | ; user:password@hostname:port 174 | ; 175 | ;newrelic.daemon.proxy = "" 176 | 177 | ; 178 | ; Setting: newrelic.daemon.pidfile 179 | ; Type : string 180 | ; Scope : system 181 | ; Default: OS dependent 182 | ; Info : Sets the name of the file to store the running daemon's process ID 183 | ; (PID) in. This file is used by the daemon startup and shutdown 184 | ; script to determine whether or not the daemon is already running. 185 | ; 186 | ;newrelic.daemon.pidfile = "" 187 | 188 | ; 189 | ; Setting: newrelic.daemon.location 190 | ; Type : string 191 | ; Scope : system 192 | ; Default: /usr/bin/newrelic-daemon 193 | ; Info : Sets the name of the daemon executable to launch. 194 | ; Please note that on OpenSolaris where /usr is frequently a read-only 195 | ; file system, the default daemon location is 196 | ; /opt/newrelic/bin/newrelic-daemon. 197 | ; 198 | ;newrelic.daemon.location = "/usr/bin/newrelic-daemon" 199 | 200 | ; 201 | ; Setting: newrelic.daemon.collector_host 202 | ; Type : string 203 | ; Scope : system 204 | ; Default: collector.newrelic.com 205 | ; Info : Sets the host name of the New Relic data collector host to use. 206 | ; Please note that this is NOT any form of local host. It refers to 207 | ; the New Relic provided host. There is very little reason to ever 208 | ; change this from the default except in certain very special 209 | ; circumstances, and then only on instruction from a New Relic sales 210 | ; person or support staff member. 211 | ; 212 | ;newrelic.daemon.collector_host = "collector.newrelic.com" 213 | 214 | ; 215 | ; Setting: newrelic.daemon.dont_launch 216 | ; Type : integer (0, 1, 2 or 3) 217 | ; Scope : system 218 | ; Default: 0 219 | ; Info : If you prefer to have the daemon launched externally before the 220 | ; agent starts up, set this variable to non-zero. The value you 221 | ; choose determines exactly when the agent is allowed to start the 222 | ; daemon: 223 | ; 0 - agent can start the daemon any time it needs to 224 | ; 1 - non-CLI (i.e Apache / php-fpm) agents can start the daemon 225 | ; 2 - only CLI agents can start the daemon 226 | ; 3 - the agent will never start the daemon 227 | ; 228 | ;newrelic.daemon.dont_launch = 0 229 | 230 | ; 231 | ; Setting: newrelic.capture_params 232 | ; Type : boolean 233 | ; Scope : per-directory 234 | ; Default: false 235 | ; Info : Enable or disable the capturing of URL parameters. If enabled, then 236 | ; any variables passed on the URL like (for example ?id=12345) will be 237 | ; saved with the request and visible in various places in the web UI. 238 | ; If you tend to pass sensitive information around directly in the URL 239 | ; then its a good idea to keep this disabled. However, if your URL 240 | ; parameters are simply used for parameters without sensitive data but 241 | ; that are meaningful to each transaction then you can enable this. 242 | ; 243 | ;newrelic.capture_params = false 244 | 245 | ; Setting: newrelic.ignored_params 246 | ; Type : string 247 | ; Scope : per-directory 248 | ; Default: none 249 | ; Info : A comma-separated list of parameters to always exclude if parameter 250 | ; capturing is enabled above. You can use this to filter out sensitive 251 | ; user data that may appear as a URL parameter. 252 | ; 253 | ;newrelic.ignored_params = "" 254 | 255 | ; 256 | ; Setting: newrelic.error_collector.enabled 257 | ; Type : boolean 258 | ; Scope : per-directory 259 | ; Default: true 260 | ; Info : Enable the New Relic error collector. This will record the 20 most 261 | ; severe errors per harvest cycle. It is rare to want to disable this. 262 | ; Please also note that your New Relic subscription level may force 263 | ; this to be disabled regardless of any value you set for it. 264 | ; 265 | ;newrelic.error_collector.enabled = true 266 | 267 | ; 268 | ; Setting: newrelic.error_collector.record_database_errors 269 | ; Type : boolean 270 | ; Scope : per-directory 271 | ; Default: false 272 | ; Info : Currently only supported for MySQL database functions. If enabled, 273 | ; this will cause errors returned by various MySQL functions to be 274 | ; treated as if they were PHP errors, and thus subject to error 275 | ; collection. This is only obeyed if the error collector is enabled 276 | ; above and the account subscription level permits error trapping. 277 | ; 278 | ;newrelic.error_collector.record_database_errors = false 279 | 280 | ; 281 | ; Setting: newrelic.error_collector.prioritize_api_errors 282 | ; Type : boolean 283 | ; Scope : per-directory 284 | ; Default: false 285 | ; Info : If the error collector is enabled and you use the New Relic API to 286 | ; notice an error, if this is set to true then assign the highest 287 | ; priority to such errors. 288 | ; 289 | ;newrelic.error_collector.prioritize_api_errors = false 290 | 291 | ; 292 | ; Setting: newrelic.browser_monitoring.auto_instrument 293 | ; Type : boolean 294 | ; Scope : per-directory 295 | ; Default: true 296 | ; Info : Enables or disables automatic real user monitoring ("auto-RUM"). 297 | ; When enabled will cause the agent to insert a header and a footer 298 | ; in HTML output that will time the actual end-user experience. 299 | ; 300 | ;newrelic.browser_monitoring.auto_instrument = true 301 | 302 | ; 303 | ; Setting: newrelic.transaction_tracer.enabled 304 | ; Type : boolean 305 | ; Scope : per-directory 306 | ; Default: true 307 | ; Info : Enables or disables the transaction tracer. When enabled this will 308 | ; produce a detailed call graph for any transaction that exceeds a 309 | ; certain threshold (see next entry). Only one transaction trace per 310 | ; application per harvest cycle is stored and it is always the slowest 311 | ; transaction during that cycle. Transaction traces are extremely 312 | ; useful when diagnosing problem spots in your application. Please 313 | ; note that TT's may be disabled by your account subscription level 314 | ; regardless of what you set here. 315 | ; 316 | ;newrelic.transaction_tracer.enabled = true 317 | 318 | ; 319 | ; Setting: newrelic.transaction_tracer.threshold 320 | ; Type : string with a time specification or the word "apdex_f" 321 | ; Scope : per-directory 322 | ; Default: "apdex_f" 323 | ; Info : Specifies the threshold above which a transaction becomes a 324 | ; candidate for the transaction tracer. This can either be an absolute 325 | ; time value like "200ms" or "1s250ms" or "1h30m" or "750us" or the 326 | ; word "apdex_f". This last value, "apdex_f", means "4 times apdex_t". 327 | ; Thus the threshold changes according to your apdex_t setting. This 328 | ; is the default. 329 | ; 330 | ;newrelic.transaction_tracer.threshold = "apdex_f" 331 | 332 | ; 333 | ; Setting: newrelic.transaction_tracer.detail 334 | ; Type : integer in the range 0-1 335 | ; Scope : per-directory 336 | ; Default: 1 337 | ; Info : Sets the level of detail in a transaction trace. Setting this to 0 338 | ; will only show the relatively few PHP functions that New Relic has 339 | ; deemed to be "interesting", as well as any custom functions you set 340 | ; (see below). A setting of 1 will trace and time all user functions. 341 | ; 342 | ; In earlier releases of the agent this was known as "top100". 343 | ; 344 | ;newrelic.transaction_tracer.detail = 1 345 | 346 | ; 347 | ; Setting: newrelic.transaction_tracer.slow_sql 348 | ; Type : boolean 349 | ; Scope : per-directory 350 | ; Default: true 351 | ; Info : Enables or disables the "slow SQL" tracer. When enabled, this will 352 | ; record the top 10 slowest SQL calls along with a stack trace of 353 | ; where the call occurred in your code. 354 | ; 355 | ;newrelic.transaction_tracer.slow_sql = true 356 | 357 | ; 358 | ; Setting: newrelic.transaction_tracer.stack_trace_threshold 359 | ; Type : time specification string ("500ms", "1s750ms" etc) 360 | ; Scope : per-directory 361 | ; Default: 500ms 362 | ; Info : Sets the threshold above which the New Relic agent will record a 363 | ; stack trace for a transaction trace. 364 | ; 365 | ;newrelic.transaction_tracer.stack_trace_threshold = 500 366 | 367 | ; 368 | ; Setting: newrelic.transaction_tracer.explain_enabled 369 | ; Type : boolean 370 | ; Scope : per-directory 371 | ; Default: true 372 | ; Info : Enables or disables requesting "explain plans" from MySQL and 373 | ; PostgreSQL databases for slow SQL calls. The threshold for 374 | ; requesting explain plans is defined below. 375 | ; 376 | ;newrelic.transaction_tracer.explain_enabled = true 377 | 378 | ; 379 | ; Setting: newrelic.transaction_tracer.explain_threshold 380 | ; Type : time specification string ("750ms", "1s 500ms" etc) 381 | ; Scope : per-directory 382 | ; Default: 500ms 383 | ; Info : Used by the slow SQL tracer to set the threshold above which an SQL 384 | ; statement is considered "slow", and to set the threshold above which 385 | ; the transaction tracer will request an "explain plan" from the data- 386 | ; base for slow SQL. This latter feature may not be active yet, please 387 | ; refer to the agent release notes to see when it becomes available. 388 | ; Only relevant if explain_enabled above is set to true. 389 | ; 390 | ;newrelic.transaction_tracer.explain_threshold = 500 391 | 392 | ; 393 | ; Setting: newrelic.transaction_tracer.record_sql 394 | ; Type : "off", "raw" or "obfuscated" 395 | ; Scope : per-directory 396 | ; Default: "obfuscated" 397 | ; Info : Sets how SQL statements are recorded (if at all). If this is set to 398 | ; "raw" then no attempt is made at obfuscating SQL statements. THIS IS 399 | ; HIGHLY DISCOURAGED IN PRODUCTION ENVIRONMENTS! Setting this to raw 400 | ; has considerable security implications as it can expose sensitive 401 | ; and private customer data. 402 | ; 403 | ;newrelic.transaction_tracer.record_sql = "obfuscated" 404 | 405 | ; Setting: newrelic.transaction_tracer.custom 406 | ; Type : string 407 | ; Scope : per-directory 408 | ; Default: none 409 | ; Info : Sets the name(s) of additional functions you want to instrument and 410 | ; appear in transaction traces. This is only meaningful if you have 411 | ; set newrelic.transaction_tracer.detail to 0. This can be a comma- 412 | ; separated list of function or class method names. 413 | ; 414 | ;newrelic.transaction_tracer.custom = "" 415 | 416 | ; 417 | ; Setting: newrelic.framework 418 | ; Type : string 419 | ; Scope : per-directory 420 | ; Default: empty (auto-detect framework) 421 | ; Info : Forces the framework to be one of the supported frameworks. This 422 | ; should only ever be used if the auto-detection fails, in which case 423 | ; we (support@newrelic.com) would very much like to know about the 424 | ; detection failure. Must be one of the following values: 425 | ; cakephp, codeigniter, drupal, drupal8, joomla, kohana, laravel, magento, 426 | ; mediawiki, symfony, wordpress, yii, zend or no_framework. 427 | ; Note that "drupal" covers only Drupal 6 and 7. 428 | ; 429 | ;newrelic.framework = "" 430 | 431 | ; 432 | ; Setting: newrelic.webtransaction.name.remove_trailing_path 433 | ; Type : boolean 434 | ; Scope : per-directory 435 | ; Default: false 436 | ; Info : Used to aid naming transactions correctly when an unsupported 437 | ; framework is being used. This option will cause anything after the 438 | ; script name to be stripped from a URL. For example, setting this 439 | ; would cause the "/xyz/zy" to be stripped from a URL such as 440 | ; "/path/to/foo.php/xyz/zy". 441 | ; 442 | ;newrelic.webtransaction.name.remove_trailing_path = false 443 | 444 | ; 445 | ; Setting: newrelic.webtransaction.name.functions 446 | ; Type : string 447 | ; Scope : per-directory 448 | ; Default: none 449 | ; Info : Unless a specific framework such as Drupal or Wordpress has been 450 | ; detected, transactions are named according to the first script 451 | ; encountered, such as login.php. However, if you use a dispatcher 452 | ; file such as index.php this produces less useful data. If you use 453 | ; a dispatcher to redirect to actions such as "login", "show", "edit" 454 | ; etc, you can set this to the top level functions for those actions, 455 | ; and the function names specified here will be used to name the 456 | ; transaction. 457 | ; 458 | ;newrelic.webtransaction.name.functions = "" 459 | 460 | ; 461 | ; Setting: newrelic.webtransaction.name.files 462 | ; Type : string 463 | ; Scope : per-directory 464 | ; Default: none 465 | ; Info : Same as newrelic.webtransaction.name.functions above but using file 466 | ; names instead of function names. Accepts standard POSIX regular 467 | ; expressions. 468 | ; 469 | ;newrelic.webtransaction.name.files = "" 470 | 471 | ; 472 | ; Setting: newrelic.daemon.auditlog 473 | ; Type : string 474 | ; Scope : system 475 | ; Default: none 476 | ; info : Sets the name of a file to record all uncompressed, un-encoded 477 | ; content that is sent from your machine to the New Relic servers. 478 | ; This includes the full URL for each command along with the payload 479 | ; delivered with the command. This allows you to satisfy yourself 480 | ; that the agent is not sending any sensitive data to our servers. 481 | ; This file must be a different file the the newrelic.daemon.logfile 482 | ; setting above. If you set it to the same name, then audit logging will be 483 | ; silently ignored. 484 | ;newrelic.daemon.auditlog = "/var/log/newrelic/audit.log" 485 | 486 | ; 487 | ; Setting: newrelic.analytics_events.enabled 488 | ; Type : boolean 489 | ; Scope : per-directory 490 | ; Default: true 491 | ; info : Collect and report analytics event data. Event data allows the 492 | ; New Relic UI to show additional information such as histograms at 493 | ; the cost of additional daemon memory and collector communication. 494 | ; 495 | ;newrelic.analytics_events.enabled = true 496 | 497 | ; 498 | ; Setting: newrelic.transaction_tracer.capture_attributes 499 | ; Type : boolean 500 | ; Scope : per-directory 501 | ; Default: true 502 | ; info : Attach custom parameters created using newrelic_add_custom_parameter 503 | ; to transaction traces. 504 | ; 505 | ;newrelic.transaction_tracer.capture_attributes = true 506 | 507 | ; 508 | ; Setting: newrelic.error_collector.capture_attributes 509 | ; Type : boolean 510 | ; Scope : per-directory 511 | ; Default: true 512 | ; info : Attach custom parameters created using newrelic_add_custom_parameter 513 | ; to traced errors. 514 | ; 515 | ;newrelic.error_collector.capture_attributes = true 516 | 517 | ; 518 | ; Setting: newrelic.analytics_events.capture_attributes 519 | ; Type : boolean 520 | ; Scope : per-directory 521 | ; Default: true 522 | ; info : Attach custom parameters created using newrelic_add_custom_parameter 523 | ; to transaction analytic events. 524 | ; 525 | ;newrelic.analytics_events.capture_attributes = true 526 | 527 | ; 528 | ; Setting: newrelic.browser_monitoring.capture_attributes 529 | ; Type : boolean 530 | ; Scope : per-directory 531 | ; Default: true 532 | ; info : Attach custom parameters created using newrelic_add_custom_parameter 533 | ; to browser monitoring analytics events. If this setting is enabled 534 | ; the custom parameters will be obfuscated and put into the real 535 | ; user monitoring Javascript injected into pages. 536 | ; 537 | ;newrelic.browser_monitoring.capture_attributes = false -------------------------------------------------------------------------------- /roles/newrelic/templates/nrsysmond.cfg: -------------------------------------------------------------------------------- 1 | # 2 | # New Relic Server Monitor configuration file. 3 | # 4 | # Lines that begin with a # are comment lines and are ignored by the server 5 | # monitor. For those options that have command line equivalents, if the 6 | # option is specified on the command line it will over-ride any value set 7 | # in this file. 8 | # 9 | 10 | # 11 | # Option : license_key 12 | # Value : 40-character hexadecimal string provided by New Relic. This is 13 | # required in order for the server monitor to start. 14 | # Default: none 15 | # 16 | license_key={{ newrelic_license_key }} 17 | 18 | # 19 | # Option : loglevel 20 | # Value : Level of detail you want in the log file (as defined by the logfile 21 | # setting below. Valid values are (in increasing levels of verbosity): 22 | # error - show errors only 23 | # warning - show errors and warnings 24 | # info - show minimal additional information messages 25 | # verbose - show more detailed information messages 26 | # debug - show debug messages 27 | # verbosedebug - show very detailed debug messages 28 | # Default: error 29 | # Note : Can also be set with the -d command line option. 30 | # 31 | loglevel={{ newrelic_loglevel }} 32 | 33 | # 34 | # Option : logfile 35 | # Value : Name of the file where the server monitor will store it's log 36 | # messages. The amount of detail stored in this file is controlled 37 | # by the loglevel option (above). 38 | # Default: none. However it is highly recommended you set a value for this. 39 | # Note : Can also be set with the -l command line option. 40 | # 41 | logfile={{ newrelic_logfile }} 42 | 43 | # 44 | # Option : proxy 45 | # Value : The name and optional login credentials of the proxy server to use 46 | # for all communication with the New Relic collector. In its simplest 47 | # form this setting is just a hostname[:port] setting. The default 48 | # port if none is specified is 1080. If your proxy requires a user 49 | # name, use the syntax user@host[:port]. If it also requires a 50 | # password use the format user:password@host[:port]. For example: 51 | # fred:secret@proxy.mydomain.com:8181 52 | # Default: none (use a direct connection) 53 | # 54 | {% if newrelic_proxy|default(False) != False %} 55 | proxy={{ newrelic_proxy }} 56 | {% else %} 57 | #proxy= 58 | {% endif %} 59 | 60 | # 61 | # Option : ssl 62 | # Value : Whether or not to use the Secure Sockets Layer (SSL) for all 63 | # communication with the New Relic collector. Possible values are 64 | # true/on or false/off. In certain rare cases you may need to modify 65 | # the SSL certificates settings below. 66 | # Default: false 67 | # 68 | ssl={{ newrelic_ssl }} 69 | 70 | # 71 | # Option : ssl_ca_bundle 72 | # Value : The name of a PEM-encoded Certificate Authority (CA) bundle to use 73 | # for SSL connections. This very rarely needs to be set. The monitor 74 | # will attempt to find the bundle in the most common locations. If 75 | # you need to use SSL and the monitor is unable to locate a CA bundle 76 | # then either set this value or the ssl_ca_path option below. 77 | # Default: /etc/ssl/certs/ca-certificates.crt or 78 | # /etc/pki/tls/certs/ca-bundle.crt 79 | # Note : Can also be set with the -b command line option. 80 | # 81 | {% if newrelic_ssl_ca_bundle|default(False) != False %} 82 | ssl_ca_bundle={{ newrelic_ssl_ca_bundle }} 83 | {% else %} 84 | #ssl_ca_bundle=/path/to/your/bundle.crt 85 | {% endif %} 86 | 87 | # 88 | # Option : ssl_ca_path 89 | # Value : If your SSL installation does not use CA bundles, but rather has a 90 | # directory with PEM-encoded Certificate Authority files, set this 91 | # option to the name of the directory that contains all the CA files. 92 | # Default: /etc/ssl/certs 93 | # Note : Can also be set with the -S command line option. 94 | # 95 | {% if newrelic_ssl_ca_path|default(False) != False %} 96 | ssl_ca_path={{ newrelic_ssl_ca_path }} 97 | {% else %} 98 | #ssl_ca_path=/etc/ssl/certs 99 | {% endif %} 100 | 101 | # 102 | # Option : pidfile 103 | # Value : Name of a file where the server monitoring daemon will store it's 104 | # process ID (PID). This is used by the startup and shutdown script 105 | # to determine if the monitor is already running, and to start it up 106 | # or shut it down. 107 | # Default: /tmp/nrsysmond.pid 108 | # Note : Can also be set with the -p command line option. 109 | # 110 | pidfile={{ newrelic_pidfile }} 111 | 112 | # 113 | # Option : collector_host 114 | # Value : The name of the New Relic collector to connect to. This should only 115 | # ever be changed on advise from a New Relic support staff member. 116 | # The format is host[:port]. Using a port number of 0 means the default 117 | # port, which is 80 (if not using the ssl option - see below) or 443 118 | # if SSL is enabled. If the port is omitted the default value is used. 119 | # Default: collector.newrelic.com 120 | # 121 | collector_host={{ newrelic_collector_host }} 122 | 123 | # 124 | # Option : timeout 125 | # Value : How long the monitor should wait to contact the collector host. If 126 | # the connection cannot be established in this period of time, the 127 | # monitor will progressively back off in 15-second increments, up to 128 | # a maximum of 300 seconds. Once the initial connection has been 129 | # established, this value is reset back to the value specified here 130 | # (or the default). This then sets the maximum time to wait for 131 | # a connection to the collector to report data. There is no back-off 132 | # once the original connection has been made. The value is in seconds. 133 | # Default: 30 134 | # 135 | timeout={{ newrelic_timeout }} 136 | -------------------------------------------------------------------------------- /roles/nginx/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart nginx 3 | service: name=nginx state=restarted enabled=yes 4 | 5 | - name: reload nginx 6 | service: name=nginx state=reloaded enabled=yes 7 | 8 | - name: stop nginx 9 | service: name=nginx state=stopped enabled=yes 10 | 11 | -------------------------------------------------------------------------------- /roles/nginx/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - apt_repository: repo='ppa:nginx/stable' state=present 3 | 4 | - name: Install nginx 5 | apt: name=nginx state=latest 6 | 7 | - name: Copy nginx configuration for wordpress 8 | template: src=wordpress dest=/etc/nginx/sites-available/wordpress force=no 9 | notify: restart nginx 10 | 11 | - name: Copy nginx ssl configuration for wordpress 12 | template: src=wordpress-ssl dest=/etc/nginx/sites-available/wordpress-ssl force=no 13 | notify: restart nginx 14 | 15 | - name: Copy nginx general configuration 16 | template: src=nginx.conf dest=/etc/nginx/nginx.conf 17 | notify: restart nginx 18 | 19 | - name: Copy nginx logrotate config 20 | template: src=logrotate dest=/etc/logrotate.d/nginx 21 | notify: restart nginx 22 | 23 | - name: Create symlink for wordpress configuration 24 | file: src=/etc/nginx/sites-available/wordpress dest=/etc/nginx/sites-enabled/wordpress state=link 25 | notify: reload nginx 26 | 27 | - name: Delete default 28 | file: path=/etc/nginx/sites-enabled/default state=absent 29 | notify: reload nginx 30 | -------------------------------------------------------------------------------- /roles/nginx/templates/logrotate: -------------------------------------------------------------------------------- 1 | /var/log/nginx/*.log { 2 | weekly 3 | missingok 4 | rotate 5 5 | compress 6 | delaycompress 7 | notifempty 8 | create 0640 www-data adm 9 | sharedscripts 10 | prerotate 11 | if [ -d /etc/logrotate.d/httpd-prerotate ]; then \ 12 | run-parts /etc/logrotate.d/httpd-prerotate; \ 13 | fi \ 14 | endscript 15 | postrotate 16 | [ -s /run/nginx.pid ] && kill -USR1 `cat /run/nginx.pid` 17 | endscript 18 | } -------------------------------------------------------------------------------- /roles/nginx/templates/nginx.conf: -------------------------------------------------------------------------------- 1 | user www-data; 2 | worker_processes auto; 3 | pid /run/nginx.pid; 4 | 5 | events { 6 | worker_connections 768; 7 | # multi_accept on; 8 | } 9 | 10 | http { 11 | 12 | ## 13 | # Basic Settings 14 | ## 15 | 16 | sendfile on; 17 | tcp_nopush on; 18 | tcp_nodelay on; 19 | keepalive_timeout 65; 20 | types_hash_max_size 2048; 21 | # server_tokens off; 22 | 23 | # server_names_hash_bucket_size 64; 24 | # server_name_in_redirect off; 25 | 26 | include /etc/nginx/mime.types; 27 | default_type application/octet-stream; 28 | 29 | ## 30 | # SSL Settings 31 | ## 32 | 33 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE 34 | ssl_prefer_server_ciphers on; 35 | 36 | ## 37 | # Logging Settings 38 | ## 39 | 40 | access_log /var/log/nginx/access.log; 41 | error_log /var/log/nginx/error.log; 42 | 43 | ## 44 | # Gzip Settings 45 | ## 46 | 47 | gzip on; 48 | gzip_disable "msie6"; 49 | 50 | gzip_vary on; 51 | gzip_proxied any; 52 | gzip_comp_level 6; 53 | gzip_buffers 16 8k; 54 | gzip_http_version 1.1; 55 | gzip_types text/plain text/css application/x-javascript application/json application/javascript text/xml application/xml application/xml+rss text/javascript; 56 | 57 | ## 58 | # Virtual Host Configs 59 | ## 60 | 61 | include /etc/nginx/conf.d/*.conf; 62 | include /etc/nginx/sites-enabled/*; 63 | } 64 | -------------------------------------------------------------------------------- /roles/nginx/templates/wordpress: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name {{ server_hostname }}; 4 | server_name {{ dev_server_hostname }}; 5 | root /var/www/html/wordpress/; 6 | 7 | client_max_body_size 64M; 8 | 9 | # Deny access to any files with a .php extension in the uploads directory 10 | location ~* /(?:uploads|files)/.*\.php$ { 11 | deny all; 12 | } 13 | 14 | location = /xmlrpc.php { 15 | deny all; 16 | access_log off; 17 | error_log off; 18 | } 19 | 20 | location / { 21 | index index.php index.html index.htm; 22 | try_files $uri $uri/ /index.php?$args; 23 | } 24 | 25 | location ~* \.(?:ico|svg|css|js|gif|jpe?g|png)$ { 26 | expires 30d; 27 | add_header Pragma public; 28 | add_header Cache-Control "public"; 29 | } 30 | 31 | location ~ \.php$ { 32 | try_files $uri =404; 33 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 34 | fastcgi_index index.php; 35 | fastcgi_pass unix:/var/run/php5-fpm.sock; 36 | fastcgi_param SCRIPT_FILENAME 37 | $document_root$fastcgi_script_name; 38 | include fastcgi_params; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /roles/nginx/templates/wordpress-ssl: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name {{ server_hostname }} {{ dev_server_hostname }}; 4 | return 301 https://{{ server_hostname }}$request_uri; 5 | } 6 | 7 | server { 8 | listen 8080; 9 | listen 443 ssl; 10 | ssl_certificate /etc/letsencrypt/live/{{ server_hostname }}/cert.pem; 11 | ssl_certificate_key /etc/letsencrypt/live/{{ server_hostname }}/privkey.pem; 12 | ssl_session_cache shared:SSL:20m; 13 | ssl_session_timeout 180m; 14 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 15 | ssl_prefer_server_ciphers on; 16 | ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5; 17 | 18 | add_header Strict-Transport-Security max-age=15768000; 19 | ssl_trusted_certificate /etc/letsencrypt/live/{{ server_hostname }}/chain.pem; 20 | resolver 8.8.8.8 8.8.4.4 valid=86400; 21 | resolver_timeout 10; 22 | 23 | ssl_stapling on; 24 | ssl_stapling_verify on; 25 | 26 | server_name {{ server_hostname }} {{ dev_server_hostname }}; 27 | root /var/www/html/wordpress/; 28 | 29 | client_max_body_size 64M; 30 | 31 | # Deny access to any files with a .php extension in the uploads directory 32 | location ~* /(?:uploads|files)/.*\.php$ { 33 | deny all; 34 | } 35 | 36 | location / { 37 | index index.php index.html index.htm; 38 | try_files $uri $uri/ /index.php?$args; 39 | } 40 | 41 | location = /xmlrpc.php { 42 | deny all; 43 | access_log off; 44 | error_log off; 45 | } 46 | 47 | location ~* \.(?:ico|svg|css|js|gif|jpe?g|png)$ { 48 | expires 30d; 49 | add_header Pragma public; 50 | add_header Cache-Control "public"; 51 | } 52 | 53 | location ~ \.php$ { 54 | try_files $uri =404; 55 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 56 | fastcgi_index index.php; 57 | fastcgi_pass unix:/var/run/php5-fpm.sock; 58 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 59 | include fastcgi_params; 60 | } 61 | } -------------------------------------------------------------------------------- /roles/php-fpm/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: stop php5-fpm 3 | shell: service php5-fpm stop > /dev/null 2>&1 4 | ignore_errors: True 5 | 6 | - name: restart php7.0-fpm 7 | shell: service php7.0-fpm restart -------------------------------------------------------------------------------- /roles/php-fpm/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - apt_repository: repo='ppa:ondrej/php' state=present 3 | 4 | - name: Install php-fpm 5 | apt: name={{item}} state=latest 6 | with_items: 7 | - php7.0-fpm 8 | - php7.0-mbstring 9 | - php7.0-mysql 10 | - php7.0-dev 11 | - php7.0-mcrypt 12 | - php7.0-curl 13 | - imagemagick 14 | - php7.0-imagick 15 | - php7.0-gd 16 | - php7.0-xmlrpc 17 | notify: 18 | - stop php5-fpm 19 | - restart php7.0-fpm 20 | 21 | - name: Install xdebug (disabled) 22 | shell: pecl install xdebug 23 | ignore_errors: yes 24 | 25 | - name: Disable default pool 26 | command: mv /etc/php/7.0/fpm/pool.d/www.conf /etc/php/7.0/fpm/pool.d/www.disabled creates=/etc/php/7.0/fpm/pool.d/www.disabled 27 | notify: restart php7.0-fpm 28 | 29 | - name: Copy php-fpm configuration 30 | template: src=wordpress.conf dest=/etc/php/7.0/fpm/pool.d 31 | notify: restart php7.0-fpm 32 | 33 | # Configure and secure php.ini 34 | 35 | - name: php.ini - Change max upload size to 15M 36 | replace: dest=/etc/php/7.0/fpm/php.ini regexp='^upload_max_filesize = .*$' replace='upload_max_filesize = 15M' 37 | notify: restart php7.0-fpm 38 | 39 | - name: php.ini - Change memory limit to 256M 40 | replace: dest=/etc/php/7.0/fpm/php.ini regexp='^memory_limit = .*$' replace='memory_limit = 256M' 41 | notify: restart php7.0-fpm 42 | 43 | - name: php.ini - Ensure php7.0-fpm cgi.fix_pathinfo=0 44 | lineinfile: dest=/etc/php/7.0/fpm/php.ini regexp='^(.*)cgi.fix_pathinfo=' line=cgi.fix_pathinfo=0 45 | notify: restart php7.0-fpm 46 | 47 | - name: Download composer 48 | get_url: 49 | url: https://getcomposer.org/installer 50 | dest: /tmp/installer 51 | 52 | - name: Install composer 53 | shell: cat /tmp/installer | php -- --install-dir=/usr/local/bin 54 | args: 55 | creates: /usr/local/bin/composer 56 | 57 | - name: Rename composer.phar to composer 58 | shell: mv /usr/local/bin/composer.phar /usr/local/bin/composer 59 | args: 60 | creates: /usr/local/bin/composer 61 | 62 | - name: Make composer executable 63 | file: 64 | path: /usr/local/bin/composer 65 | mode: a+x 66 | state: file 67 | owner: {{remote_user}} 68 | -------------------------------------------------------------------------------- /roles/php-fpm/templates/wordpress.conf: -------------------------------------------------------------------------------- 1 | [wordpress] 2 | listen = /var/run/php5-fpm.sock 3 | listen.owner = www-data 4 | listen.group = www-data 5 | listen.mode = 0660 6 | user = wordpress 7 | group = wordpress 8 | pm = dynamic 9 | pm.max_children = 6 10 | pm.start_servers = 3 11 | pm.min_spare_servers = 3 12 | pm.max_spare_servers = 5 13 | pm.max_requests = 500 14 | chdir = /var/www/html/wordpress/ -------------------------------------------------------------------------------- /roles/phpmyadmin/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart nginx 3 | service: name=nginx state=restarted enabled=yes -------------------------------------------------------------------------------- /roles/phpmyadmin/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install dbconfig-common 3 | apt: pkg=dbconfig-common state=latest force=yes 4 | 5 | - name: Install phpmyadmin 6 | apt: name=phpmyadmin state=latest force=yes 7 | 8 | - name: Copy nginx configuration for phpmyadmin 9 | template: src=phpmyadmin dest=/etc/nginx/sites-available/phpmyadmin 10 | notify: restart nginx -------------------------------------------------------------------------------- /roles/phpmyadmin/templates/phpmyadmin: -------------------------------------------------------------------------------- 1 | server { 2 | listen 9997; 3 | server_name {{ server_hostname }}; 4 | server_name {{ dev_server_hostname }}; 5 | root /usr/share/phpmyadmin; 6 | index index.php; 7 | 8 | location ~ \.php$ { 9 | try_files $uri =404; 10 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 11 | fastcgi_index index.php; 12 | fastcgi_pass unix:/var/run/php5-fpm.sock; 13 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 14 | include fastcgi_params; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /roles/postfix/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart postfix 3 | service: name=postfix state=restarted enabled=yes 4 | -------------------------------------------------------------------------------- /roles/postfix/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Set Postfix option hostname 3 | debconf: name=postfix question="postfix/mailname" value="{{server_hostname}}" vtype="string" 4 | 5 | - name: Set Postfix option type as internet site 6 | debconf: name=postfix question="postfix/main_mailer_type" value="'Internet Site'" vtype="string" 7 | 8 | - name: Install Postfix 9 | apt: name={{item}} state=latest 10 | with_items: 11 | - postfix 12 | - mailutils 13 | 14 | -------------------------------------------------------------------------------- /roles/redis/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart php7.0-fpm 3 | shell: service php7.0-fpm restart 4 | 5 | - name: restart redis 6 | service: name=redis-server state=restarted enabled=yes -------------------------------------------------------------------------------- /roles/redis/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Add Redis source 3 | apt_repository: repo='ppa:chris-lea/redis-server' state=present 4 | 5 | - name: Install redis 6 | apt: name=redis-server state=latest 7 | notify: rrestart redis 8 | 9 | - name: Copy redis.conf 10 | template: src=redis.conf dest=/etc/redis/redis.conf 11 | notify: restart redis 12 | 13 | - name: Install php redis 14 | apt: name=php-redis state=latest 15 | notify: restart php7.0-fpm -------------------------------------------------------------------------------- /roles/redis/templates/redis.conf: -------------------------------------------------------------------------------- 1 | # Redis configuration file example 2 | 3 | # Note on units: when memory size is needed, it is possible to specify 4 | # it in the usual form of 1k 5GB 4M and so forth: 5 | # 6 | # 1k => 1000 bytes 7 | # 1kb => 1024 bytes 8 | # 1m => 1000000 bytes 9 | # 1mb => 1024*1024 bytes 10 | # 1g => 1000000000 bytes 11 | # 1gb => 1024*1024*1024 bytes 12 | # 13 | # units are case insensitive so 1GB 1Gb 1gB are all the same. 14 | 15 | # By default Redis does not run as a daemon. Use 'yes' if you need it. 16 | # Note that Redis will write a pid file in /var/run/redis.pid when daemonized. 17 | daemonize yes 18 | 19 | # When running daemonized, Redis writes a pid file in /var/run/redis.pid by 20 | # default. You can specify a custom pid file location here. 21 | pidfile /var/run/redis/redis-server.pid 22 | 23 | # Accept connections on the specified port, default is 6379. 24 | # If port 0 is specified Redis will not listen on a TCP socket. 25 | port 6379 26 | 27 | # By default Redis listens for connections from all the network interfaces 28 | # available on the server. It is possible to listen to just one or multiple 29 | # interfaces using the "bind" configuration directive, followed by one or 30 | # more IP addresses. 31 | # 32 | # Examples: 33 | # 34 | # bind 192.168.1.100 10.0.0.1 35 | bind 127.0.0.1 36 | 37 | # Specify the path for the unix socket that will be used to listen for 38 | # incoming connections. There is no default, so Redis will not listen 39 | # on a unix socket when not specified. 40 | # 41 | # unixsocket /var/run/redis/redis.sock 42 | # unixsocketperm 755 43 | 44 | # Close the connection after a client is idle for N seconds (0 to disable) 45 | timeout 0 46 | 47 | # TCP keepalive. 48 | # 49 | # If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence 50 | # of communication. This is useful for two reasons: 51 | # 52 | # 1) Detect dead peers. 53 | # 2) Take the connection alive from the point of view of network 54 | # equipment in the middle. 55 | # 56 | # On Linux, the specified value (in seconds) is the period used to send ACKs. 57 | # Note that to close the connection the double of the time is needed. 58 | # On other kernels the period depends on the kernel configuration. 59 | # 60 | # A reasonable value for this option is 60 seconds. 61 | tcp-keepalive 0 62 | 63 | # Specify the server verbosity level. 64 | # This can be one of: 65 | # debug (a lot of information, useful for development/testing) 66 | # verbose (many rarely useful info, but not a mess like the debug level) 67 | # notice (moderately verbose, what you want in production probably) 68 | # warning (only very important / critical messages are logged) 69 | loglevel notice 70 | 71 | # Specify the log file name. Also the emptry string can be used to force 72 | # Redis to log on the standard output. Note that if you use standard 73 | # output for logging but daemonize, logs will be sent to /dev/null 74 | logfile /var/log/redis/redis-server.log 75 | 76 | # To enable logging to the system logger, just set 'syslog-enabled' to yes, 77 | # and optionally update the other syslog parameters to suit your needs. 78 | # syslog-enabled no 79 | 80 | # Specify the syslog identity. 81 | # syslog-ident redis 82 | 83 | # Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7. 84 | # syslog-facility local0 85 | 86 | # Set the number of databases. The default database is DB 0, you can select 87 | # a different one on a per-connection basis using SELECT where 88 | # dbid is a number between 0 and 'databases'-1 89 | databases 16 90 | 91 | ################################ SNAPSHOTTING ################################# 92 | # 93 | # Save the DB on disk: 94 | # 95 | # save 96 | # 97 | # Will save the DB if both the given number of seconds and the given 98 | # number of write operations against the DB occurred. 99 | # 100 | # In the example below the behaviour will be to save: 101 | # after 900 sec (15 min) if at least 1 key changed 102 | # after 300 sec (5 min) if at least 10 keys changed 103 | # after 60 sec if at least 10000 keys changed 104 | # 105 | # Note: you can disable saving at all commenting all the "save" lines. 106 | # 107 | # It is also possible to remove all the previously configured save 108 | # points by adding a save directive with a single empty string argument 109 | # like in the following example: 110 | # 111 | # save "" 112 | 113 | save 900 1 114 | save 300 10 115 | save 60 10000 116 | 117 | # By default Redis will stop accepting writes if RDB snapshots are enabled 118 | # (at least one save point) and the latest background save failed. 119 | # This will make the user aware (in an hard way) that data is not persisting 120 | # on disk properly, otherwise chances are that no one will notice and some 121 | # distater will happen. 122 | # 123 | # If the background saving process will start working again Redis will 124 | # automatically allow writes again. 125 | # 126 | # However if you have setup your proper monitoring of the Redis server 127 | # and persistence, you may want to disable this feature so that Redis will 128 | # continue to work as usually even if there are problems with disk, 129 | # permissions, and so forth. 130 | stop-writes-on-bgsave-error yes 131 | 132 | # Compress string objects using LZF when dump .rdb databases? 133 | # For default that's set to 'yes' as it's almost always a win. 134 | # If you want to save some CPU in the saving child set it to 'no' but 135 | # the dataset will likely be bigger if you have compressible values or keys. 136 | rdbcompression yes 137 | 138 | # Since version 5 of RDB a CRC64 checksum is placed at the end of the file. 139 | # This makes the format more resistant to corruption but there is a performance 140 | # hit to pay (around 10%) when saving and loading RDB files, so you can disable it 141 | # for maximum performances. 142 | # 143 | # RDB files created with checksum disabled have a checksum of zero that will 144 | # tell the loading code to skip the check. 145 | rdbchecksum yes 146 | 147 | # The filename where to dump the DB 148 | dbfilename dump.rdb 149 | 150 | # The working directory. 151 | # 152 | # The DB will be written inside this directory, with the filename specified 153 | # above using the 'dbfilename' configuration directive. 154 | # 155 | # The Append Only File will also be created inside this directory. 156 | # 157 | # Note that you must specify a directory here, not a file name. 158 | dir /var/lib/redis 159 | 160 | ################################# REPLICATION ################################# 161 | 162 | # Master-Slave replication. Use slaveof to make a Redis instance a copy of 163 | # another Redis server. Note that the configuration is local to the slave 164 | # so for example it is possible to configure the slave to save the DB with a 165 | # different interval, or to listen to another port, and so on. 166 | # 167 | # slaveof 168 | 169 | # If the master is password protected (using the "requirepass" configuration 170 | # directive below) it is possible to tell the slave to authenticate before 171 | # starting the replication synchronization process, otherwise the master will 172 | # refuse the slave request. 173 | # 174 | # masterauth 175 | 176 | # When a slave loses its connection with the master, or when the replication 177 | # is still in progress, the slave can act in two different ways: 178 | # 179 | # 1) if slave-serve-stale-data is set to 'yes' (the default) the slave will 180 | # still reply to client requests, possibly with out of date data, or the 181 | # data set may just be empty if this is the first synchronization. 182 | # 183 | # 2) if slave-serve-stale-data is set to 'no' the slave will reply with 184 | # an error "SYNC with master in progress" to all the kind of commands 185 | # but to INFO and SLAVEOF. 186 | # 187 | slave-serve-stale-data yes 188 | 189 | # You can configure a slave instance to accept writes or not. Writing against 190 | # a slave instance may be useful to store some ephemeral data (because data 191 | # written on a slave will be easily deleted after resync with the master) but 192 | # may also cause problems if clients are writing to it because of a 193 | # misconfiguration. 194 | # 195 | # Since Redis 2.6 by default slaves are read-only. 196 | # 197 | # Note: read only slaves are not designed to be exposed to untrusted clients 198 | # on the internet. It's just a protection layer against misuse of the instance. 199 | # Still a read only slave exports by default all the administrative commands 200 | # such as CONFIG, DEBUG, and so forth. To a limited extend you can improve 201 | # security of read only slaves using 'rename-command' to shadow all the 202 | # administrative / dangerous commands. 203 | slave-read-only yes 204 | 205 | # Slaves send PINGs to server in a predefined interval. It's possible to change 206 | # this interval with the repl_ping_slave_period option. The default value is 10 207 | # seconds. 208 | # 209 | # repl-ping-slave-period 10 210 | 211 | # The following option sets the replication timeout for: 212 | # 213 | # 1) Bulk transfer I/O during SYNC, from the point of view of slave. 214 | # 2) Master timeout from the point of view of slaves (data, pings). 215 | # 3) Slave timeout from the point of view of masters (REPLCONF ACK pings). 216 | # 217 | # It is important to make sure that this value is greater than the value 218 | # specified for repl-ping-slave-period otherwise a timeout will be detected 219 | # every time there is low traffic between the master and the slave. 220 | # 221 | # repl-timeout 60 222 | 223 | # Disable TCP_NODELAY on the slave socket after SYNC? 224 | # 225 | # If you select "yes" Redis will use a smaller number of TCP packets and 226 | # less bandwidth to send data to slaves. But this can add a delay for 227 | # the data to appear on the slave side, up to 40 milliseconds with 228 | # Linux kernels using a default configuration. 229 | # 230 | # If you select "no" the delay for data to appear on the slave side will 231 | # be reduced but more bandwidth will be used for replication. 232 | # 233 | # By default we optimize for low latency, but in very high traffic conditions 234 | # or when the master and slaves are many hops away, turning this to "yes" may 235 | # be a good idea. 236 | repl-disable-tcp-nodelay no 237 | 238 | # Set the replication backlog size. The backlog is a buffer that accumulates 239 | # slave data when slaves are disconnected for some time, so that when a slave 240 | # wants to reconnect again, often a full resync is not needed, but a partial 241 | # resync is enough, just passing the portion of data the slave missed while 242 | # disconnected. 243 | # 244 | # The biggest the replication backlog, the longer the time the slave can be 245 | # disconnected and later be able to perform a partial resynchronization. 246 | # 247 | # The backlog is only allocated once there is at least a slave connected. 248 | # 249 | # repl-backlog-size 1mb 250 | 251 | # After a master has no longer connected slaves for some time, the backlog 252 | # will be freed. The following option configures the amount of seconds that 253 | # need to elapse, starting from the time the last slave disconnected, for 254 | # the backlog buffer to be freed. 255 | # 256 | # A value of 0 means to never release the backlog. 257 | # 258 | # repl-backlog-ttl 3600 259 | 260 | # The slave priority is an integer number published by Redis in the INFO output. 261 | # It is used by Redis Sentinel in order to select a slave to promote into a 262 | # master if the master is no longer working correctly. 263 | # 264 | # A slave with a low priority number is considered better for promotion, so 265 | # for instance if there are three slaves with priority 10, 100, 25 Sentinel will 266 | # pick the one wtih priority 10, that is the lowest. 267 | # 268 | # However a special priority of 0 marks the slave as not able to perform the 269 | # role of master, so a slave with priority of 0 will never be selected by 270 | # Redis Sentinel for promotion. 271 | # 272 | # By default the priority is 100. 273 | slave-priority 100 274 | 275 | # It is possible for a master to stop accepting writes if there are less than 276 | # N slaves connected, having a lag less or equal than M seconds. 277 | # 278 | # The N slaves need to be in "online" state. 279 | # 280 | # The lag in seconds, that must be <= the specified value, is calculated from 281 | # the last ping received from the slave, that is usually sent every second. 282 | # 283 | # This option does not GUARANTEES that N replicas will accept the write, but 284 | # will limit the window of exposure for lost writes in case not enough slaves 285 | # are available, to the specified number of seconds. 286 | # 287 | # For example to require at least 3 slaves with a lag <= 10 seconds use: 288 | # 289 | # min-slaves-to-write 3 290 | # min-slaves-max-lag 10 291 | # 292 | # Setting one or the other to 0 disables the feature. 293 | # 294 | # By default min-slaves-to-write is set to 0 (feature disabled) and 295 | # min-slaves-max-lag is set to 10. 296 | 297 | ################################## SECURITY ################################### 298 | 299 | # Require clients to issue AUTH before processing any other 300 | # commands. This might be useful in environments in which you do not trust 301 | # others with access to the host running redis-server. 302 | # 303 | # This should stay commented out for backward compatibility and because most 304 | # people do not need auth (e.g. they run their own servers). 305 | # 306 | # Warning: since Redis is pretty fast an outside user can try up to 307 | # 150k passwords per second against a good box. This means that you should 308 | # use a very strong password otherwise it will be very easy to break. 309 | # 310 | 311 | # Command renaming. 312 | # 313 | # It is possible to change the name of dangerous commands in a shared 314 | # environment. For instance the CONFIG command may be renamed into something 315 | # hard to guess so that it will still be available for internal-use tools 316 | # but not available for general clients. 317 | # 318 | # Example: 319 | # 320 | # rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52 321 | # 322 | # It is also possible to completely kill a command by renaming it into 323 | # an empty string: 324 | # 325 | # rename-command CONFIG "" 326 | # 327 | # Please note that changing the name of commands that are logged into the 328 | # AOF file or transmitted to slaves may cause problems. 329 | 330 | ################################### LIMITS #################################### 331 | 332 | # Set the max number of connected clients at the same time. By default 333 | # this limit is set to 10000 clients, however if the Redis server is not 334 | # able to configure the process file limit to allow for the specified limit 335 | # the max number of allowed clients is set to the current file limit 336 | # minus 32 (as Redis reserves a few file descriptors for internal uses). 337 | # 338 | # Once the limit is reached Redis will close all the new connections sending 339 | # an error 'max number of clients reached'. 340 | # 341 | # maxclients 10000 342 | 343 | # Don't use more memory than the specified amount of bytes. 344 | # When the memory limit is reached Redis will try to remove keys 345 | # accordingly to the eviction policy selected (see maxmemmory-policy). 346 | # 347 | # If Redis can't remove keys according to the policy, or if the policy is 348 | # set to 'noeviction', Redis will start to reply with errors to commands 349 | # that would use more memory, like SET, LPUSH, and so on, and will continue 350 | # to reply to read-only commands like GET. 351 | # 352 | # This option is usually useful when using Redis as an LRU cache, or to set 353 | # an hard memory limit for an instance (using the 'noeviction' policy). 354 | # 355 | # WARNING: If you have slaves attached to an instance with maxmemory on, 356 | # the size of the output buffers needed to feed the slaves are subtracted 357 | # from the used memory count, so that network problems / resyncs will 358 | # not trigger a loop where keys are evicted, and in turn the output 359 | # buffer of slaves is full with DELs of keys evicted triggering the deletion 360 | # of more keys, and so forth until the database is completely emptied. 361 | # 362 | # In short... if you have slaves attached it is suggested that you set a lower 363 | # limit for maxmemory so that there is some free RAM on the system for slave 364 | # output buffers (but this is not needed if the policy is 'noeviction'). 365 | # 366 | # maxmemory 367 | 368 | # MAXMEMORY POLICY: how Redis will select what to remove when maxmemory 369 | # is reached. You can select among five behaviors: 370 | # 371 | # volatile-lru -> remove the key with an expire set using an LRU algorithm 372 | # allkeys-lru -> remove any key accordingly to the LRU algorithm 373 | # volatile-random -> remove a random key with an expire set 374 | # allkeys-random -> remove a random key, any key 375 | # volatile-ttl -> remove the key with the nearest expire time (minor TTL) 376 | # noeviction -> don't expire at all, just return an error on write operations 377 | # 378 | # Note: with any of the above policies, Redis will return an error on write 379 | # operations, when there are not suitable keys for eviction. 380 | # 381 | # At the date of writing this commands are: set setnx setex append 382 | # incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd 383 | # sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby 384 | # zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby 385 | # getset mset msetnx exec sort 386 | # 387 | # The default is: 388 | # 389 | # maxmemory-policy volatile-lru 390 | 391 | # LRU and minimal TTL algorithms are not precise algorithms but approximated 392 | # algorithms (in order to save memory), so you can select as well the sample 393 | # size to check. For instance for default Redis will check three keys and 394 | # pick the one that was used less recently, you can change the sample size 395 | # using the following configuration directive. 396 | # 397 | # maxmemory-samples 3 398 | 399 | ############################## APPEND ONLY MODE ############################### 400 | 401 | # By default Redis asynchronously dumps the dataset on disk. This mode is 402 | # good enough in many applications, but an issue with the Redis process or 403 | # a power outage may result into a few minutes of writes lost (depending on 404 | # the configured save points). 405 | # 406 | # The Append Only File is an alternative persistence mode that provides 407 | # much better durability. For instance using the default data fsync policy 408 | # (see later in the config file) Redis can lose just one second of writes in a 409 | # dramatic event like a server power outage, or a single write if something 410 | # wrong with the Redis process itself happens, but the operating system is 411 | # still running correctly. 412 | # 413 | # AOF and RDB persistence can be enabled at the same time without problems. 414 | # If the AOF is enabled on startup Redis will load the AOF, that is the file 415 | # with the better durability guarantees. 416 | # 417 | # Please check http://redis.io/topics/persistence for more information. 418 | 419 | appendonly no 420 | 421 | # The name of the append only file (default: "appendonly.aof") 422 | # appendfilename appendonly.aof 423 | 424 | # The fsync() call tells the Operating System to actually write data on disk 425 | # instead to wait for more data in the output buffer. Some OS will really flush 426 | # data on disk, some other OS will just try to do it ASAP. 427 | # 428 | # Redis supports three different modes: 429 | # 430 | # no: don't fsync, just let the OS flush the data when it wants. Faster. 431 | # always: fsync after every write to the append only log . Slow, Safest. 432 | # everysec: fsync only one time every second. Compromise. 433 | # 434 | # The default is "everysec", as that's usually the right compromise between 435 | # speed and data safety. It's up to you to understand if you can relax this to 436 | # "no" that will let the operating system flush the output buffer when 437 | # it wants, for better performances (but if you can live with the idea of 438 | # some data loss consider the default persistence mode that's snapshotting), 439 | # or on the contrary, use "always" that's very slow but a bit safer than 440 | # everysec. 441 | # 442 | # More details please check the following article: 443 | # http://antirez.com/post/redis-persistence-demystified.html 444 | # 445 | # If unsure, use "everysec". 446 | 447 | # appendfsync always 448 | appendfsync everysec 449 | # appendfsync no 450 | 451 | # When the AOF fsync policy is set to always or everysec, and a background 452 | # saving process (a background save or AOF log background rewriting) is 453 | # performing a lot of I/O against the disk, in some Linux configurations 454 | # Redis may block too long on the fsync() call. Note that there is no fix for 455 | # this currently, as even performing fsync in a different thread will block 456 | # our synchronous write(2) call. 457 | # 458 | # In order to mitigate this problem it's possible to use the following option 459 | # that will prevent fsync() from being called in the main process while a 460 | # BGSAVE or BGREWRITEAOF is in progress. 461 | # 462 | # This means that while another child is saving, the durability of Redis is 463 | # the same as "appendfsync none". In practical terms, this means that it is 464 | # possible to lose up to 30 seconds of log in the worst scenario (with the 465 | # default Linux settings). 466 | # 467 | # If you have latency problems turn this to "yes". Otherwise leave it as 468 | # "no" that is the safest pick from the point of view of durability. 469 | no-appendfsync-on-rewrite no 470 | 471 | # Automatic rewrite of the append only file. 472 | # Redis is able to automatically rewrite the log file implicitly calling 473 | # BGREWRITEAOF when the AOF log size grows by the specified percentage. 474 | # 475 | # This is how it works: Redis remembers the size of the AOF file after the 476 | # latest rewrite (if no rewrite has happened since the restart, the size of 477 | # the AOF at startup is used). 478 | # 479 | # This base size is compared to the current size. If the current size is 480 | # bigger than the specified percentage, the rewrite is triggered. Also 481 | # you need to specify a minimal size for the AOF file to be rewritten, this 482 | # is useful to avoid rewriting the AOF file even if the percentage increase 483 | # is reached but it is still pretty small. 484 | # 485 | # Specify a percentage of zero in order to disable the automatic AOF 486 | # rewrite feature. 487 | 488 | auto-aof-rewrite-percentage 100 489 | auto-aof-rewrite-min-size 64mb 490 | 491 | ################################ LUA SCRIPTING ############################### 492 | 493 | # Max execution time of a Lua script in milliseconds. 494 | # 495 | # If the maximum execution time is reached Redis will log that a script is 496 | # still in execution after the maximum allowed time and will start to 497 | # reply to queries with an error. 498 | # 499 | # When a long running script exceed the maximum execution time only the 500 | # SCRIPT KILL and SHUTDOWN NOSAVE commands are available. The first can be 501 | # used to stop a script that did not yet called write commands. The second 502 | # is the only way to shut down the server in the case a write commands was 503 | # already issue by the script but the user don't want to wait for the natural 504 | # termination of the script. 505 | # 506 | # Set it to 0 or a negative value for unlimited execution without warnings. 507 | lua-time-limit 5000 508 | 509 | ################################## SLOW LOG ################################### 510 | 511 | # The Redis Slow Log is a system to log queries that exceeded a specified 512 | # execution time. The execution time does not include the I/O operations 513 | # like talking with the client, sending the reply and so forth, 514 | # but just the time needed to actually execute the command (this is the only 515 | # stage of command execution where the thread is blocked and can not serve 516 | # other requests in the meantime). 517 | # 518 | # You can configure the slow log with two parameters: one tells Redis 519 | # what is the execution time, in microseconds, to exceed in order for the 520 | # command to get logged, and the other parameter is the length of the 521 | # slow log. When a new command is logged the oldest one is removed from the 522 | # queue of logged commands. 523 | 524 | # The following time is expressed in microseconds, so 1000000 is equivalent 525 | # to one second. Note that a negative number disables the slow log, while 526 | # a value of zero forces the logging of every command. 527 | slowlog-log-slower-than 10000 528 | 529 | # There is no limit to this length. Just be aware that it will consume memory. 530 | # You can reclaim memory used by the slow log with SLOWLOG RESET. 531 | slowlog-max-len 128 532 | 533 | ############################# Event notification ############################## 534 | 535 | # Redis can notify Pub/Sub clients about events happening in the key space. 536 | # This feature is documented at http://redis.io/topics/keyspace-events 537 | # 538 | # For instance if keyspace events notification is enabled, and a client 539 | # performs a DEL operation on key "foo" stored in the Database 0, two 540 | # messages will be published via Pub/Sub: 541 | # 542 | # PUBLISH __keyspace@0__:foo del 543 | # PUBLISH __keyevent@0__:del foo 544 | # 545 | # It is possible to select the events that Redis will notify among a set 546 | # of classes. Every class is identified by a single character: 547 | # 548 | # K Keyspace events, published with __keyspace@__ prefix. 549 | # E Keyevent events, published with __keyevent@__ prefix. 550 | # g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ... 551 | # $ String commands 552 | # l List commands 553 | # s Set commands 554 | # h Hash commands 555 | # z Sorted set commands 556 | # x Expired events (events generated every time a key expires) 557 | # e Evicted events (events generated when a key is evicted for maxmemory) 558 | # A Alias for g$lshzxe, so that the "AKE" string means all the events. 559 | # 560 | # The "notify-keyspace-events" takes as argument a string that is composed 561 | # by zero or multiple characters. The empty string means that notifications 562 | # are disabled at all. 563 | # 564 | # Example: to enable list and generic events, from the point of view of the 565 | # event name, use: 566 | # 567 | # notify-keyspace-events Elg 568 | # 569 | # Example 2: to get the stream of the expired keys subscribing to channel 570 | # name __keyevent@0__:expired use: 571 | # 572 | # notify-keyspace-events Ex 573 | # 574 | # By default all notifications are disabled because most users don't need 575 | # this feature and the feature has some overhead. Note that if you don't 576 | # specify at least one of K or E, no events will be delivered. 577 | notify-keyspace-events "" 578 | 579 | ############################### ADVANCED CONFIG ############################### 580 | 581 | # Hashes are encoded using a memory efficient data structure when they have a 582 | # small number of entries, and the biggest entry does not exceed a given 583 | # threshold. These thresholds can be configured using the following directives. 584 | hash-max-ziplist-entries 512 585 | hash-max-ziplist-value 64 586 | 587 | # Similarly to hashes, small lists are also encoded in a special way in order 588 | # to save a lot of space. The special representation is only used when 589 | # you are under the following limits: 590 | list-max-ziplist-entries 512 591 | list-max-ziplist-value 64 592 | 593 | # Sets have a special encoding in just one case: when a set is composed 594 | # of just strings that happens to be integers in radix 10 in the range 595 | # of 64 bit signed integers. 596 | # The following configuration setting sets the limit in the size of the 597 | # set in order to use this special memory saving encoding. 598 | set-max-intset-entries 512 599 | 600 | # Similarly to hashes and lists, sorted sets are also specially encoded in 601 | # order to save a lot of space. This encoding is only used when the length and 602 | # elements of a sorted set are below the following limits: 603 | zset-max-ziplist-entries 128 604 | zset-max-ziplist-value 64 605 | 606 | # Active rehashing uses 1 millisecond every 100 milliseconds of CPU time in 607 | # order to help rehashing the main Redis hash table (the one mapping top-level 608 | # keys to values). The hash table implementation Redis uses (see dict.c) 609 | # performs a lazy rehashing: the more operation you run into an hash table 610 | # that is rehashing, the more rehashing "steps" are performed, so if the 611 | # server is idle the rehashing is never complete and some more memory is used 612 | # by the hash table. 613 | # 614 | # The default is to use this millisecond 10 times every second in order to 615 | # active rehashing the main dictionaries, freeing memory when possible. 616 | # 617 | # If unsure: 618 | # use "activerehashing no" if you have hard latency requirements and it is 619 | # not a good thing in your environment that Redis can reply form time to time 620 | # to queries with 2 milliseconds delay. 621 | # 622 | # use "activerehashing yes" if you don't have such hard requirements but 623 | # want to free memory asap when possible. 624 | activerehashing yes 625 | 626 | # The client output buffer limits can be used to force disconnection of clients 627 | # that are not reading data from the server fast enough for some reason (a 628 | # common reason is that a Pub/Sub client can't consume messages as fast as the 629 | # publisher can produce them). 630 | # 631 | # The limit can be set differently for the three different classes of clients: 632 | # 633 | # normal -> normal clients 634 | # slave -> slave clients and MONITOR clients 635 | # pubsub -> clients subcribed to at least one pubsub channel or pattern 636 | # 637 | # The syntax of every client-output-buffer-limit directive is the following: 638 | # 639 | # client-output-buffer-limit 640 | # 641 | # A client is immediately disconnected once the hard limit is reached, or if 642 | # the soft limit is reached and remains reached for the specified number of 643 | # seconds (continuously). 644 | # So for instance if the hard limit is 32 megabytes and the soft limit is 645 | # 16 megabytes / 10 seconds, the client will get disconnected immediately 646 | # if the size of the output buffers reach 32 megabytes, but will also get 647 | # disconnected if the client reaches 16 megabytes and continuously overcomes 648 | # the limit for 10 seconds. 649 | # 650 | # By default normal clients are not limited because they don't receive data 651 | # without asking (in a push way), but just after a request, so only 652 | # asynchronous clients may create a scenario where data is requested faster 653 | # than it can read. 654 | # 655 | # Instead there is a default limit for pubsub and slave clients, since 656 | # subscribers and slaves receive data in a push fashion. 657 | # 658 | # Both the hard or the soft limit can be disabled by setting them to zero. 659 | client-output-buffer-limit normal 0 0 0 660 | client-output-buffer-limit slave 256mb 64mb 60 661 | client-output-buffer-limit pubsub 32mb 8mb 60 662 | 663 | # Redis calls an internal function to perform many background tasks, like 664 | # closing connections of clients in timeot, purging expired keys that are 665 | # never requested, and so forth. 666 | # 667 | # Not all tasks are performed with the same frequency, but Redis checks for 668 | # tasks to perform accordingly to the specified "hz" value. 669 | # 670 | # By default "hz" is set to 10. Raising the value will use more CPU when 671 | # Redis is idle, but at the same time will make Redis more responsive when 672 | # there are many keys expiring at the same time, and timeouts may be 673 | # handled with more precision. 674 | # 675 | # The range is between 1 and 500, however a value over 100 is usually not 676 | # a good idea. Most users should use the default of 10 and raise this up to 677 | # 100 only in environments where very low latency is required. 678 | hz 10 679 | 680 | # When a child rewrites the AOF file, if the following option is enabled 681 | # the file will be fsync-ed every 32 MB of data generated. This is useful 682 | # in order to commit the file to the disk more incrementally and avoid 683 | # big latency spikes. 684 | aof-rewrite-incremental-fsync yes 685 | 686 | ################################## INCLUDES ################################### 687 | 688 | # Include one or more other config files here. This is useful if you 689 | # have a standard template that goes to all Redis server but also need 690 | # to customize a few per-server settings. Include files can include 691 | # other files, so use this wisely. 692 | # 693 | # include /path/to/local.conf 694 | # include /path/to/other.conf -------------------------------------------------------------------------------- /roles/sftp/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart sshd 3 | service: name=ssh state=restarted -------------------------------------------------------------------------------- /roles/sftp/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create sftp default user 3 | user: name=wordpress-sftp group=wordpress state=present home=/var/www/html/wordpress 4 | 5 | - name: Copy sshd_config template 6 | template: src=sshd_config dest=/etc/ssh/ force=yes 7 | notify: restart sshd 8 | 9 | -------------------------------------------------------------------------------- /roles/sftp/templates/sshd_config: -------------------------------------------------------------------------------- 1 | # Package generated configuration file 2 | # See the sshd_config(5) manpage for details 3 | 4 | # What ports, IPs and protocols we listen for 5 | Port 22 6 | # Use these options to restrict which interfaces/protocols sshd will bind to 7 | #ListenAddress :: 8 | #ListenAddress 0.0.0.0 9 | Protocol 2 10 | # HostKeys for protocol version 2 11 | HostKey /etc/ssh/ssh_host_rsa_key 12 | HostKey /etc/ssh/ssh_host_dsa_key 13 | HostKey /etc/ssh/ssh_host_ecdsa_key 14 | HostKey /etc/ssh/ssh_host_ed25519_key 15 | #Privilege Separation is turned on for security 16 | UsePrivilegeSeparation yes 17 | 18 | # Lifetime and size of ephemeral version 1 server key 19 | KeyRegenerationInterval 3600 20 | ServerKeyBits 1024 21 | 22 | # Logging 23 | SyslogFacility AUTH 24 | LogLevel INFO 25 | 26 | # Authentication: 27 | LoginGraceTime 120 28 | PermitRootLogin without-password 29 | StrictModes yes 30 | 31 | RSAAuthentication yes 32 | PubkeyAuthentication yes 33 | #AuthorizedKeysFile %h/.ssh/authorized_keys 34 | 35 | # Don't read the user's ~/.rhosts and ~/.shosts files 36 | IgnoreRhosts yes 37 | # For this to work you will also need host keys in /etc/ssh_known_hosts 38 | RhostsRSAAuthentication no 39 | # similar for protocol version 2 40 | HostbasedAuthentication no 41 | # Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication 42 | #IgnoreUserKnownHosts yes 43 | 44 | # To enable empty passwords, change to yes (NOT RECOMMENDED) 45 | PermitEmptyPasswords no 46 | 47 | # Change to yes to enable challenge-response passwords (beware issues with 48 | # some PAM modules and threads) 49 | ChallengeResponseAuthentication no 50 | 51 | # Change to no to disable tunnelled clear text passwords 52 | PasswordAuthentication yes 53 | 54 | # Kerberos options 55 | #KerberosAuthentication no 56 | #KerberosGetAFSToken no 57 | #KerberosOrLocalPasswd yes 58 | #KerberosTicketCleanup yes 59 | 60 | # GSSAPI options 61 | #GSSAPIAuthentication no 62 | #GSSAPICleanupCredentials yes 63 | 64 | X11Forwarding yes 65 | X11DisplayOffset 10 66 | PrintMotd no 67 | PrintLastLog yes 68 | TCPKeepAlive yes 69 | #UseLogin no 70 | 71 | #MaxStartups 10:30:60 72 | #Banner /etc/issue.net 73 | 74 | # Allow client to pass locale environment variables 75 | AcceptEnv LANG LC_* 76 | 77 | Subsystem sftp /usr/lib/openssh/sftp-server 78 | 79 | # Set this to 'yes' to enable PAM authentication, account processing, 80 | # and session processing. If this is enabled, PAM authentication will 81 | # be allowed through the ChallengeResponseAuthentication and 82 | # PasswordAuthentication. Depending on your PAM configuration, 83 | # PAM authentication via ChallengeResponseAuthentication may bypass 84 | # the setting of "PermitRootLogin without-password". 85 | # If you just want the PAM account and session checks to run without 86 | # PAM authentication, then enable this but set PasswordAuthentication 87 | # and ChallengeResponseAuthentication to 'no'. 88 | UsePAM yes 89 | 90 | 91 | Match group filetransfer 92 | ChrootDirectory %h 93 | X11Forwarding no 94 | AllowTcpForwarding no 95 | ForceCommand internal-sftp 96 | -------------------------------------------------------------------------------- /roles/ufw/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install ufw package 3 | apt: name=ufw state=present 4 | 5 | - name: Disable all incoming traffic by default 6 | ufw: state=enabled policy=deny direction=incoming 7 | 8 | - name: Enable all outgoing traffic by default 9 | ufw: state=enabled policy=allow direction=outgoing 10 | 11 | - name: Allow normal web trafic traffic 12 | ufw: rule=allow port=80 proto=tcp 13 | 14 | - name: Allow ssl traffic 15 | ufw: rule=allow port=443 proto=tcp 16 | 17 | - name: Allow DNS traffic on tcp 18 | ufw: rule=allow port=53 proto=tcp 19 | 20 | - name: Allow DNS traffic on udp 21 | ufw: rule=allow port=53 proto=udp 22 | 23 | - name: Allow ssh traffic 24 | ufw: rule=allow port=22 proto=tcp -------------------------------------------------------------------------------- /roles/webgrind/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart nginx 3 | service: name=nginx state=restarted enabled=yes -------------------------------------------------------------------------------- /roles/webgrind/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install graphviz 3 | apt: name=graphviz state=latest 4 | 5 | - name: PHP | Download webgrind package 6 | get_url: url=https://github.com/jokkedk/webgrind/archive/master.zip dest=/tmp/webgrind.tar.gz 7 | 8 | - name: PHP | Extract the webgrind package 9 | unarchive: src=/tmp/webgrind.tar.gz dest=/var/www/html/ copy=no 10 | 11 | - name: PHP | Rename the webgrind folder 12 | shell: mv /var/www/html/webgrind-master/ /var/www/html/webgrind creates=/var/www/html/webgrind/ 13 | 14 | - name: PHP | Remove the webgrind-master dir 15 | file: path=/var/www/html/webgrind/webgrind-master state=absent 16 | 17 | - name: Copy nginx configuration for webgrind 18 | template: src=webgrind dest=/etc/nginx/sites-available/webgrind 19 | notify: restart nginx 20 | 21 | - name: Create config file 22 | template: src=config.php dest=/var/www/html/webgrind/config.php force=yes 23 | 24 | - name: PHP | Create the webgrind dirs 25 | file: name=/var/www/html/webgrind/storage state=directory mode=0700 26 | 27 | - name: Change ownership of webgrind installation 28 | file: path=/var/www/html/webgrind/ owner=wordpress group=wordpress state=directory recurse=yes -------------------------------------------------------------------------------- /roles/webgrind/templates/config.php: -------------------------------------------------------------------------------- 1 | cancel = true; 16 | } 17 | 18 | // Variants can be set by functions which use early-set globals like $_SERVER to run simple tests. 19 | // Functions defined in WordPress, plugins, and themes are not available and MUST NOT be used. 20 | // Example: vary_cache_on_function('return preg_match("/feedburner/i", $_SERVER["HTTP_USER_AGENT"]);'); 21 | // This will cause batcache to cache a variant for requests from Feedburner. 22 | // Tips for writing $function: 23 | // X_X DO NOT use any functions from your theme or plugins. Those files have not been included. Fatal error. 24 | // X_X DO NOT use any WordPress functions except is_admin() and is_multisite(). Fatal error. 25 | // X_X DO NOT include or require files from anywhere without consulting expensive professionals first. Fatal error. 26 | // X_X DO NOT use $wpdb, $blog_id, $current_user, etc. These have not been initialized. 27 | // ^_^ DO understand how create_function works. This is how your code is used: create_function('', $function); 28 | // ^_^ DO remember to return something. The return value determines the cache variant. 29 | function vary_cache_on_function($function) { 30 | global $batcache; 31 | 32 | if ( preg_match('/include|require|echo|print|dump|export|open|sock|unlink|`|eval/i', $function) ) 33 | die('Illegal word in variant determiner.'); 34 | 35 | if ( !preg_match('/\$_/', $function) ) 36 | die('Variant determiner should refer to at least one $_ variable.'); 37 | 38 | $batcache->add_variant($function); 39 | } 40 | 41 | class batcache { 42 | // This is the base configuration. You can edit these variables or move them into your wp-config.php file. 43 | var $max_age = 300; // Expire batcache items aged this many seconds (zero to disable batcache) 44 | 45 | var $remote = 0; // Zero disables sending buffers to remote datacenters (req/sec is never sent) 46 | 47 | var $times = 1; // Only batcache a page after it is accessed this many times... (two or more) 48 | var $seconds = 30; // ...in this many seconds (zero to ignore this and use batcache immediately) 49 | 50 | var $group = 'batcache'; // Name of memcached group. You can simulate a cache flush by changing this. 51 | 52 | var $unique = array(); // If you conditionally serve different content, put the variable values here. 53 | 54 | var $vary = array(); // Array of functions for create_function. The return value is added to $unique above. 55 | 56 | var $headers = array(); // Add headers here as name=>value or name=>array(values). These will be sent with every response from the cache. 57 | 58 | var $cache_redirects = false; // Set true to enable redirect caching. 59 | var $redirect_status = false; // This is set to the response code during a redirect. 60 | var $redirect_location = false; // This is set to the redirect location. 61 | 62 | var $use_stale = true; // Is it ok to return stale cached response when updating the cache? 63 | var $uncached_headers = array('transfer-encoding'); // These headers will never be cached. Apply strtolower. 64 | 65 | var $debug = true; // Set false to hide the batcache info 66 | 67 | var $cache_control = true; // Set false to disable Last-Modified and Cache-Control headers 68 | 69 | var $cancel = false; // Change this to cancel the output buffer. Use batcache_cancel(); 70 | 71 | var $noskip_cookies = array( 'wordpress_test_cookie' ); // Names of cookies - if they exist and the cache would normally be bypassed, don't bypass it 72 | 73 | var $genlock = false; 74 | var $do = false; 75 | 76 | function batcache( $settings ) { 77 | if ( is_array( $settings ) ) foreach ( $settings as $k => $v ) 78 | $this->$k = $v; 79 | } 80 | 81 | function is_ssl() { 82 | if ( isset($_SERVER['HTTPS']) ) { 83 | if ( 'on' == strtolower($_SERVER['HTTPS']) ) 84 | return true; 85 | if ( '1' == $_SERVER['HTTPS'] ) 86 | return true; 87 | } elseif ( isset($_SERVER['SERVER_PORT']) && ( '443' == $_SERVER['SERVER_PORT'] ) ) { 88 | return true; 89 | } 90 | return false; 91 | } 92 | 93 | function status_header( $status_header, $status_code ) { 94 | $this->status_header = $status_header; 95 | $this->status_code = $status_code; 96 | 97 | return $status_header; 98 | } 99 | 100 | function redirect_status( $status, $location ) { 101 | if ( $this->cache_redirects ) { 102 | $this->redirect_status = $status; 103 | $this->redirect_location = $location; 104 | } 105 | 106 | return $status; 107 | } 108 | 109 | function do_headers( $headers1, $headers2 = array() ) { 110 | // Merge the arrays of headers into one 111 | $headers = array(); 112 | $keys = array_unique( array_merge( array_keys( $headers1 ), array_keys( $headers2 ) ) ); 113 | foreach ( $keys as $k ) { 114 | $headers[$k] = array(); 115 | if ( isset( $headers1[$k] ) && isset( $headers2[$k] ) ) 116 | $headers[$k] = array_merge( (array) $headers2[$k], (array) $headers1[$k] ); 117 | elseif ( isset( $headers2[$k] ) ) 118 | $headers[$k] = (array) $headers2[$k]; 119 | else 120 | $headers[$k] = (array) $headers1[$k]; 121 | $headers[$k] = array_unique( $headers[$k] ); 122 | } 123 | // These headers take precedence over any previously sent with the same names 124 | foreach ( $headers as $k => $values ) { 125 | $clobber = true; 126 | foreach ( $values as $v ) { 127 | header( "$k: $v", $clobber ); 128 | $clobber = false; 129 | } 130 | } 131 | } 132 | 133 | function configure_groups() { 134 | // Configure the memcached client 135 | if ( ! $this->remote ) 136 | if ( function_exists('wp_cache_add_no_remote_groups') ) 137 | wp_cache_add_no_remote_groups(array($this->group)); 138 | if ( function_exists('wp_cache_add_global_groups') ) 139 | wp_cache_add_global_groups(array($this->group)); 140 | } 141 | 142 | // Defined here because timer_stop() calls number_format_i18n() 143 | function timer_stop($display = 0, $precision = 3) { 144 | global $timestart, $timeend; 145 | $mtime = microtime(); 146 | $mtime = explode(' ',$mtime); 147 | $mtime = $mtime[1] + $mtime[0]; 148 | $timeend = $mtime; 149 | $timetotal = $timeend-$timestart; 150 | $r = number_format($timetotal, $precision); 151 | if ( $display ) 152 | echo $r; 153 | return $r; 154 | } 155 | 156 | function ob($output) { 157 | // PHP5 and objects disappearing before output buffers? 158 | wp_cache_init(); 159 | 160 | // Remember, $wp_object_cache was clobbered in wp-settings.php so we have to repeat this. 161 | $this->configure_groups(); 162 | 163 | if ( $this->cancel !== false ) { 164 | wp_cache_delete( "{$this->url_key}_genlock", $this->group ); 165 | return $output; 166 | } 167 | 168 | // Do not batcache blank pages unless they are HTTP redirects 169 | $output = trim($output); 170 | if ( $output === '' && (!$this->redirect_status || !$this->redirect_location) ) { 171 | wp_cache_delete( "{$this->url_key}_genlock", $this->group ); 172 | return; 173 | } 174 | 175 | // Do not cache 5xx responses 176 | if ( isset( $this->status_code ) && intval($this->status_code / 100) == 5 ) { 177 | wp_cache_delete( "{$this->url_key}_genlock", $this->group ); 178 | return $output; 179 | } 180 | 181 | $this->do_variants($this->vary); 182 | $this->generate_keys(); 183 | 184 | // Construct and save the batcache 185 | $this->cache = array( 186 | 'output' => $output, 187 | 'time' => time(), 188 | 'timer' => $this->timer_stop(false, 3), 189 | 'headers' => array(), 190 | 'status_header' => $this->status_header, 191 | 'redirect_status' => $this->redirect_status, 192 | 'redirect_location' => $this->redirect_location, 193 | 'version' => $this->url_version 194 | ); 195 | 196 | foreach ( headers_list() as $header ) { 197 | list($k, $v) = array_map('trim', explode(':', $header, 2)); 198 | $this->cache['headers'][$k][] = $v; 199 | } 200 | 201 | if ( !empty( $this->cache['headers'] ) && !empty( $this->uncached_headers ) ) { 202 | foreach ( $this->uncached_headers as $header ) 203 | unset( $this->cache['headers'][$header] ); 204 | } 205 | 206 | foreach ( $this->cache['headers'] as $header => $values ) { 207 | // Do not cache if cookies were set 208 | if ( strtolower( $header ) === 'set-cookie' ) { 209 | wp_cache_delete( "{$this->url_key}_genlock", $this->group ); 210 | return $output; 211 | } 212 | 213 | foreach ( (array) $values as $value ) 214 | if ( preg_match('/^Cache-Control:.*max-?age=(\d+)/i', "$header: $value", $matches) ) 215 | $this->max_age = intval($matches[1]); 216 | } 217 | 218 | $this->cache['max_age'] = $this->max_age; 219 | 220 | wp_cache_set($this->key, $this->cache, $this->group, $this->max_age + $this->seconds + 30); 221 | 222 | // Unlock regeneration 223 | wp_cache_delete("{$this->url_key}_genlock", $this->group); 224 | 225 | if ( $this->cache_control ) { 226 | // Don't clobber Last-Modified header if already set, e.g. by WP::send_headers() 227 | if ( !isset($this->cache['headers']['Last-Modified']) ) 228 | header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $this->cache['time'] ) . ' GMT', true ); 229 | if ( !isset($this->cache['headers']['Cache-Control']) ) 230 | header("Cache-Control: max-age=$this->max_age, must-revalidate", false); 231 | } 232 | 233 | $this->do_headers( $this->headers ); 234 | 235 | // Add some debug info just before debug ) { 237 | $this->add_debug_just_cached(); 238 | } 239 | 240 | // Pass output to next ob handler 241 | batcache_stats( 'batcache', 'total_page_views' ); 242 | return $this->cache['output']; 243 | } 244 | 245 | function add_variant($function) { 246 | $key = md5($function); 247 | $this->vary[$key] = $function; 248 | } 249 | 250 | function do_variants($dimensions = false) { 251 | // This function is called without arguments early in the page load, then with arguments during the OB handler. 252 | if ( $dimensions === false ) 253 | $dimensions = wp_cache_get("{$this->url_key}_vary", $this->group); 254 | else 255 | wp_cache_set("{$this->url_key}_vary", $dimensions, $this->group, $this->max_age + 10); 256 | 257 | if ( is_array($dimensions) ) { 258 | ksort($dimensions); 259 | foreach ( $dimensions as $key => $function ) { 260 | $fun = create_function('', $function); 261 | $value = $fun(); 262 | $this->keys[$key] = $value; 263 | } 264 | } 265 | } 266 | 267 | function generate_keys() { 268 | // ksort($this->keys); // uncomment this when traffic is slow 269 | $this->key = md5(serialize($this->keys)); 270 | $this->req_key = $this->key . '_reqs'; 271 | } 272 | 273 | function add_debug_just_cached() { 274 | $generation = $this->cache['timer']; 275 | $bytes = strlen( serialize( $this->cache ) ); 276 | $html = <<max_age} seconds 280 | --> 281 | 282 | HTML; 283 | $this->add_debug_html_to_output( $html ); 284 | } 285 | 286 | function add_debug_from_cache() { 287 | $seconds_ago = time() - $this->cache['time']; 288 | $generation = $this->cache['timer']; 289 | $serving = $this->timer_stop( false, 3 ); 290 | $expires = $this->cache['max_age'] - time() + $this->cache['time']; 291 | $html = << 298 | 299 | HTML; 300 | $this->add_debug_html_to_output( $html ); 301 | } 302 | 303 | function add_debug_html_to_output( $debug_html ) { 304 | // Casing on the Content-Type header is inconsistent 305 | foreach ( array( 'Content-Type', 'Content-type' ) as $key ) { 306 | if ( isset( $this->cache['headers'][ $key ][0] ) && 0 !== strpos( $this->cache['headers'][ $key ][0], 'text/html' ) ) 307 | return; 308 | } 309 | 310 | $head_position = strpos( $this->cache['output'], 'cache['output'] .= "\n$debug_html"; 315 | } 316 | } 317 | 318 | global $batcache; 319 | // Pass in the global variable which may be an array of settings to override defaults. 320 | $batcache = new batcache($batcache); 321 | 322 | if ( ! defined( 'WP_CONTENT_DIR' ) ) 323 | return; 324 | 325 | // Never batcache interactive scripts or API endpoints. 326 | if ( in_array( 327 | basename( $_SERVER['SCRIPT_FILENAME'] ), 328 | array( 329 | 'wp-app.php', 330 | 'xmlrpc.php', 331 | ) ) ) 332 | return; 333 | 334 | // Never batcache WP javascript generators 335 | if ( strstr( $_SERVER['SCRIPT_FILENAME'], 'wp-includes/js' ) ) 336 | return; 337 | 338 | // Never batcache when POST data is present. 339 | if ( ! empty( $GLOBALS['HTTP_RAW_POST_DATA'] ) || ! empty( $_POST ) ) 340 | return; 341 | 342 | // Never batcache when cookies indicate a cache-exempt visitor. 343 | if ( is_array( $_COOKIE) && ! empty( $_COOKIE ) ) { 344 | foreach ( array_keys( $_COOKIE ) as $batcache->cookie ) { 345 | if ( ! in_array( $batcache->cookie, $batcache->noskip_cookies ) && ( substr( $batcache->cookie, 0, 2 ) == 'wp' || substr( $batcache->cookie, 0, 9 ) == 'wordpress' || substr( $batcache->cookie, 0, 14 ) == 'comment_author' ) ) { 346 | batcache_stats( 'batcache', 'cookie_skip' ); 347 | return; 348 | } 349 | } 350 | } 351 | 352 | if ( ! include_once( WP_CONTENT_DIR . '/object-cache.php' ) ) 353 | return; 354 | 355 | wp_cache_init(); // Note: wp-settings.php calls wp_cache_init() which clobbers the object made here. 356 | 357 | if ( ! is_object( $wp_object_cache ) ) 358 | return; 359 | 360 | // Now that the defaults are set, you might want to use different settings under certain conditions. 361 | 362 | /* Example: if your documents have a mobile variant (a different document served by the same URL) you must tell batcache about the variance. Otherwise you might accidentally cache the mobile version and serve it to desktop users, or vice versa. 363 | $batcache->unique['mobile'] = is_mobile_user_agent(); 364 | */ 365 | 366 | /* Example: never batcache for this host 367 | if ( $_SERVER['HTTP_HOST'] == 'do-not-batcache-me.com' ) 368 | return; 369 | */ 370 | 371 | /* Example: batcache everything on this host regardless of traffic level 372 | if ( $_SERVER['HTTP_HOST'] == 'always-batcache-me.com' ) 373 | return; 374 | */ 375 | 376 | /* Example: If you sometimes serve variants dynamically (e.g. referrer search term highlighting) you probably don't want to batcache those variants. Remember this code is run very early in wp-settings.php so plugins are not yet loaded. You will get a fatal error if you try to call an undefined function. Either include your plugin now or define a test function in this file. 377 | if ( include_once( 'plugins/searchterm-highlighter.php') && referrer_has_search_terms() ) 378 | return; 379 | */ 380 | 381 | // Disabled 382 | if ( $batcache->max_age < 1 ) 383 | return; 384 | 385 | // Make sure we can increment. If not, turn off the traffic sensor. 386 | if ( ! method_exists( $GLOBALS['wp_object_cache'], 'incr' ) ) 387 | $batcache->times = 0; 388 | 389 | // Necessary to prevent clients using cached version after login cookies set. If this is a problem, comment it out and remove all Last-Modified headers. 390 | header('Vary: Cookie', false); 391 | 392 | // Things that define a unique page. 393 | if ( isset( $_SERVER['QUERY_STRING'] ) ) 394 | parse_str($_SERVER['QUERY_STRING'], $batcache->query); 395 | 396 | $batcache->keys = array( 397 | 'host' => $_SERVER['HTTP_HOST'], 398 | 'method' => $_SERVER['REQUEST_METHOD'], 399 | 'path' => ( $batcache->pos = strpos($_SERVER['REQUEST_URI'], '?') ) ? substr($_SERVER['REQUEST_URI'], 0, $batcache->pos) : $_SERVER['REQUEST_URI'], 400 | 'query' => $batcache->query, 401 | 'extra' => $batcache->unique 402 | ); 403 | 404 | if ( $batcache->is_ssl() ) 405 | $batcache->keys['ssl'] = true; 406 | 407 | // Recreate the permalink from the URL 408 | $batcache->permalink = 'http://' . $batcache->keys['host'] . $batcache->keys['path'] . ( isset($batcache->keys['query']['p']) ? "?p=" . $batcache->keys['query']['p'] : '' ); 409 | $batcache->url_key = md5($batcache->permalink); 410 | $batcache->configure_groups(); 411 | $batcache->url_version = (int) wp_cache_get("{$batcache->url_key}_version", $batcache->group); 412 | $batcache->do_variants(); 413 | $batcache->generate_keys(); 414 | 415 | // Get the batcache 416 | $batcache->cache = wp_cache_get($batcache->key, $batcache->group); 417 | 418 | if ( isset( $batcache->cache['version'] ) && $batcache->cache['version'] < $batcache->url_version ) { 419 | // Always refresh the cache if a newer version is available. 420 | $batcache->do = true; 421 | } else if ( $batcache->seconds < 1 || $batcache->times < 2 ) { 422 | // Are we only caching frequently-requested pages? 423 | $batcache->do = true; 424 | } else { 425 | // No batcache item found, or ready to sample traffic again at the end of the batcache life? 426 | if ( !is_array($batcache->cache) || time() >= $batcache->cache['time'] + $batcache->max_age - $batcache->seconds ) { 427 | wp_cache_add($batcache->req_key, 0, $batcache->group); 428 | $batcache->requests = wp_cache_incr($batcache->req_key, 1, $batcache->group); 429 | 430 | if ( $batcache->requests >= $batcache->times && 431 | time() >= $batcache->cache['time'] + $batcache->cache['max_age'] 432 | ) { 433 | wp_cache_delete( $batcache->req_key, $batcache->group ); 434 | $batcache->do = true; 435 | } else { 436 | $batcache->do = false; 437 | } 438 | } 439 | } 440 | 441 | // Obtain cache generation lock 442 | if ( $batcache->do ) 443 | $batcache->genlock = wp_cache_add("{$batcache->url_key}_genlock", 1, $batcache->group, 10); 444 | 445 | if ( isset( $batcache->cache['time'] ) && // We have cache 446 | ! $batcache->genlock && // We have not obtained cache regeneration lock 447 | ( 448 | time() < $batcache->cache['time'] + $batcache->cache['max_age'] || // Batcached page that hasn't expired || 449 | ( $batcache->do && $batcache->use_stale ) // Regenerating it in another request and can use stale cache 450 | ) 451 | ) { 452 | // Issue redirect if cached and enabled 453 | if ( $batcache->cache['redirect_status'] && $batcache->cache['redirect_location'] && $batcache->cache_redirects ) { 454 | $status = $batcache->cache['redirect_status']; 455 | $location = $batcache->cache['redirect_location']; 456 | // From vars.php 457 | $is_IIS = (strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false || strpos($_SERVER['SERVER_SOFTWARE'], 'ExpressionDevServer') !== false); 458 | 459 | $batcache->do_headers( $batcache->headers ); 460 | if ( $is_IIS ) { 461 | header("Refresh: 0;url=$location"); 462 | } else { 463 | if ( php_sapi_name() != 'cgi-fcgi' ) { 464 | $texts = array( 465 | 300 => 'Multiple Choices', 466 | 301 => 'Moved Permanently', 467 | 302 => 'Found', 468 | 303 => 'See Other', 469 | 304 => 'Not Modified', 470 | 305 => 'Use Proxy', 471 | 306 => 'Reserved', 472 | 307 => 'Temporary Redirect', 473 | ); 474 | $protocol = $_SERVER["SERVER_PROTOCOL"]; 475 | if ( 'HTTP/1.1' != $protocol && 'HTTP/1.0' != $protocol ) 476 | $protocol = 'HTTP/1.0'; 477 | if ( isset($texts[$status]) ) 478 | header("$protocol $status " . $texts[$status]); 479 | else 480 | header("$protocol 302 Found"); 481 | } 482 | header("Location: $location"); 483 | } 484 | exit; 485 | } 486 | 487 | // Respect ETags served with feeds. 488 | $three04 = false; 489 | if ( isset( $SERVER['HTTP_IF_NONE_MATCH'] ) && isset( $batcache->cache['headers']['ETag'][0] ) && $_SERVER['HTTP_IF_NONE_MATCH'] == $batcache->cache['headers']['ETag'][0] ) 490 | $three04 = true; 491 | 492 | // Respect If-Modified-Since. 493 | elseif ( $batcache->cache_control && isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ) { 494 | $client_time = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']); 495 | if ( isset($batcache->cache['headers']['Last-Modified'][0]) ) 496 | $cache_time = strtotime($batcache->cache['headers']['Last-Modified'][0]); 497 | else 498 | $cache_time = $batcache->cache['time']; 499 | 500 | if ( $client_time >= $cache_time ) 501 | $three04 = true; 502 | } 503 | 504 | // Use the batcache save time for Last-Modified so we can issue "304 Not Modified" but don't clobber a cached Last-Modified header. 505 | if ( $batcache->cache_control && !isset($batcache->cache['headers']['Last-Modified'][0]) ) { 506 | header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $batcache->cache['time'] ) . ' GMT', true ); 507 | header('Cache-Control: max-age=' . ($batcache->cache['max_age'] - time() + $batcache->cache['time']) . ', must-revalidate', true); 508 | } 509 | 510 | // Add some debug info just before 511 | if ( $batcache->debug ) { 512 | $batcache->add_debug_from_cache(); 513 | } 514 | 515 | $batcache->do_headers( $batcache->headers, $batcache->cache['headers'] ); 516 | 517 | if ( $three04 ) { 518 | header("HTTP/1.1 304 Not Modified", true, 304); 519 | die; 520 | } 521 | 522 | if ( !empty($batcache->cache['status_header']) ) 523 | header($batcache->cache['status_header'], true); 524 | 525 | // Have you ever heard a death rattle before? 526 | die($batcache->cache['output']); 527 | } 528 | 529 | // Didn't meet the minimum condition? 530 | if ( ! $batcache->do || ! $batcache->genlock ) 531 | return; 532 | 533 | $wp_filter['status_header'][10]['batcache'] = array( 'function' => array(&$batcache, 'status_header'), 'accepted_args' => 2 ); 534 | $wp_filter['wp_redirect_status'][10]['batcache'] = array( 'function' => array(&$batcache, 'redirect_status'), 'accepted_args' => 2 ); 535 | 536 | ob_start(array(&$batcache, 'ob')); 537 | 538 | // It is safer to omit the final PHP closing tag. -------------------------------------------------------------------------------- /roles/wordpress/templates/object-cache.php: -------------------------------------------------------------------------------- 1 | add( $key, $data, $group, (int) $expire ); 46 | } 47 | 48 | /** 49 | * Closes the cache. 50 | * 51 | * This function has ceased to do anything since WordPress 2.5. The 52 | * functionality was removed along with the rest of the persistent cache. This 53 | * does not mean that plugins can't implement this function when they need to 54 | * make sure that the cache is cleaned up after WordPress no longer needs it. 55 | * 56 | * @return bool Always returns True 57 | */ 58 | function wp_cache_close() { 59 | 60 | return true; 61 | } 62 | 63 | /** 64 | * Decrement numeric cache item's value 65 | * 66 | * @uses $wp_object_cache Object Cache Class 67 | * @see WP_Object_Cache::decr() 68 | * 69 | * @param int|string $key The cache key to increment 70 | * @param int $offset The amount by which to decrement the item's value. Default is 1. 71 | * @param string $group The group the key is in. 72 | * @return false|int False on failure, the item's new value on success. 73 | */ 74 | function wp_cache_decr( $key, $offset = 1, $group = '' ) { 75 | 76 | global $wp_object_cache; 77 | 78 | return $wp_object_cache->decr( $key, $offset, $group ); 79 | } 80 | 81 | /** 82 | * Removes the cache contents matching key and group. 83 | * 84 | * @uses $wp_object_cache Object Cache Class 85 | * @see WP_Object_Cache::delete() 86 | * 87 | * @param int|string $key What the contents in the cache are called 88 | * @param string $group Where the cache contents are grouped 89 | * @return bool True on successful removal, false on failure 90 | */ 91 | function wp_cache_delete( $key, $group = '' ) { 92 | 93 | global $wp_object_cache; 94 | 95 | return $wp_object_cache->delete( $key, $group ); 96 | } 97 | 98 | /** 99 | * Removes cache contents for a given group. 100 | * 101 | * @uses $wp_object_cache Object Cache Class 102 | * @see WP_Object_Cache::delete_group() 103 | * 104 | * @param string $group Where the cache contents are grouped 105 | * @return bool True on successful removal, false on failure 106 | */ 107 | function wp_cache_delete_group( $group ) { 108 | 109 | global $wp_object_cache; 110 | return $wp_object_cache->delete_group( $group ); 111 | } 112 | 113 | 114 | /** 115 | * Removes all cache items. 116 | * 117 | * @uses $wp_object_cache Object Cache Class 118 | * @see WP_Object_Cache::flush() 119 | * 120 | * @return bool False on failure, true on success 121 | */ 122 | function wp_cache_flush() { 123 | 124 | global $wp_object_cache; 125 | 126 | return $wp_object_cache->flush(); 127 | } 128 | 129 | /** 130 | * Retrieves the cache contents from the cache by key and group. 131 | * 132 | * @uses $wp_object_cache Object Cache Class 133 | * @see WP_Object_Cache::get() 134 | * 135 | * @param int|string $key What the contents in the cache are called 136 | * @param string $group Where the cache contents are grouped 137 | * @param bool $force Whether to force an update of the local cache from the persistent cache (default is false) 138 | * @param bool $found Whether key was found in the cache. Disambiguates a return of false, a storable value. 139 | * @return bool|mixed False on failure to retrieve contents or the cache 140 | * contents on success 141 | */ 142 | function wp_cache_get( $key, $group = '', $force = false, &$found = null ) { 143 | 144 | global $wp_object_cache; 145 | 146 | return $wp_object_cache->get( $key, $group, $force, $found ); 147 | } 148 | 149 | /** 150 | * Increment numeric cache item's value 151 | * 152 | * @uses $wp_object_cache Object Cache Class 153 | * @see WP_Object_Cache::incr() 154 | * 155 | * @param int|string $key The cache key to increment 156 | * @param int $offset The amount by which to increment the item's value. Default is 1. 157 | * @param string $group The group the key is in. 158 | * @return false|int False on failure, the item's new value on success. 159 | */ 160 | function wp_cache_incr( $key, $offset = 1, $group = '' ) { 161 | 162 | global $wp_object_cache; 163 | 164 | return $wp_object_cache->incr( $key, $offset, $group ); 165 | } 166 | 167 | /** 168 | * Sets up Object Cache Global and assigns it. 169 | * 170 | * @global WP_Object_Cache $wp_object_cache WordPress Object Cache 171 | */ 172 | function wp_cache_init() { 173 | 174 | $GLOBALS['wp_object_cache'] = new WP_Object_Cache(); 175 | } 176 | 177 | /** 178 | * Replaces the contents of the cache with new data. 179 | * 180 | * @uses $wp_object_cache Object Cache Class 181 | * @see WP_Object_Cache::replace() 182 | * 183 | * @param int|string $key What to call the contents in the cache 184 | * @param mixed $data The contents to store in the cache 185 | * @param string $group Where to group the cache contents 186 | * @param int $expire When to expire the cache contents 187 | * @return bool False if not exists, true if contents were replaced 188 | */ 189 | function wp_cache_replace( $key, $data, $group = '', $expire = 0 ) { 190 | 191 | global $wp_object_cache; 192 | 193 | return $wp_object_cache->replace( $key, $data, $group, (int) $expire ); 194 | } 195 | 196 | /** 197 | * Saves the data to the cache. 198 | * 199 | * @uses $wp_object_cache Object Cache Class 200 | * @see WP_Object_Cache::set() 201 | * 202 | * @param int|string $key What to call the contents in the cache 203 | * @param mixed $data The contents to store in the cache 204 | * @param string $group Where to group the cache contents 205 | * @param int $expire When to expire the cache contents 206 | * @return bool False on failure, true on success 207 | */ 208 | function wp_cache_set( $key, $data, $group = '', $expire = 0 ) { 209 | 210 | global $wp_object_cache; 211 | 212 | return $wp_object_cache->set( $key, $data, $group, (int) $expire ); 213 | } 214 | 215 | /** 216 | * Switch the interal blog id. 217 | * 218 | * This changes the blog id used to create keys in blog specific groups. 219 | * 220 | * @param int $blog_id Blog ID 221 | */ 222 | function wp_cache_switch_to_blog( $blog_id ) { 223 | 224 | global $wp_object_cache; 225 | 226 | return $wp_object_cache->switch_to_blog( $blog_id ); 227 | } 228 | 229 | /** 230 | * Adds a group or set of groups to the list of global groups. 231 | * 232 | * @param string|array $groups A group or an array of groups to add 233 | */ 234 | function wp_cache_add_global_groups( $groups ) { 235 | 236 | global $wp_object_cache; 237 | 238 | return $wp_object_cache->add_global_groups( $groups ); 239 | } 240 | 241 | /** 242 | * Adds a group or set of groups to the list of non-persistent groups. 243 | * 244 | * @param string|array $groups A group or an array of groups to add 245 | */ 246 | function wp_cache_add_non_persistent_groups( $groups ) { 247 | 248 | global $wp_object_cache; 249 | 250 | $wp_object_cache->add_non_persistent_groups( $groups ); 251 | } 252 | 253 | /** 254 | * Reset internal cache keys and structures. If the cache backend uses global 255 | * blog or site IDs as part of its cache keys, this function instructs the 256 | * backend to reset those keys and perform any cleanup since blog or site IDs 257 | * have changed since cache init. 258 | * 259 | * This function is deprecated. Use wp_cache_switch_to_blog() instead of this 260 | * function when preparing the cache for a blog switch. For clearing the cache 261 | * during unit tests, consider using wp_cache_init(). wp_cache_init() is not 262 | * recommended outside of unit tests as the performance penality for using it is 263 | * high. 264 | * 265 | * @deprecated 3.5.0 266 | */ 267 | function wp_cache_reset() { 268 | 269 | _deprecated_function( __FUNCTION__, '3.5' ); 270 | 271 | global $wp_object_cache; 272 | 273 | return $wp_object_cache->reset(); 274 | } 275 | 276 | /** 277 | * WordPress Object Cache 278 | * 279 | * The WordPress Object Cache is used to save on trips to the database. The 280 | * Object Cache stores all of the cache data to memory and makes the cache 281 | * contents available by using a key, which is used to name and later retrieve 282 | * the cache contents. 283 | * 284 | * The Object Cache can be replaced by other caching mechanisms by placing files 285 | * in the wp-content folder which is looked at in wp-settings. If that file 286 | * exists, then this file will not be included. 287 | */ 288 | class WP_Object_Cache { 289 | 290 | /** 291 | * Holds the cached objects 292 | * 293 | * @var array 294 | * @access private 295 | */ 296 | var $cache = array(); 297 | 298 | /** 299 | * The amount of times the cache data was already stored in the cache. 300 | * 301 | * @access private 302 | * @var int 303 | */ 304 | var $cache_hits = 0; 305 | 306 | /** 307 | * Amount of times the cache did not have the request in cache 308 | * 309 | * @var int 310 | * @access public 311 | */ 312 | var $cache_misses = 0; 313 | 314 | /** 315 | * List of global groups 316 | * 317 | * @var array 318 | * @access protected 319 | */ 320 | var $global_groups = array(); 321 | 322 | /** 323 | * List of non-persistent groups 324 | * 325 | * @var array 326 | * @access protected 327 | */ 328 | var $non_persistent_groups = array(); 329 | 330 | /** 331 | * The blog prefix to prepend to keys in non-global groups. 332 | * 333 | * @var int 334 | * @access private 335 | */ 336 | var $blog_prefix; 337 | 338 | /** 339 | * Whether or not Redis is connected 340 | * 341 | * @var bool 342 | * @access private 343 | */ 344 | var $is_redis_connected = false; 345 | 346 | /** 347 | * The last triggered error 348 | */ 349 | var $last_triggered_error = ''; 350 | 351 | /** 352 | * Whether or not to use true cache groups, instead of flattening. 353 | * 354 | * @var bool 355 | * @access private 356 | */ 357 | const USE_GROUPS = WP_REDIS_USE_CACHE_GROUPS; 358 | 359 | /** 360 | * Adds data to the cache if it doesn't already exist. 361 | * 362 | * @uses WP_Object_Cache::_exists Checks to see if the cache already has data. 363 | * @uses WP_Object_Cache::set Sets the data after the checking the cache 364 | * contents existence. 365 | * 366 | * @param int|string $key What to call the contents in the cache 367 | * @param mixed $data The contents to store in the cache 368 | * @param string $group Where to group the cache contents 369 | * @param int $expire When to expire the cache contents 370 | * @return bool False if cache key and group already exist, true on success 371 | */ 372 | public function add( $key, $data, $group = 'default', $expire = 0 ) { 373 | 374 | if ( empty( $group ) ) { 375 | $group = 'default'; 376 | } 377 | 378 | if ( function_exists( 'wp_suspend_cache_addition' ) && wp_suspend_cache_addition() ) { 379 | return false; 380 | } 381 | 382 | if ( $this->_exists( $key, $group ) ) { 383 | return false; 384 | } 385 | 386 | return $this->set( $key, $data, $group, (int) $expire ); 387 | } 388 | 389 | /** 390 | * Sets the list of global groups. 391 | * 392 | * @param array $groups List of groups that are global. 393 | */ 394 | public function add_global_groups( $groups ) { 395 | 396 | $groups = (array) $groups; 397 | 398 | $groups = array_fill_keys( $groups, true ); 399 | $this->global_groups = array_merge( $this->global_groups, $groups ); 400 | } 401 | 402 | /** 403 | * Sets the list of non-persistent groups. 404 | * 405 | * @param array $groups List of groups that are non-persistent. 406 | */ 407 | public function add_non_persistent_groups( $groups ) { 408 | 409 | $groups = (array) $groups; 410 | 411 | $groups = array_fill_keys( $groups, true ); 412 | $this->non_persistent_groups = array_merge( $this->non_persistent_groups, $groups ); 413 | } 414 | 415 | /** 416 | * Decrement numeric cache item's value 417 | * 418 | * @param int|string $key The cache key to increment 419 | * @param int $offset The amount by which to decrement the item's value. Default is 1. 420 | * @param string $group The group the key is in. 421 | * @return false|int False on failure, the item's new value on success. 422 | */ 423 | public function decr( $key, $offset = 1, $group = 'default' ) { 424 | 425 | if ( empty( $group ) ) { 426 | $group = 'default'; 427 | } 428 | 429 | // The key needs to exist in order to be decremented 430 | if ( ! $this->_exists( $key, $group ) ) { 431 | return false; 432 | } 433 | 434 | $offset = (int) $offset; 435 | 436 | // If this isn't a persistant group, we have to sort this out ourselves, grumble grumble 437 | if ( ! $this->_should_persist( $group ) ) { 438 | $existing = $this->_get_internal( $key, $group ); 439 | if ( empty( $existing ) || ! is_numeric( $existing ) ) { 440 | $existing = 0; 441 | } else { 442 | $existing -= $offset; 443 | } 444 | if ( $existing < 0 ) { 445 | $existing = 0; 446 | } 447 | $this->_set_internal( $key, $group, $existing ); 448 | return $existing; 449 | } 450 | 451 | if ( self::USE_GROUPS ) { 452 | $redis_safe_group = $this->_key( '', $group ); 453 | $result = $this->_call_redis( 'hIncrBy', $redis_safe_group, $key, -$offset, $group ); 454 | if ( $result < 0 ) { 455 | $result = 0; 456 | $this->_call_redis( 'hSet', $redis_safe_group, $key, $result ); 457 | } 458 | } else { 459 | $id = $this->_key( $key, $group ); 460 | $result = $this->_call_redis( 'decrBy', $id, $offset ); 461 | if ( $result < 0 ) { 462 | $result = 0; 463 | $this->_call_redis( 'set', $id, $result ); 464 | } 465 | } 466 | 467 | if ( is_int( $result ) ) { 468 | $this->_set_internal( $key, $group, $result ); 469 | } 470 | return $result; 471 | } 472 | 473 | /** 474 | * Remove the contents of the cache key in the group 475 | * 476 | * If the cache key does not exist in the group and $force parameter is set 477 | * to false, then nothing will happen. The $force parameter is set to false 478 | * by default. 479 | * 480 | * @param int|string $key What the contents in the cache are called 481 | * @param string $group Where the cache contents are grouped 482 | * @param bool $force Optional. Whether to force the unsetting of the cache key in the group 483 | * key in the group 484 | * @return bool False if the contents weren't deleted and true on success 485 | */ 486 | public function delete( $key, $group = 'default', $force = false ) { 487 | 488 | if ( empty( $group ) ) { 489 | $group = 'default'; 490 | } 491 | 492 | if ( ! $force && ! $this->_exists( $key, $group ) ) { 493 | return false; 494 | } 495 | 496 | if ( $this->_should_persist( $group ) ) { 497 | if ( self::USE_GROUPS ) { 498 | $redis_safe_group = $this->_key( '', $group ); 499 | $result = $this->_call_redis( 'hDel', $redis_safe_group, $key ); 500 | } else { 501 | $id = $this->_key( $key, $group ); 502 | $result = $this->_call_redis( 'delete', $id ); 503 | } 504 | if ( 1 !== $result ) { 505 | return false; 506 | } 507 | } 508 | 509 | $this->_unset_internal( $key, $group ); 510 | return true; 511 | } 512 | 513 | /** 514 | * Remove the contents of all cache keys in the group. 515 | * 516 | * @param string $group Where the cache contents are grouped. 517 | * @return boolean True on success, false on failure. 518 | */ 519 | public function delete_group( $group ) { 520 | 521 | if ( ! self::USE_GROUPS ) { 522 | return false; 523 | } 524 | 525 | $multisite_safe_group = $this->multisite && ! isset( $this->global_groups[ $group ] ) ? $this->blog_prefix . $group : $group; 526 | $redis_safe_group = $this->_key( '', $group ); 527 | if ( $this->_should_persist( $group ) ) { 528 | $result = $this->_call_redis( 'delete', $redis_safe_group ); 529 | if ( 1 !== $result ) { 530 | return false; 531 | } 532 | } else if ( ! $this->_should_persist( $group ) && ! isset( $this->cache[ $multisite_safe_group ] ) ) { 533 | return false; 534 | } 535 | unset( $this->cache[ $multisite_safe_group ] ); 536 | return true; 537 | } 538 | 539 | /** 540 | * Clears the object cache of all data. 541 | * 542 | * By default, this will flush the session cache as well as Redis, but we 543 | * can leave the redis cache intact if we want. This is helpful when, for 544 | * instance, you're running a batch process and want to clear the session 545 | * store to reduce the memory footprint, but you don't want to have to 546 | * re-fetch all the values from the database. 547 | * 548 | * @param bool $redis Should we flush redis as well as the session cache? 549 | * @return bool Always returns true 550 | */ 551 | public function flush( $redis = true ) { 552 | 553 | $this->cache = array(); 554 | if ( $redis ) { 555 | $this->_call_redis( 'flushAll' ); 556 | } 557 | 558 | return true; 559 | } 560 | 561 | /** 562 | * Retrieves the cache contents, if it exists 563 | * 564 | * The contents will be first attempted to be retrieved by searching by the 565 | * key in the cache group. If the cache is hit (success) then the contents 566 | * are returned. 567 | * 568 | * On failure, the number of cache misses will be incremented. 569 | * 570 | * @param int|string $key What the contents in the cache are called 571 | * @param string $group Where the cache contents are grouped 572 | * @param string $force Whether to force a refetch rather than relying on the local cache (default is false) 573 | * @return bool|mixed False on failure to retrieve contents or the cache 574 | * contents on success 575 | */ 576 | public function get( $key, $group = 'default', $force = false, &$found = null ) { 577 | 578 | if ( empty( $group ) ) { 579 | $group = 'default'; 580 | } 581 | 582 | if ( ! $this->_exists( $key, $group ) ) { 583 | $this->cache_misses += 1; 584 | return false; 585 | } 586 | $this->cache_hits += 1; 587 | 588 | if ( $this->_should_persist( $group ) && ( $force || ! $this->_isset_internal( $key, $group ) ) ) { 589 | if ( self::USE_GROUPS ) { 590 | $redis_safe_group = $this->_key( '', $group ); 591 | $value = $this->_call_redis( 'hGet', $redis_safe_group, $key ); 592 | } else { 593 | $id = $this->_key( $key, $group ); 594 | $value = $this->_call_redis( 'get', $id ); 595 | } 596 | if ( ! is_numeric( $value ) ) { 597 | $value = unserialize( $value ); 598 | } 599 | $this->_set_internal( $key, $group, $value ); 600 | } 601 | return $this->_get_internal( $key, $group ); 602 | } 603 | 604 | /** 605 | * Increment numeric cache item's value 606 | * 607 | * @param int|string $key The cache key to increment 608 | * @param int $offset The amount by which to increment the item's value. Default is 1. 609 | * @param string $group The group the key is in. 610 | * @return false|int False on failure, the item's new value on success. 611 | */ 612 | public function incr( $key, $offset = 1, $group = 'default' ) { 613 | 614 | if ( empty( $group ) ) { 615 | $group = 'default'; 616 | } 617 | 618 | // The key needs to exist in order to be incremented 619 | if ( ! $this->_exists( $key, $group ) ) { 620 | return false; 621 | } 622 | 623 | $offset = (int) $offset; 624 | 625 | // If this isn't a persistant group, we have to sort this out ourselves, grumble grumble 626 | if ( ! $this->_should_persist( $group ) ) { 627 | $existing = $this->_get_internal( $key, $group ); 628 | if ( empty( $existing ) || ! is_numeric( $existing ) ) { 629 | $existing = 1; 630 | } else { 631 | $existing += $offset; 632 | } 633 | if ( $existing < 0 ) { 634 | $existing = 0; 635 | } 636 | $this->_set_internal( $key, $group, $existing ); 637 | return $existing; 638 | } 639 | 640 | if ( self::USE_GROUPS ) { 641 | $redis_safe_group = $this->_key( '', $group ); 642 | $result = $this->_call_redis( 'hIncrBy', $redis_safe_group, $key, $offset, $group ); 643 | if ( $result < 0 ) { 644 | $result = 0; 645 | $this->_call_redis( 'hSet', $redis_safe_group, $key, $result ); 646 | } 647 | } else { 648 | $id = $this->_key( $key, $group ); 649 | $result = $this->_call_redis( 'incrBy', $id, $offset ); 650 | if ( $result < 0 ) { 651 | $result = 0; 652 | $this->_call_redis( 'set', $id, $result ); 653 | } 654 | } 655 | 656 | if ( is_int( $result ) ) { 657 | $this->_set_internal( $key, $group, $result ); 658 | } 659 | return $result; 660 | } 661 | 662 | /** 663 | * Replace the contents in the cache, if contents already exist 664 | * 665 | * @see WP_Object_Cache::set() 666 | * 667 | * @param int|string $key What to call the contents in the cache 668 | * @param mixed $data The contents to store in the cache 669 | * @param string $group Where to group the cache contents 670 | * @param int $expire When to expire the cache contents 671 | * @return bool False if not exists, true if contents were replaced 672 | */ 673 | public function replace( $key, $data, $group = 'default', $expire = 0 ) { 674 | 675 | if ( empty( $group ) ) { 676 | $group = 'default'; 677 | } 678 | 679 | if ( ! $this->_exists( $key, $group ) ) { 680 | return false; 681 | } 682 | 683 | return $this->set( $key, $data, $group, (int) $expire ); 684 | } 685 | 686 | /** 687 | * Reset keys 688 | * 689 | * @deprecated 3.5.0 690 | */ 691 | public function reset() { 692 | 693 | _deprecated_function( __FUNCTION__, '3.5', 'switch_to_blog()' ); 694 | } 695 | 696 | /** 697 | * Sets the data contents into the cache 698 | * 699 | * The cache contents is grouped by the $group parameter followed by the 700 | * $key. This allows for duplicate ids in unique groups. Therefore, naming of 701 | * the group should be used with care and should follow normal function 702 | * naming guidelines outside of core WordPress usage. 703 | * 704 | * The $expire parameter is not used, because the cache will automatically 705 | * expire for each time a page is accessed and PHP finishes. The method is 706 | * more for cache plugins which use files. 707 | * 708 | * @param int|string $key What to call the contents in the cache 709 | * @param mixed $data The contents to store in the cache 710 | * @param string $group Where to group the cache contents 711 | * @param int $expire TTL for the data, in seconds 712 | * @return bool Always returns true 713 | */ 714 | public function set( $key, $data, $group = 'default', $expire = 0 ) { 715 | 716 | if ( empty( $group ) ) { 717 | $group = 'default'; 718 | } 719 | 720 | if ( is_object( $data ) ) { 721 | $data = clone $data; 722 | } 723 | 724 | $this->_set_internal( $key, $group, $data ); 725 | 726 | if ( ! $this->_should_persist( $group ) ) { 727 | return true; 728 | } 729 | 730 | // If this is an integer, store it as such. Otherwise, serialize it. 731 | if ( ! is_numeric( $data ) || intval( $data ) !== $data ) { 732 | $data = serialize( $data ); 733 | } 734 | 735 | // Redis doesn't support expire on hash group keys 736 | if ( self::USE_GROUPS ) { 737 | $redis_safe_group = $this->_key( '', $group ); 738 | $this->_call_redis( 'hSet', $redis_safe_group, $key, $data ); 739 | return true; 740 | } 741 | 742 | $id = $this->_key( $key, $group ); 743 | if ( empty( $expire ) ) { 744 | $this->_call_redis( 'set', $id, $data ); 745 | } else { 746 | $this->_call_redis( 'setex', $id, $expire, $data ); 747 | } 748 | return true; 749 | } 750 | 751 | /** 752 | * Echoes the stats of the caching. 753 | * 754 | * Gives the cache hits, and cache misses. Also prints every cached group, 755 | * key and the data. 756 | */ 757 | public function stats() { 758 | 759 | echo '

'; 760 | echo 'Cache Hits:' . (int) $this->cache_hits . '
'; 761 | echo 'Cache Misses:' . (int) $this->cache_misses . '
'; 762 | echo '

'; 763 | echo '
    '; 764 | foreach ( $this->cache as $group => $cache ) { 765 | echo '
  • Group: ' . esc_html( $group ) . ' - ( ' . number_format( strlen( serialize( $cache ) ) / 1024, 2 ) . 'k )
  • '; 766 | } 767 | echo '
'; 768 | } 769 | 770 | /** 771 | * Switch the interal blog id. 772 | * 773 | * This changes the blog id used to create keys in blog specific groups. 774 | * 775 | * @param int $blog_id Blog ID 776 | */ 777 | public function switch_to_blog( $blog_id ) { 778 | 779 | $blog_id = (int) $blog_id; 780 | $this->blog_prefix = $this->multisite ? $blog_id . ':' : ''; 781 | } 782 | 783 | /** 784 | * Utility function to determine whether a key exists in the cache. 785 | * 786 | * @access protected 787 | */ 788 | protected function _exists( $key, $group ) { 789 | 790 | if ( $this->_isset_internal( $key, $group ) ) { 791 | return true; 792 | } 793 | if ( self::USE_GROUPS ) { 794 | $redis_safe_group = $this->_key( '', $group ); 795 | return $this->_call_redis( 'hExists', $redis_safe_group, $key ); 796 | } 797 | $id = $this->_key( $key, $group ); 798 | return $this->_call_redis( 'exists', $id ); 799 | } 800 | 801 | /** 802 | * Check whether there's a value in the internal object cache. 803 | * 804 | * @param string $key 805 | * @param string $group 806 | * @return boolean 807 | */ 808 | protected function _isset_internal( $key, $group ) { 809 | 810 | if ( self::USE_GROUPS ) { 811 | $multisite_safe_group = $this->multisite && ! isset( $this->global_groups[ $group ] ) ? $this->blog_prefix . $group : $group; 812 | return isset( $this->cache[ $multisite_safe_group ][ $key ] ); 813 | } else { 814 | $key = $this->_key( $key, $group ); 815 | return isset( $this->cache[ $key ] ); 816 | } 817 | } 818 | 819 | /** 820 | * Get a value from the internal object cache 821 | * 822 | * @param string $key 823 | * @param string $group 824 | * @return mixed 825 | */ 826 | protected function _get_internal( $key, $group ) { 827 | 828 | $value = null; 829 | if ( self::USE_GROUPS ) { 830 | $multisite_safe_group = $this->multisite && ! isset( $this->global_groups[ $group ] ) ? $this->blog_prefix . $group : $group; 831 | if ( isset( $this->cache[ $multisite_safe_group ][ $key ] ) ) { 832 | $value = $this->cache[ $multisite_safe_group ][ $key ]; 833 | } 834 | } else { 835 | $key = $this->_key( $key, $group ); 836 | if ( isset( $this->cache[ $key ] ) ) { 837 | $value = $this->cache[ $key ]; 838 | } 839 | } 840 | if ( is_object( $value ) ) { 841 | return clone $value; 842 | } 843 | return $value; 844 | } 845 | 846 | /** 847 | * Set a value to the internal object cache 848 | * 849 | * @param string $key 850 | * @param string $group 851 | * @param mixed $value 852 | */ 853 | protected function _set_internal( $key, $group, $value ) { 854 | 855 | // Redis converts null to an empty string 856 | if ( is_null( $value ) ) { 857 | $value = ''; 858 | } 859 | if ( self::USE_GROUPS ) { 860 | $multisite_safe_group = $this->multisite && ! isset( $this->global_groups[ $group ] ) ? $this->blog_prefix . $group : $group; 861 | if ( ! isset( $this->cache[ $multisite_safe_group ] ) ) { 862 | $this->cache[ $multisite_safe_group ] = array(); 863 | } 864 | $this->cache[ $multisite_safe_group ][ $key ] = $value; 865 | } else { 866 | $key = $this->_key( $key, $group ); 867 | $this->cache[ $key ] = $value; 868 | } 869 | } 870 | 871 | /** 872 | * Unset a value from the internal object cache 873 | * 874 | * @param string $key 875 | * @param string $group 876 | */ 877 | protected function _unset_internal( $key, $group ) { 878 | 879 | if ( self::USE_GROUPS ) { 880 | $multisite_safe_group = $this->multisite && ! isset( $this->global_groups[ $group ] ) ? $this->blog_prefix . $group : $group; 881 | if ( isset( $this->cache[ $multisite_safe_group ][ $key ] ) ) { 882 | unset( $this->cache[ $multisite_safe_group ][ $key ] ); 883 | } 884 | } else { 885 | $key = $this->_key( $key, $group ); 886 | if ( isset( $this->cache[ $key ] ) ) { 887 | unset( $this->cache[ $key ] ); 888 | } 889 | } 890 | } 891 | 892 | /** 893 | * Utility function to generate the redis key for a given key and group. 894 | * 895 | * @param string $key The cache key. 896 | * @param string $group The cache group. 897 | * @return string A properly prefixed redis cache key. 898 | */ 899 | protected function _key( $key = '', $group = 'default' ) { 900 | 901 | if ( empty( $group ) ) { 902 | $group = 'default'; 903 | } 904 | 905 | if ( ! empty( $this->global_groups[ $group ] ) ) { 906 | $prefix = $this->global_prefix; 907 | } else { 908 | $prefix = $this->blog_prefix; 909 | } 910 | 911 | return preg_replace( '/\s+/', '', WP_CACHE_KEY_SALT . "$prefix$group:$key" ); 912 | } 913 | 914 | /** 915 | * Does this group use persistent storage? 916 | * 917 | * @param string $group Cache group. 918 | * @return bool true if the group is persistent, false if not. 919 | */ 920 | protected function _should_persist( $group ) { 921 | 922 | return empty( $this->non_persistent_groups[ $group ] ); 923 | } 924 | 925 | /** 926 | * Wrapper method for connecting to Redis, which lets us retry the connection 927 | */ 928 | protected function _connect_redis() { 929 | 930 | global $redis_server; 931 | 932 | if ( ! class_exists( 'Redis' ) ) { 933 | $this->is_redis_connected = false; 934 | return $this->is_redis_connected; 935 | } 936 | 937 | if ( empty( $redis_server ) ) { 938 | // Attempt to automatically load Pantheon's Redis config from the env. 939 | if ( isset( $_SERVER['CACHE_HOST'] ) ) { 940 | $redis_server = array( 941 | 'host' => $_SERVER['CACHE_HOST'], 942 | 'port' => $_SERVER['CACHE_PORT'], 943 | 'auth' => $_SERVER['CACHE_PASSWORD'], 944 | ); 945 | } else { 946 | $redis_server = array( 947 | 'host' => '127.0.0.1', 948 | 'port' => 6379, 949 | ); 950 | } 951 | } 952 | 953 | $this->redis = new Redis; 954 | 955 | if ( file_exists( $redis_server['host'] ) && 'socket' === filetype( $redis_server['host'] ) ) { //unix socket connection 956 | //port must be null or socket won't connect 957 | $port = null; 958 | } else { //tcp connection 959 | $port = ! empty( $redis_server['port'] ) ? $redis_server['port'] : 6379; 960 | } 961 | $this->redis->connect( $redis_server['host'], $port, 1, null, 100 ); // 1s timeout, 100ms delay between reconnections 962 | if ( ! empty( $redis_server['auth'] ) ) { 963 | try { 964 | $this->redis->auth( $redis_server['auth'] ); 965 | } catch ( RedisException $e ) { 966 | // PhpRedis throws an Exception when it fails a server call. 967 | // To prevent WordPress from fataling, we catch the Exception. 968 | try { 969 | $this->last_triggered_error = 'WP Redis: ' . $e->getMessage(); 970 | // Be friendly to developers debugging production servers by triggering an error 971 | // @codingStandardsIgnoreStart 972 | trigger_error( $this->last_triggered_error, E_USER_WARNING ); 973 | // @codingStandardsIgnoreEnd 974 | } catch ( PHPUnit_Framework_Error_Warning $e ) { 975 | // PHPUnit throws an Exception when `trigger_error()` is called. 976 | // To ensure our tests (which expect Exceptions to be caught) continue to run, 977 | // we catch the PHPUnit exception and inspect the RedisException message 978 | } 979 | } 980 | } 981 | $this->is_redis_connected = $this->redis->isConnected(); 982 | 983 | return $this->is_redis_connected; 984 | } 985 | 986 | /** 987 | * Wrapper method for calls to Redis, which fails gracefully when Redis is unavailable 988 | * 989 | * @param string $method 990 | * @param mixed $args 991 | * @return mixed 992 | */ 993 | protected function _call_redis( $method ) { 994 | 995 | global $wpdb; 996 | 997 | $arguments = func_get_args(); 998 | array_shift( $arguments ); // ignore $method 999 | 1000 | // $group is intended for the failback, and isn't passed to the Redis callback 1001 | if ( 'hIncrBy' === $method ) { 1002 | $group = array_pop( $arguments ); 1003 | } 1004 | 1005 | if ( $this->is_redis_connected ) { 1006 | try { 1007 | $retval = call_user_func_array( array( $this->redis, $method ), $arguments ); 1008 | return $retval; 1009 | } catch ( RedisException $e ) { 1010 | 1011 | try { 1012 | $this->last_triggered_error = 'WP Redis: ' . $e->getMessage(); 1013 | // Be friendly to developers debugging production servers by triggering an error 1014 | // @codingStandardsIgnoreStart 1015 | trigger_error( $this->last_triggered_error, E_USER_WARNING ); 1016 | // @codingStandardsIgnoreEnd 1017 | } catch ( PHPUnit_Framework_Error_Warning $e ) { 1018 | // PHPUnit throws an Exception when `trigger_error()` is called. 1019 | // To ensure our tests (which expect Exceptions to be caught) continue to run, 1020 | // we catch the PHPUnit exception and inspect the RedisException message 1021 | } 1022 | // Attempt to refresh the connection if it was successfully established once 1023 | // $this->is_redis_connected will be set inside _connect_redis() 1024 | if ( $this->_connect_redis() ) { 1025 | return call_user_func_array( array( $this, '_call_redis' ), array_merge( array( $method ), $arguments ) ); 1026 | } 1027 | 1028 | } 1029 | } 1030 | 1031 | // Mock expected behavior from Redis for these methods 1032 | switch ( $method ) { 1033 | case 'incr': 1034 | case 'incrBy': 1035 | $val = $this->cache[ $arguments[0] ]; 1036 | $offset = isset( $arguments[1] ) && 'incrBy' === $method ? $arguments[1] : 1; 1037 | $val = $val + $offset; 1038 | return $val; 1039 | case 'hIncrBy': 1040 | $val = $this->_get_internal( $arguments[1], $group ); 1041 | return $val + $arguments[2]; 1042 | case 'decrBy': 1043 | case 'decr': 1044 | $val = $this->cache[ $arguments[0] ]; 1045 | $offset = isset( $arguments[1] ) && 'decrBy' === $method ? $arguments[1] : 1; 1046 | $val = $val - $offset; 1047 | return $val; 1048 | case 'delete': 1049 | case 'hDel': 1050 | return 1; 1051 | case 'flushAll': 1052 | case 'IsConnected': 1053 | case 'exists': 1054 | return false; 1055 | } 1056 | 1057 | } 1058 | 1059 | /** 1060 | * Sets up object properties; PHP 5 style constructor 1061 | * 1062 | * @return null|WP_Object_Cache If cache is disabled, returns null. 1063 | */ 1064 | public function __construct() { 1065 | global $table_prefix; 1066 | 1067 | $this->multisite = is_multisite(); 1068 | $this->blog_prefix = $this->multisite ? $blog_id . ':' : ''; 1069 | 1070 | $this->global_prefix = ( $this->multisite || defined( 'CUSTOM_USER_TABLE' ) && defined( 'CUSTOM_USER_META_TABLE' ) ) ? '' : $table_prefix; 1071 | 1072 | 1073 | $this->_connect_redis(); 1074 | 1075 | /** 1076 | * @todo This should be moved to the PHP4 style constructor, PHP5 1077 | * already calls __destruct() 1078 | */ 1079 | register_shutdown_function( array( $this, '__destruct' ) ); 1080 | } 1081 | 1082 | /** 1083 | * Will save the object cache before object is completely destroyed. 1084 | * 1085 | * Called upon object destruction, which should be when PHP ends. 1086 | * 1087 | * @return bool True value. Won't be used by PHP 1088 | */ 1089 | public function __destruct() { 1090 | 1091 | return true; 1092 | } 1093 | } -------------------------------------------------------------------------------- /roles/wordpress/templates/wp-config.php: -------------------------------------------------------------------------------- 1 | array('127.0.0.1:11211')); 84 | 85 | /* That's all, stop editing! Happy blogging. */ 86 | 87 | /** Absolute path to the WordPress directory. */ 88 | if ( !defined('ABSPATH') ) 89 | define('ABSPATH', dirname(__FILE__) . '/'); 90 | 91 | /** Sets up WordPress vars and included files. */ 92 | require_once(ABSPATH . 'wp-settings.php'); 93 | -------------------------------------------------------------------------------- /site.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install WordPress, MySQL, Nginx, and PHP-FPM 3 | hosts: all 4 | remote_user: "{{remote_user}}" 5 | sudo: yes 6 | 7 | roles: 8 | - common 9 | - ufw 10 | - mariadb 11 | - nginx 12 | - postfix 13 | - php-fpm 14 | - redis 15 | - wordpress 16 | - webgrind 17 | - backup 18 | - sftp 19 | - newrelic 20 | - letsencrypt 21 | --------------------------------------------------------------------------------