wordpress-charmers team mailing list archive
-
wordpress-charmers team
-
Mailing list archive
-
Message #00089
[Merge] ~barryprice/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master into ~wordpress-charmers/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master
Barry Price has proposed merging ~barryprice/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master into ~wordpress-charmers/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master.
Commit message:
Adding plugin handling for Akismet and Launchpad OpenID integration
Requested reviews:
Wordpress Charmers (wordpress-charmers)
For more details, see:
https://code.launchpad.net/~barryprice/charm-k8s-wordpress/+git/wordpress-k8s-image-builder/+merge/380153
--
Your team Wordpress Charmers is requested to review the proposed merge of ~barryprice/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master into ~wordpress-charmers/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master.
diff --git a/.gitignore b/.gitignore
index e0f7f96..d8aaacd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
files/plugins/
files/themes/
+.coverage
.tox/
+files/__pycache__/
+tests/unit/__pycache__/
diff --git a/Dockerfile b/Dockerfile
index 24b6b0b..c866c98 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -19,7 +19,8 @@ RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selectio
# Update all packages, remove cruft, install required packages, configure apache
RUN apt-get update && apt-get -y dist-upgrade \
&& apt-get --purge autoremove -y \
- && apt-get install -y apache2 php libapache2-mod-php php-mysql php-gd curl ssl-cert pwgen \
+ && apt-get install -y apache2 php libapache2-mod-php php-mysql php-gd \
+ curl ssl-cert pwgen python3 python3-yaml php-symfony-yaml \
&& sed -ri 's/^export ([^=]+)=(.*)$/: ${\1:=\2}\nexport \1/' "$APACHE_ENVVARS" \
&& . "$APACHE_ENVVARS" \
&& for dir in "$APACHE_LOCK_DIR" "$APACHE_RUN_DIR" "$APACHE_LOG_DIR"; do rm -rvf "$dir"; mkdir -p "$dir"; chown "$APACHE_RUN_USER:$APACHE_RUN_GROUP" "$dir"; chmod 777 "$dir"; done \
@@ -47,13 +48,22 @@ COPY --chown=www-data:www-data ./files/plugins/ /var/www/html/wp-content/plugins
COPY --chown=www-data:www-data ./files/themes/ /var/www/html/wp-content/themes/
# wp-info.php contains template variables which our ENTRYPOINT script will populate
-COPY ./files/wp-info.php /var/www/html/
-COPY ./files/wp-config.php /var/www/html/
+COPY ./files/wp-info.php ./files/wp-config.php /var/www/html/
+
+# Copy our helper scripts and their wrapper into their own directory
+COPY ./files/_add_option.php \
+ ./files/_enable_plugin.php \
+ ./files/_get_option.php \
+ ./files/plugin_handler.py \
+ /srv/wordpress-helpers/
+
+# Make the wrapper executable
+RUN chmod 0755 /srv/wordpress-helpers/plugin_handler.py
# entrypoint script will configure Wordpress based on env variables
COPY ./files/docker-entrypoint.sh /usr/local/bin/
-
RUN chmod 0755 /usr/local/bin/docker-entrypoint.sh
+
# Port 80 only, TLS will terminate elsewhere
EXPOSE 80
diff --git a/Makefile b/Makefile
index 36c7f00..a3cddc9 100644
--- a/Makefile
+++ b/Makefile
@@ -24,12 +24,18 @@ lint: clean
@echo "Running flake8"
@tox -e lint
+test: lint
+ @echo "Running unit tests"
+ @tox -e unit
+
clean:
@echo "Cleaning files"
@rm -rf ./.tox
@rm -rf ./.pytest_cache
@rm -rf ./files/plugins/*
@rm -rf ./files/themes/*
+ @rm -rf ./files/__pycache__
+ @rm -rf ./tests/unit/__pycache__
@mkdir -p ./files/plugins
@mkdir -p ./files/themes
diff --git a/files/_add_option.php b/files/_add_option.php
new file mode 100644
index 0000000..cbfe8a8
--- /dev/null
+++ b/files/_add_option.php
@@ -0,0 +1,11 @@
+<?php
+
+# PHP cli helper which adds new option to wp_settings table
+#
+# Example use:
+# php ./_add_option.php akismet_strictness 0
+
+@include "wp-config.php";
+@include_once "wp-includes/option.php";
+@include_once "wp-includes/functions.php";
+add_option($argv[1], maybe_unserialize($argv[2]));
diff --git a/files/_enable_plugin.php b/files/_enable_plugin.php
new file mode 100644
index 0000000..11ed3e7
--- /dev/null
+++ b/files/_enable_plugin.php
@@ -0,0 +1,14 @@
+<?php
+
+# PHP cli helper which enables a plugin. Plugin name is in
+# format returned by _list_inactive_plugins.php helper
+#
+# Example use:
+# php ./_enable_plugin.php akismet/akismet.php openid/openid.php
+
+@include "wp-config.php";
+@include_once "wp-admin/includes/plugin.php";
+$result = activate_plugins(array_slice($argv, 1));
+if ( is_wp_error( $result ) ) {
+ throw new Exception($result);
+}
diff --git a/files/_get_option.php b/files/_get_option.php
new file mode 100644
index 0000000..4168301
--- /dev/null
+++ b/files/_get_option.php
@@ -0,0 +1,15 @@
+<?php
+
+# PHP cli helper which returns option value if present
+#
+# Example use:
+# php ./_get_option.php akismet_strictness
+
+@include "wp-config.php";
+@include_once "wp-includes/option.php";
+@include_once "/usr/share/php/Symfony/Component/Yaml/autoload.php";
+
+use Symfony\Component\Yaml\Yaml;
+
+$value = get_option($argv[1]);
+print Yaml::dump($value);
diff --git a/files/docker-entrypoint.sh b/files/docker-entrypoint.sh
index 5f67d9d..136a0d5 100644
--- a/files/docker-entrypoint.sh
+++ b/files/docker-entrypoint.sh
@@ -11,4 +11,6 @@ do
sed -i -e "s/%%%${key}%%%/$(pwgen 64 1)/" /var/www/html/wp-info.php
done
+nohup bash -c "/srv/wordpress-helpers/plugin_handler.py &"
+
exec "$@"
diff --git a/files/plugin_handler.py b/files/plugin_handler.py
new file mode 100644
index 0000000..e3c8f93
--- /dev/null
+++ b/files/plugin_handler.py
@@ -0,0 +1,117 @@
+#!/usr/bin/python3
+
+import logging
+import os
+import subprocess
+import urllib.request
+from time import sleep
+from yaml import safe_load
+
+helpers_path = '/srv/wordpress-helpers'
+install_path = '/var/www/html'
+
+
+def call_php_helper(helper, stdin='', *args):
+ path = os.path.join(helpers_path, helper)
+ cmd = ['php', path]
+ cmd.extend([str(arg) for arg in args])
+ logging.info(cmd)
+ process = subprocess.Popen(
+ cmd,
+ cwd=install_path,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ )
+ return process.communicate(stdin)[0] # spit back stdout+stderr combined
+
+
+def enable_plugin(*plugins):
+ logging.info("Enabling plugins: {}".format(plugins))
+ logging.info(call_php_helper('_enable_plugin.php', '', *plugins))
+
+
+def get_option(key):
+ value = call_php_helper('_get_option.php', '', key)
+ return safe_load(value)
+
+
+def add_option(key, value):
+ # Ensure we don't overwrite settings
+ if not get_option(key):
+ logging.info('Adding option: {}'.format(key))
+ call_php_helper('_add_option.php', '', key, value)
+ else:
+ logging.info('Option "{}" already in place, skipping.'.format(key))
+
+
+def encode_team_map(team_map):
+ # example: site-sysadmins=administrator,site-editors=editor,site-executives=editor
+ team_map_lines = []
+ i = 0
+ team_map_lines.append("a:{}:{{".format(len(team_map.split(','))))
+ for mapping in team_map.split(','):
+ i = i + 1
+ team, role = mapping.split('=', 2)
+ team_map_lines.append('i:{};'.format(i))
+ team_map_lines.append('O:8:"stdClass":4:{')
+ team_map_lines.append('s:2:"id";')
+ team_map_lines.append('i:{};'.format(i))
+ team_map_lines.append('s:4:"team";')
+ team_map_lines.append('s:{}:"{}";'.format(len(team), team))
+ team_map_lines.append('s:4:"role";')
+ team_map_lines.append('s:{}:"{}";'.format(len(role), role))
+ team_map_lines.append('s:6:"server";')
+ team_map_lines.append('s:1:"0";')
+ team_map_lines.append('}')
+ team_map_lines.append('}')
+
+ return ''.join(team_map_lines)
+
+
+def enable_akismet(key):
+ enable_plugin('akismet/akismet.php')
+ add_option('akismet_strictness', '0')
+ add_option('akismet_show_user_comments_approved', '0')
+ add_option('wordpress_api_key', key)
+
+
+def enable_openid(team_map):
+ encoded_team_map = encode_team_map(team_map)
+ enable_plugin('openid/openid.php')
+ add_option('openid_required_for_registration', '1')
+ add_option('openid_teams_trust_list', encoded_team_map)
+
+
+def wait_for_wordpress():
+ url = 'http://localhost'
+ sleep_time = 10
+ success = False
+ while success is not True:
+ try:
+ response = urllib.request.urlopen(url, timeout=sleep_time)
+ except Exception:
+ logging.info('Waiting for Wordpress to accept connections')
+ sleep(sleep_time)
+ else:
+ if response.status == 200:
+ success = True
+ else:
+ logging.info('Waiting for Wordpress to return HTTP 200 (got {})'.format(response.status))
+ sleep(sleep_time)
+ return True
+
+
+if __name__ == '__main__':
+ logger = logging.getLogger(__name__)
+ logging.basicConfig(filename='/var/log/wordpress-plugin-handler.log', level=logging.DEBUG)
+
+ wait_for_wordpress()
+
+ key = os.getenv('WP_PLUGIN_AKISMET_KEY')
+ if key:
+ enable_akismet(key)
+
+ team_map = os.getenv('WP_PLUGIN_OPENID_TEAM_MAP')
+ if team_map:
+ enable_openid(team_map)
diff --git a/tests/unit/requirements.txt b/tests/unit/requirements.txt
new file mode 100644
index 0000000..092d46a
--- /dev/null
+++ b/tests/unit/requirements.txt
@@ -0,0 +1,3 @@
+pytest
+pytest-cov
+PyYAML
diff --git a/tests/unit/test_plugin_hander.py b/tests/unit/test_plugin_hander.py
new file mode 100644
index 0000000..d72b865
--- /dev/null
+++ b/tests/unit/test_plugin_hander.py
@@ -0,0 +1,19 @@
+import os
+import sys
+import unittest
+from unittest import mock
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))))
+from files import plugin_handler # NOQA: E402
+
+
+class testWrapper(unittest.TestCase):
+ def setUp(self):
+ self.maxDiff = None
+
+ @mock.patch("files.plugin_handler.logging")
+ def test_team_mapper(self, foo):
+ given = "canonical-sysadmins=administrator,canonical-website-editors=editor,canonical-website-admins=administrator,launchpad=editor" # NOQA: E501
+ want = """a:4:{i:1;O:8:"stdClass":4:{s:2:"id";i:1;s:4:"team";s:19:"canonical-sysadmins";s:4:"role";s:13:"administrator";s:6:"server";s:1:"0";}i:2;O:8:"stdClass":4:{s:2:"id";i:2;s:4:"team";s:25:"canonical-website-editors";s:4:"role";s:6:"editor";s:6:"server";s:1:"0";}i:3;O:8:"stdClass":4:{s:2:"id";i:3;s:4:"team";s:24:"canonical-website-admins";s:4:"role";s:13:"administrator";s:6:"server";s:1:"0";}i:4;O:8:"stdClass":4:{s:2:"id";i:4;s:4:"team";s:9:"launchpad";s:4:"role";s:6:"editor";s:6:"server";s:1:"0";}}""" # NOQA: E501
+ got = plugin_handler.encode_team_map(given)
+ self.assertTrue(got == want)
diff --git a/tox.ini b/tox.ini
index 4c0b186..c03998c 100644
--- a/tox.ini
+++ b/tox.ini
@@ -22,6 +22,14 @@ passenv = HTTP_PROXY HTTPS_PROXY
setenv =
BZR_HOME = /tmp
+[testenv:unit]
+commands =
+ pytest {posargs:-v --cov=. --cov-report=term-missing --cov-branch}
+deps = -r{toxinidir}/tests/unit/requirements.txt
+setenv =
+ PYTHONPATH={toxinidir}/lib
+ TZ=UTC
+
[flake8]
exclude =
.git,
Follow ups
-
[Merge] ~barryprice/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master into ~wordpress-charmers/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master
From: noreply, 2020-03-23
-
Re: [Merge] ~barryprice/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master into ~wordpress-charmers/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master
From: Canonical IS Mergebot, 2020-03-23
-
[Merge] ~barryprice/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master into ~wordpress-charmers/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master
From: Tom Haddon, 2020-03-23
-
Re: [Merge] ~barryprice/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master into ~wordpress-charmers/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master
From: Barry Price, 2020-03-23
-
Re: [Merge] ~barryprice/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master into ~wordpress-charmers/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master
From: Tom Haddon, 2020-03-10
-
Re: [Merge] ~barryprice/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master into ~wordpress-charmers/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master
From: Stuart Bishop, 2020-03-10
-
Re: [Merge] ~barryprice/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master into ~wordpress-charmers/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master
From: Stuart Bishop, 2020-03-10
-
Re: [Merge] ~barryprice/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master into ~wordpress-charmers/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master
From: Barry Price, 2020-03-09
-
Re: [Merge] ~barryprice/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master into ~wordpress-charmers/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master
From: Tom Haddon, 2020-03-06
-
Re: [Merge] ~barryprice/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master into ~wordpress-charmers/charm-k8s-wordpress/+git/wordpress-k8s-image-builder:master
From: Canonical IS Mergebot, 2020-03-06