From 768f69889bb70c7581afa8985eda1d837c5d095d Mon Sep 17 00:00:00 2001 From: Diego Lendoiro Date: Thu, 11 Jan 2018 10:02:45 +0100 Subject: [PATCH] initial Dockerfile tests --- Dockerfile | 8 +- bin/docker-entrypoint.sh | 67 +++++++++------- conf/supervisord.conf | 36 +++++++++ spec/docker_image/Dockerfile_spec.rb | 93 ++++++++++++++++++++++ spec/passbolt_api/Dockerfile_spec.rb | 111 --------------------------- 5 files changed, 175 insertions(+), 140 deletions(-) create mode 100644 conf/supervisord.conf create mode 100644 spec/docker_image/Dockerfile_spec.rb delete mode 100644 spec/passbolt_api/Dockerfile_spec.rb diff --git a/Dockerfile b/Dockerfile index 0ea71f5..5864d1e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,7 @@ ENV PASSBOLT_URL https://github.com/passbolt/passbolt_api/archive/v${PASSBOLT_VE ARG PHP_EXTENSIONS="gd \ intl \ + pdo_mysql \ xsl" ARG PHP_GNUPG_BUILD_DEPS="php7-dev \ @@ -22,8 +23,6 @@ ARG PHP_GNUPG_BUILD_DEPS="php7-dev \ file" RUN apk add --no-cache $PHP_GNUPG_BUILD_DEPS \ - sed \ - bash \ nginx \ gpgme \ gnupg1 \ @@ -43,7 +42,10 @@ RUN apk add --no-cache $PHP_GNUPG_BUILD_DEPS \ COPY src/passbolt_api/ /var/www/passbolt/ # && curl -sSL $PASSBOLT_URL | tar zxf - -C /var/www/passbolt --strip-components 1 \ -RUN chown -R nginx:nginx /var/www/passbolt \ +RUN cd /var/www/passbolt \ + && composer global require hirak/prestissimo \ + && composer install \ + && chown -R nginx:nginx /var/www/passbolt \ && chmod -R o-w /var/www/passbolt \ && chmod -R +w /var/www/passbolt/tmp \ && chmod -R +w /var/www/passbolt/webroot/img/public diff --git a/bin/docker-entrypoint.sh b/bin/docker-entrypoint.sh index f55d543..4a1556f 100755 --- a/bin/docker-entrypoint.sh +++ b/bin/docker-entrypoint.sh @@ -1,20 +1,19 @@ -#!/bin/bash +#!/usr/bin/env sh set -eo pipefail base_path='/var/www/passbolt' -gpg_private_key="$base_path/config/gpg/serverkey.private.asc" +gpg_private_key="$base_path/config/gpg/serverkey_private.asc" gpg_public_key="$base_path/config/gpg/serverkey.asc" -app_config="$base_path/config/app.php" ssl_key='/etc/ssl/certs/certificate.key' ssl_cert='/etc/ssl/certs/certificate.crt' gpg_gen_key() { - local key_email="${KEY_EMAIL:-passbolt@yourdomain.com}" - local key_name="${KEY_NAME:-Passbolt default user}" - local key_length="${KEY_LENGTH:-4096}" - local subkey_length="${SUBKEY_LENGTH:-4096}" - local expiration="${KEY_EXPIRATION:-0}" + key_email="${KEY_EMAIL:-passbolt@yourdomain.com}" + key_name="${KEY_NAME:-Passbolt default user}" + key_length="${KEY_LENGTH:-2048}" + subkey_length="${SUBKEY_LENGTH:-2048}" + expiration="${KEY_EXPIRATION:-0}" su -m -c "gpg --batch --gen-key < $gpg_private_key" -ls /bin/bash nginx - su -m -c "gpg --armor --export $key_email > $gpg_public_key" -ls /bin/bash nginx + su -m -c "gpg --armor --export-secret-keys $key_email > $gpg_private_key" -ls /bin/sh nginx + su -m -c "gpg --armor --export $key_email > $gpg_public_key" -ls /bin/sh nginx } gpg_import_key() { - local key_id="" - key_id=$(su -m -c "gpg --with-colons $gpg_private_key | grep sec |cut -f5 -d:" -ls /bin/bash nginx) - su -m -c "gpg --batch --import $gpg_public_key" -ls /bin/bash nginx - su -m -c "gpg -K $key_id" -ls /bin/bash nginx || su -m -c "gpg --batch --import $gpg_private_key" -ls /bin/bash nginx + key_id="" + key_id=$(su -m -c "gpg --with-colons $gpg_private_key | grep sec |cut -f5 -d:" -ls /bin/sh nginx) + su -m -c "gpg --batch --import $gpg_public_key" -ls /bin/sh nginx + su -m -c "gpg -K $key_id" -ls /bin/sh nginx || su -m -c "gpg --batch --import $gpg_private_key" -ls /bin/sh nginx } gen_ssl_cert() { @@ -45,30 +44,44 @@ gen_ssl_cert() { } install() { - if [ ! -f $app_config ] && [ ! -L $app_config ]; then - cp $base_path/config/app.default.php $app_config - fi - tables=$(mysql -u "$DATABASE_USER" -h "$DB_HOST" -P "$DB_PORT" -p -BN -e "SHOW TABLES FROM $DB_NAME" -p"$DB_PASS" |wc -l) + tables="" + tables=$(mysql \ + -u "$DATASOURCES_DEFAULT_USERNAME" \ + -h "$DATASOURCES_DEFAULT_HOST" \ + -P "$DATASOURCES_DEFAULT_PORT" \ + -BN -e "SHOW TABLES FROM $DATASOURCES_DEFAULT_DATABASE" \ + -p"$DATASOURCES_DEFAULT_PASSWORD" |wc -l) + if [ "$tables" -eq 0 ]; then - su -c "/var/www/passbolt/bin/cake passbolt install --no-admin" -ls /bin/bash nginx + su -c "cp /var/www/passbolt/config/app.default.php /var/www/passbolt/config/app.php" -s /bin/sh nginx + su -m -c "PATH=$PATH:/usr/local/bin /var/www/passbolt/bin/cake passbolt install --no-admin --force" -s /bin/sh nginx else echo "Enjoy! ☮" fi } email_cron_job() { - local root_crontab='/etc/crontabs/root' - local cron_task_dir='/etc/periodic/1min' - local cron_task='/etc/periodic/1min/email_queue_processing' - local process_email="/var/www/passbolt/app/Console/cake EmailQueue.sender --quiet" + root_crontab='/etc/crontabs/root' + cron_task_dir='/etc/periodic/1min' + cron_task='/etc/periodic/1min/email_queue_processing' + process_email="PATH=$PATH:/usr/local/bin /var/www/passbolt/app/Console/cake EmailQueue.sender --quiet" mkdir -p $cron_task_dir echo "* * * * * run-parts $cron_task_dir" >> $root_crontab echo "#!/bin/sh" > $cron_task chmod +x $cron_task - echo "su -c \"$process_email\" -ls /bin/bash nginx" >> $cron_task + echo "su -c \"$process_email\" -s /bin/sh nginx" >> $cron_task } +if [ -z "$DATASOURCES_DEFAULT_HOST" ] \ + && [ -z "$DATASOURCES_DEFAULT_USERNAME" ] \ + && [ -z "$DATASOURCES_DEFAULT_PASSWORD" ] \ + && [ -z "$DATASOURCES_DEFAULT_DATABASE" ]; then + echo >&2 'Error: database credentials not provided' + echo >&2 'You must provide database details: hostname, username and password' + exit 1 +fi + if [ ! -f $gpg_private_key ] && [ ! -L $gpg_private_key ] || \ [ ! -f $gpg_public_key ] && [ ! -L $gpg_public_key ]; then gpg_gen_key @@ -82,7 +95,9 @@ if [ ! -f $ssl_key ] && [ ! -L $ssl_key ] && \ gen_ssl_cert fi -#gpg_auto_fingerprint=$(gpg --fingerprint "$key_email" | grep fingerprint | awk '{for(i=4;i<=NF;++i)printf \$i}') +gpg_auto_fingerprint=$(su -m -c "gpg --with-fingerprint $gpg_public_key | grep fingerprint | awk '{for(i=4;i<=NF;++i)printf \$i}'" -ls /bin/sh nginx) +export PASSBOLT_GPG_SERVER_KEY_FINGERPRINT=$gpg_auto_fingerprint install email_cron_job + /usr/bin/supervisord -n -c /etc/supervisord.conf diff --git a/conf/supervisord.conf b/conf/supervisord.conf new file mode 100644 index 0000000..9cd1f5a --- /dev/null +++ b/conf/supervisord.conf @@ -0,0 +1,36 @@ +[unix_http_server] +file=/tmp/supervisor.sock ; (the path to the socket file) + +[supervisord] +logfile=/tmp/supervisord.log ; (main log file;default $CWD/supervisord.log) +logfile_maxbytes=50MB ; (max main logfile bytes b4 rotation;default 50MB) +logfile_backups=10 ; (num of main logfile rotation backups;default 10) +loglevel=info ; (log level;default info; others: debug,warn,trace) +pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid) +nodaemon=false ; (start in foreground if true;default false) +minfds=1024 ; (min. avail startup file descriptors;default 1024) +minprocs=200 ; (min. avail process descriptors;default 200) + +; the below section must remain in the config file for RPC +; (supervisorctl/web interface) to work, additional interfaces may be +; added by defining them in separate rpcinterface: sections +[rpcinterface:supervisor] +supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface + +[supervisorctl] +serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket + +[program:php-fpm] +command=php-fpm +autostart=true +priority=5 + +[program:nginx] +command=nginx -g "pid /tmp/nginx.pid; daemon off;" +autostart=true +priority=10 + +[program:crond] +command=crond -f -c /etc/crontabs +autostart=true +priority=20 diff --git a/spec/docker_image/Dockerfile_spec.rb b/spec/docker_image/Dockerfile_spec.rb new file mode 100644 index 0000000..387295d --- /dev/null +++ b/spec/docker_image/Dockerfile_spec.rb @@ -0,0 +1,93 @@ +require 'spec_helper' + +describe 'Dockerfile' do + + before(:all) do + set :env, { + 'DATASOURCES_DEFAULT_HOST' => '172.17.0.2', + 'DATASOURCES_DEFAULT_PASSWORD' => 'P4ssb0lt', + 'DATASOURCES_DEFAULT_USERNAME' => 'passbolt', + 'DATASOURCES_DEFAULT_DATABASE' => 'passbolt', + 'PASSBOLT_GPG_KEYRING' => '/var/lib/nginx/.gnupg' + } + + @image = Docker::Image.build_from_dir(ROOT_DOCKERFILES) + set :docker_image, @image.id + set :docker_container_create_options, { 'Cmd' => '/bin/sh' } + end + + let(:nginx_conf) { '/etc/nginx/nginx.conf' } + let(:site_conf) { '/etc/nginx/conf.d/default.conf' } + let(:passbolt_home) { '/var/www/passbolt' } + let(:passbolt_tmp) { '/var/www/passbolt/tmp' } + let(:passbolt_image) { '/var/www/passbolt/webroot/img/public' } + let(:passbolt_owner) { 'nginx' } + let(:exposed_ports) { [ '80', '443' ] } + let(:composer) { '/usr/local/bin/composer'} + let(:php_extensions) { [ + 'curl', 'gd', 'intl', 'json', 'mcrypt', 'mysqlnd', 'xsl', 'phar', + 'posix', 'xml', 'xsl', 'zlib', 'ctype', 'pdo', 'gnupg', 'pdo_mysql' + ] } + + describe 'passbolt required php extensions' do + it 'has php extensions installed' do + php_extensions.each do |ext| + expect(command("php --ri #{ext}").exit_status).to eq 0 + end + end + end + + describe 'php composer' do + it 'is installed' do + expect(file(composer)).to be_executable + end + end + + describe 'supervisor' do + it 'is installed' do + expect(package('supervisor')).to be_installed + end + end + + describe 'passbolt directory structure' do + it 'must exist and be directories' do + expect(file(passbolt_home)).to be_a_directory + expect(file(passbolt_tmp)).to be_a_directory + expect(file(passbolt_image)).to be_a_directory + end + + it 'must be owned by correct user' do + expect(file(passbolt_home)).to be_owned_by(passbolt_owner) + expect(file(passbolt_tmp)).to be_owned_by(passbolt_owner) + expect(file(passbolt_image)).to be_owned_by(passbolt_owner) + end + end + + describe 'nginx configuration' do + it 'is installed correctly' do + expect(file(nginx_conf)).to exist + end + + it 'has the correct permissions' do + expect(file(nginx_conf)).to be_owned_by 'root' + end + end + + describe 'nginx site configuration' do + it 'is installed correctly' do + expect(file(site_conf)).to exist + end + + it 'has the correct permissions' do + expect(file(site_conf)).to be_owned_by 'root' + end + end + + describe 'ports exposed' do + it 'exposes port' do + exposed_ports.each do |port| + expect(@image.json['ContainerConfig']['ExposedPorts']).to include("#{port}/tcp") + end + end + end +end diff --git a/spec/passbolt_api/Dockerfile_spec.rb b/spec/passbolt_api/Dockerfile_spec.rb deleted file mode 100644 index 3c3503b..0000000 --- a/spec/passbolt_api/Dockerfile_spec.rb +++ /dev/null @@ -1,111 +0,0 @@ -require 'spec_helper' - -describe 'passbolt_api service' do - - before(:all) do - @mysql = Docker::Container.create( - 'Env' => [ - 'MYSQL_ROOT_PASSWORD=test', - 'MYSQL_DATABASE=passbolt', - 'MYSQL_USER=passbolt', - 'MYSQL_PASSWORD=P4ssb0lt' - ], - 'Image' => 'mysql') - @mysql.start - - image = Docker::Image.build_from_dir(ROOT_DOCKERFILES) - - set :docker_image, image.id - set :env, { 'DB_HOST' => @mysql.json['NetworkSettings']['IPAddress'] } - end - - after(:all) do - @mysql.kill - end - - let(:nginx_conf) { '/etc/nginx/nginx.conf' } - let(:site_conf) { '/etc/nginx/conf.d/default.conf' } - let(:passbolt_home) { '/var/www/passbolt' } - let(:passbolt_tmp) { '/var/www/passbolt/tmp' } - let(:passbolt_image) { '/var/www/passbolt/webroot/img/public' } - let(:passbolt_owner) { 'nginx' } - - describe "passbolt required php extensions" do - - php_extensions = [ - 'curl', 'gd', 'intl', 'json', 'mcrypt', 'mysqlnd', 'xsl', 'phar', - 'posix', 'xml', 'xsl', 'zlib', 'ctype', 'pdo', 'gnupg' - ] - - php_extensions.each do |ext| - it "#{ext} must be installed" do - expect(command("php --ri #{ext}").exit_status).to eq 0 - end - end - end - - describe 'supervisor' do - xit 'is installed' do - expect(package('supervisor')).to be_installed - end - end - - describe 'passbolt home dirs' do - it 'must exist and be directories' do - expect(file(passbolt_home)).to be_a_directory - expect(file(passbolt_tmp)).to be_a_directory - expect(file(passbolt_image)).to be_a_directory - end - - it 'must be owned by correct user' do - expect(file(passbolt_home)).to be_owned_by(passbolt_owner) - end - end - - describe 'nginx configuration' do - it 'is installed correctly' do - expect(file(nginx_conf)).to exist - end - - it 'has the correct permissions' do - expect(file(nginx_conf)).to be_owned_by 'root' - end - end - - describe 'site configuration' do - it 'is installed correctly' do - expect(file(site_conf)).to exist - end - - it 'has the correct permissions' do - expect(file(site_conf)).to be_owned_by 'root' - end - end - - describe 'php service' do - xit 'is running supervised' do - expect(service('php-fpm')).to be_running.under('supervisor') - end - end - - describe port(9000) do - xit { is_expected.to be_listening.with('tcp') } - end - - describe 'email cron' do - xit 'is running supervised' do - expect(service('crond')).to be_running.under('supervisor') - end - end - - describe 'web service' do - xit 'is running supervised' do - expect(service('nginx')).to be_running.under('supervisor') - end - - xit 'is listening on port 80' do - expect(port(80)).to be_listening.with('tcp') - end - end - -end