diff --git a/5.6/Dockerfile b/5.6/Dockerfile index da2cca7f..04046447 100644 --- a/5.6/Dockerfile +++ b/5.6/Dockerfile @@ -1,4 +1,4 @@ -FROM centos:centos7 +FROM centos/s2i-core-centos7 # MySQL image for OpenShift. # @@ -11,6 +11,7 @@ FROM centos:centos7 # * $MYSQL_ROOT_PASSWORD (Optional) - Password for the 'root' MySQL account ENV MYSQL_VERSION=5.6 \ + APP_DATA=/opt/app-root/src \ HOME=/var/lib/mysql ENV SUMMARY="MySQL 5.6 SQL database server" \ @@ -35,9 +36,10 @@ EXPOSE 3306 # This image must forever use UID 27 for mysql user so our volumes are # safe in the future. This should *never* change, the last test is there # to make sure of that. -RUN yum install -y centos-release-scl && \ - INSTALL_PKGS="tar rsync gettext hostname bind-utils rh-mysql56" && \ - yum -y --setopt=tsflags=nodocs install $INSTALL_PKGS && \ +RUN yum install -y yum-utils && \ + yum install -y centos-release-scl && \ + INSTALL_PKGS="rsync tar gettext hostname bind-utils groff-base shadow-utils rh-mysql56" && \ + yum install -y --setopt=tsflags=nodocs $INSTALL_PKGS && \ rpm -V $INSTALL_PKGS && \ yum clean all && \ mkdir -p /var/lib/mysql/data && chown -R mysql.0 /var/lib/mysql && \ @@ -56,12 +58,13 @@ ENV BASH_ENV=${CONTAINER_SCRIPTS_PATH}/scl_enable \ PROMPT_COMMAND=". ${CONTAINER_SCRIPTS_PATH}/scl_enable" COPY 5.6/root-common / +COPY 5.6/s2i-common/bin/ $STI_SCRIPTS_PATH COPY 5.6/root / # this is needed due to issues with squash # when this directory gets rm'd by the container-setup # script. -RUN rm -rf /etc/my.cnf.d/* +RUN rm -rf /etc/my.cnf.d/* RUN /usr/libexec/container-setup VOLUME ["/var/lib/mysql/data"] diff --git a/5.6/Dockerfile.rhel7 b/5.6/Dockerfile.rhel7 index bc1a687d..f307aca2 100644 --- a/5.6/Dockerfile.rhel7 +++ b/5.6/Dockerfile.rhel7 @@ -1,4 +1,4 @@ -FROM rhel7 +FROM rhscl/s2i-core-rhel7 # MySQL image for OpenShift. # @@ -11,6 +11,7 @@ FROM rhel7 # * $MYSQL_ROOT_PASSWORD (Optional) - Password for the 'root' MySQL account ENV MYSQL_VERSION=5.6 \ + APP_DATA=/opt/app-root/src \ HOME=/var/lib/mysql ENV SUMMARY="MySQL 5.6 SQL database server" \ @@ -43,7 +44,7 @@ RUN yum repolist > /dev/null && \ yum-config-manager --enable rhel-7-server-rpms && \ yum-config-manager --enable rhel-7-server-optional-rpms && \ yum-config-manager --enable rhel-server-rhscl-7-rpms && \ - INSTALL_PKGS="rsync tar gettext hostname bind-utils rh-mysql56" && \ + INSTALL_PKGS="rsync tar gettext hostname bind-utils groff-base shadow-utils rh-mysql56" && \ yum install -y --setopt=tsflags=nodocs $INSTALL_PKGS && \ rpm -V $INSTALL_PKGS && \ yum clean all && \ @@ -63,12 +64,13 @@ ENV BASH_ENV=${CONTAINER_SCRIPTS_PATH}/scl_enable \ PROMPT_COMMAND=". ${CONTAINER_SCRIPTS_PATH}/scl_enable" COPY 5.6/root-common / +COPY 5.6/s2i-common/bin/ $STI_SCRIPTS_PATH COPY 5.6/root / # this is needed due to issues with squash # when this directory gets rm'd by the container-setup # script. -RUN rm -rf /etc/my.cnf.d/* +RUN rm -rf /etc/my.cnf.d/* RUN /usr/libexec/container-setup VOLUME ["/var/lib/mysql/data"] diff --git a/5.6/root/usr/share/container-scripts/mysql/README.md b/5.6/root/usr/share/container-scripts/mysql/README.md index 5ad40772..0af81880 100644 --- a/5.6/root/usr/share/container-scripts/mysql/README.md +++ b/5.6/root/usr/share/container-scripts/mysql/README.md @@ -44,7 +44,6 @@ or if it was already present, `mysqld` is executed and will run as PID 1. You ca stop the detached container by running `docker stop mysql_database`. - Environment variables and volumes --------------------------------- @@ -142,6 +141,81 @@ location is `/etc/my.cnf` but you can change it to `/etc/mysql/my.cnf` by settin `MYSQL_DEFAULTS_FILE=/etc/mysql/my.cnf` +Extending image +--------------- +This image can be extended using [source-to-image](https://github.com/openshift/source-to-image). + +For example, to build a customized MariaDB database image `my-mysql-rhel7` +with a configuration in `~/image-configuration/` run: + +``` +$ s2i build ~/image-configuration/ rhscl/mysql-56-rhel7 my-mysql-rhel7 +``` + +The directory passed to `s2i build` can contain these directories: + +`mysql-cfg/` + When starting the container, files from this directory will be used as + a configuration for the `mysqld` daemon. + `envsubst` command is run on this file to still allow customization of + the image using environmental variables + +`mysql-pre-init/` + Shell scripts (`*.sh`) available in this directory are sourced before + `mysqld` daemon is started. + +`mysql-init/` + Shell scripts (`*.sh`) available in this directory are sourced when + `mysqld` daemon is started locally. In this phase, use `${mysql_flags}` + to connect to the locally running daemon, for example `mysql $mysql_flags < dump.sql` + +Variables that can be used in the scripts provided to s2i: + +`$mysql_flags` + arguments for the `mysql` tool that will connect to the locally running `mysqld` during initialization + +`$MYSQL_RUNNING_AS_MASTER` + variable defined when the container is run with `run-mysqld-master` command + +`$MYSQL_RUNNING_AS_SLAVE` + variable defined when the container is run with `run-mysqld-slave` command + +`$MYSQL_DATADIR_FIRST_INIT` + variable defined when the container was initialized from the empty data dir + +During `s2i build` all provided files are copied into `/opt/app-root/src` +directory into the resulting image. If some configuration files are present +in the destination directory, files with the same name are overwritten. +Also only one file with the same name can be used for customization and user +provided files are preferred over default files in +`/usr/share/container-scripts/mysql/`- so it is possible to overwrite them. + +Same configuration directory structure can be used to customize the image +every time the image is started using `docker run`. The directory has to be +mounted into `/opt/app-root/src/` in the image +(`-v ./image-configuration/:/opt/app-root/src/`). +This overwrites customization built into the image. + + +Securing the connection with SSL +-------------------------------- +In order to secure the connection with SSL, use the extending feature described +above. In particular, put the SSL certificates into a separate directory: + + sslapp/mysql-certs/server-cert-selfsigned.pem + sslapp/mysql-certs/server-key.pem + +And then put a separate configuration file into mysql-cfg: + + $> cat sslapp/mysql-cfg/ssl.cnf + [mysqld] + ssl-key=${APP_DATA}/mysql-certs/server-key.pem + ssl-cert=${APP_DATA}/mysql-certs/server-cert-selfsigned.pem + +Such a directory `sslapp` can then be mounted into the container with -v, +or a new container image can be built using s2i. + + Changing the replication binlog_format -------------------------------------- Some applications may wish to use `row` binlog_formats (for example, those built diff --git a/5.6/s2i-common b/5.6/s2i-common new file mode 120000 index 00000000..49d966b0 --- /dev/null +++ b/5.6/s2i-common @@ -0,0 +1 @@ +../s2i-common/ \ No newline at end of file diff --git a/5.7/Dockerfile b/5.7/Dockerfile index f8496411..c35d9c7a 100644 --- a/5.7/Dockerfile +++ b/5.7/Dockerfile @@ -38,7 +38,6 @@ EXPOSE 3306 # to make sure of that. RUN yum install -y yum-utils && \ yum install -y centos-release-scl && \ - yum-config-manager --enable centos-sclo-rh-testing && \ INSTALL_PKGS="rsync tar gettext hostname bind-utils groff-base shadow-utils rh-mysql57" && \ yum install -y --setopt=tsflags=nodocs $INSTALL_PKGS && \ rpm -V $INSTALL_PKGS && \ @@ -59,6 +58,7 @@ ENV BASH_ENV=${CONTAINER_SCRIPTS_PATH}/scl_enable \ PROMPT_COMMAND=". ${CONTAINER_SCRIPTS_PATH}/scl_enable" COPY 5.7/root-common / +COPY 5.7/s2i-common/bin/ $STI_SCRIPTS_PATH COPY 5.7/root / # this is needed due to issues with squash diff --git a/5.7/Dockerfile.fedora b/5.7/Dockerfile.fedora index c14a119c..2c4ff39a 100644 --- a/5.7/Dockerfile.fedora +++ b/5.7/Dockerfile.fedora @@ -1,4 +1,4 @@ -FROM registry.fedoraproject.org/fedora:26 +FROM registry.fedoraproject.org/f26/s2i-core:latest # MySQL image for OpenShift. # @@ -11,6 +11,7 @@ FROM registry.fedoraproject.org/fedora:26 # * $MYSQL_ROOT_PASSWORD (Optional) - Password for the 'root' MySQL account ENV MYSQL_VERSION=5.7 \ + APP_DATA=/opt/app-root/src \ HOME=/var/lib/mysql ENV SUMMARY="MySQL 5.7 SQL database server" \ @@ -47,7 +48,7 @@ RUN ln -s /usr/bin/python3 /usr/bin/python # This image must forever use UID 27 for mysql user so our volumes are # safe in the future. This should *never* change, the last test is there # to make sure of that. -RUN INSTALL_PKGS="rsync tar gettext hostname bind-utils community-mysql-server policycoreutils" && \ +RUN INSTALL_PKGS="rsync tar gettext hostname bind-utils groff-base shadow-utils community-mysql-server policycoreutils" && \ yum install -y --setopt=tsflags=nodocs $INSTALL_PKGS && \ rpm -V $INSTALL_PKGS && \ yum clean all && \ @@ -59,6 +60,7 @@ ENV CONTAINER_SCRIPTS_PATH=/usr/share/container-scripts/mysql \ MYSQL_PREFIX=/usr COPY 5.7/root-common / +COPY 5.7/s2i-common/bin/ $STI_SCRIPTS_PATH COPY 5.7/root / # this is needed due to issues with squash diff --git a/5.7/Dockerfile.rhel7 b/5.7/Dockerfile.rhel7 index 27cb18d0..e25fdea8 100644 --- a/5.7/Dockerfile.rhel7 +++ b/5.7/Dockerfile.rhel7 @@ -1,4 +1,4 @@ -FROM rhel7 +FROM rhscl/s2i-core-rhel7 # MySQL image for OpenShift. # @@ -11,6 +11,7 @@ FROM rhel7 # * $MYSQL_ROOT_PASSWORD (Optional) - Password for the 'root' MySQL account ENV MYSQL_VERSION=5.7 \ + APP_DATA=/opt/app-root/src \ HOME=/var/lib/mysql ENV SUMMARY="MySQL 5.7 SQL database server" \ @@ -43,7 +44,7 @@ RUN yum repolist > /dev/null && \ yum-config-manager --enable rhel-7-server-rpms && \ yum-config-manager --enable rhel-7-server-optional-rpms && \ yum-config-manager --enable rhel-server-rhscl-7-rpms && \ - INSTALL_PKGS="rsync tar gettext hostname bind-utils rh-mysql57" && \ + INSTALL_PKGS="rsync tar gettext hostname bind-utils groff-base shadow-utils rh-mysql57" && \ yum install -y --setopt=tsflags=nodocs $INSTALL_PKGS && \ rpm -V $INSTALL_PKGS && \ yum clean all && \ @@ -63,6 +64,7 @@ ENV BASH_ENV=${CONTAINER_SCRIPTS_PATH}/scl_enable \ PROMPT_COMMAND=". ${CONTAINER_SCRIPTS_PATH}/scl_enable" COPY 5.7/root-common / +COPY 5.7/s2i-common/bin/ $STI_SCRIPTS_PATH COPY 5.7/root / # this is needed due to issues with squash diff --git a/5.7/root/usr/share/container-scripts/mysql/README.md b/5.7/root/usr/share/container-scripts/mysql/README.md index 09bc8314..3315d3a4 100644 --- a/5.7/root/usr/share/container-scripts/mysql/README.md +++ b/5.7/root/usr/share/container-scripts/mysql/README.md @@ -44,7 +44,6 @@ or if it was already present, `mysqld` is executed and will run as PID 1. You ca stop the detached container by running `docker stop mysql_database`. - Environment variables and volumes --------------------------------- @@ -142,6 +141,81 @@ location is `/etc/my.cnf` but you can change it to `/etc/mysql/my.cnf` by settin `MYSQL_DEFAULTS_FILE=/etc/mysql/my.cnf` +Extending image +--------------- +This image can be extended using [source-to-image](https://github.com/openshift/source-to-image). + +For example, to build a customized MariaDB database image `my-mysql-rhel7` +with a configuration in `~/image-configuration/` run: + +``` +$ s2i build ~/image-configuration/ rhscl/mysql-57-rhel7 my-mysql-rhel7 +``` + +The directory passed to `s2i build` can contain these directories: + +`mysql-cfg/` + When starting the container, files from this directory will be used as + a configuration for the `mysqld` daemon. + `envsubst` command is run on this file to still allow customization of + the image using environmental variables + +`mysql-pre-init/` + Shell scripts (`*.sh`) available in this directory are sourced before + `mysqld` daemon is started. + +`mysql-init/` + Shell scripts (`*.sh`) available in this directory are sourced when + `mysqld` daemon is started locally. In this phase, use `${mysql_flags}` + to connect to the locally running daemon, for example `mysql $mysql_flags < dump.sql` + +Variables that can be used in the scripts provided to s2i: + +`$mysql_flags` + arguments for the `mysql` tool that will connect to the locally running `mysqld` during initialization + +`$MYSQL_RUNNING_AS_MASTER` + variable defined when the container is run with `run-mysqld-master` command + +`$MYSQL_RUNNING_AS_SLAVE` + variable defined when the container is run with `run-mysqld-slave` command + +`$MYSQL_DATADIR_FIRST_INIT` + variable defined when the container was initialized from the empty data dir + +During `s2i build` all provided files are copied into `/opt/app-root/src` +directory into the resulting image. If some configuration files are present +in the destination directory, files with the same name are overwritten. +Also only one file with the same name can be used for customization and user +provided files are preferred over default files in +`/usr/share/container-scripts/mysql/`- so it is possible to overwrite them. + +Same configuration directory structure can be used to customize the image +every time the image is started using `docker run`. The directory has to be +mounted into `/opt/app-root/src/` in the image +(`-v ./image-configuration/:/opt/app-root/src/`). +This overwrites customization built into the image. + + +Securing the connection with SSL +-------------------------------- +In order to secure the connection with SSL, use the extending feature described +above. In particular, put the SSL certificates into a separate directory: + + sslapp/mysql-certs/server-cert-selfsigned.pem + sslapp/mysql-certs/server-key.pem + +And then put a separate configuration file into mysql-cfg: + + $> cat sslapp/mysql-cfg/ssl.cnf + [mysqld] + ssl-key=${APP_DATA}/mysql-certs/server-key.pem + ssl-cert=${APP_DATA}/mysql-certs/server-cert-selfsigned.pem + +Such a directory `sslapp` can then be mounted into the container with -v, +or a new container image can be built using s2i. + + Changing the replication binlog_format -------------------------------------- Some applications may wish to use `row` binlog_formats (for example, those built diff --git a/5.7/s2i-common b/5.7/s2i-common new file mode 120000 index 00000000..49d966b0 --- /dev/null +++ b/5.7/s2i-common @@ -0,0 +1 @@ +../s2i-common/ \ No newline at end of file diff --git a/examples/extend-image/mysql-cfg/myconfig.cnf b/examples/extend-image/mysql-cfg/myconfig.cnf new file mode 100644 index 00000000..7764adf9 --- /dev/null +++ b/examples/extend-image/mysql-cfg/myconfig.cnf @@ -0,0 +1,3 @@ +[mysqld] +query-cache-limit=262144 + diff --git a/examples/extend-image/mysql-data/init.sql b/examples/extend-image/mysql-data/init.sql new file mode 100644 index 00000000..31599829 --- /dev/null +++ b/examples/extend-image/mysql-data/init.sql @@ -0,0 +1,4 @@ +CREATE TABLE products (id INTEGER, name VARCHAR(256), price FLOAT, variant INTEGER); +CREATE TABLE products_variant (id INTEGER, name VARCHAR(256)); +INSERT INTO products_variant (id, name) VALUES ('1', 'blue'), ('2', 'green'); + diff --git a/examples/extend-image/mysql-init/80-add-arbitrary-users.sh b/examples/extend-image/mysql-init/80-add-arbitrary-users.sh new file mode 100644 index 00000000..55ae2d29 --- /dev/null +++ b/examples/extend-image/mysql-init/80-add-arbitrary-users.sh @@ -0,0 +1,17 @@ +create_arbitrary_users() { + # Do not care what option is compulsory here, just create what is specified + log_info "Creating user specified by MYSQL_OPERATIONS_USER (${MYSQL_OPERATIONS_USER}) ..." +mysql $mysql_flags < /etc/my.cnf.d/base.cnf -envsubst < ${CONTAINER_SCRIPTS_PATH}/my-paas.cnf.template > /etc/my.cnf.d/paas.cnf -envsubst < ${CONTAINER_SCRIPTS_PATH}/my-tuning.cnf.template > /etc/my.cnf.d/tuning.cnf +# pre-init files +process_extending_files ${APP_DATA}/mysql-pre-init/ ${CONTAINER_SCRIPTS_PATH}/pre-init/ if [ ! -d "$MYSQL_DATADIR/mysql" ]; then initialize_database "$@" @@ -21,14 +17,8 @@ else start_local_mysql "$@" fi -if [ -f ${CONTAINER_SCRIPTS_PATH}/passwd-change.sh ]; then - log_info 'Setting passwords ...' - source ${CONTAINER_SCRIPTS_PATH}/passwd-change.sh -fi -if [ -f ${CONTAINER_SCRIPTS_PATH}/post-init.sh ]; then - log_info 'Sourcing post-init.sh ...' - source ${CONTAINER_SCRIPTS_PATH}/post-init.sh -fi +# init files +process_extending_files ${APP_DATA}/mysql-init/ ${CONTAINER_SCRIPTS_PATH}/init/ # Restart the MySQL server with public IP bindings shutdown_local_mysql diff --git a/root-common/usr/bin/run-mysqld-master b/root-common/usr/bin/run-mysqld-master index e282e2dd..ed332eaa 100755 --- a/root-common/usr/bin/run-mysqld-master +++ b/root-common/usr/bin/run-mysqld-master @@ -3,27 +3,22 @@ # This is an entrypoint that runs the MySQL server in the 'master' mode. # +export_vars=$(cgroup-limits); export $export_vars source ${CONTAINER_SCRIPTS_PATH}/common.sh set -eu export_setting_variables -[ -f ${CONTAINER_SCRIPTS_PATH}/validate-replication-variables.sh ] && source ${CONTAINER_SCRIPTS_PATH}/validate-replication-variables.sh -[ -f ${CONTAINER_SCRIPTS_PATH}/validate-variables.sh ] && source ${CONTAINER_SCRIPTS_PATH}/validate-variables.sh - log_volume_info $MYSQL_DATADIR +export MYSQL_RUNNING_AS_MASTER=1 + # The 'server-id' for master needs to be constant export MYSQL_SERVER_ID=1 log_info "The 'master' server-id is ${MYSQL_SERVER_ID}" -# Process the MySQL configuration files -log_info 'Processing MySQL configuration files ...' -envsubst < ${CONTAINER_SCRIPTS_PATH}/my-base.cnf.template > /etc/my.cnf.d/base.cnf -envsubst < ${CONTAINER_SCRIPTS_PATH}/my-paas.cnf.template > /etc/my.cnf.d/paas.cnf -envsubst < ${CONTAINER_SCRIPTS_PATH}/my-master.cnf.template > /etc/my.cnf.d/master.cnf -envsubst < ${CONTAINER_SCRIPTS_PATH}/my-repl-gtid.cnf.template > /etc/my.cnf.d/repl-gtid.cnf -envsubst < ${CONTAINER_SCRIPTS_PATH}/my-tuning.cnf.template > /etc/my.cnf.d/tuning.cnf +# pre-init files +process_extending_files ${APP_DATA}/mysql-pre-init/ ${CONTAINER_SCRIPTS_PATH}/pre-init/ if [ ! -d "$MYSQL_DATADIR/mysql" ]; then initialize_database "$@" @@ -40,8 +35,8 @@ mysql $mysql_flags < /etc/my.cnf.d/base.cnf -envsubst < ${CONTAINER_SCRIPTS_PATH}/my-paas.cnf.template > /etc/my.cnf.d/paas.cnf -envsubst < ${CONTAINER_SCRIPTS_PATH}/my-slave.cnf.template > /etc/my.cnf.d/slave.cnf -envsubst < ${CONTAINER_SCRIPTS_PATH}/my-repl-gtid.cnf.template > /etc/my.cnf.d/repl-gtid.cnf -envsubst < ${CONTAINER_SCRIPTS_PATH}/my-tuning.cnf.template > /etc/my.cnf.d/tuning.cnf +# pre-init files +process_extending_files ${APP_DATA}/mysql-pre-init/ ${CONTAINER_SCRIPTS_PATH}/pre-init/ if [ ! -e "${MYSQL_DATADIR}/mysql" ]; then # Initialize MySQL database and wait for the MySQL master to accept @@ -36,8 +30,8 @@ if [ ! -e "${MYSQL_DATADIR}/mysql" ]; then CHANGE MASTER TO MASTER_HOST='${MYSQL_MASTER_SERVICE_NAME}',MASTER_USER='${MYSQL_MASTER_USER}', MASTER_PASSWORD='${MYSQL_MASTER_PASSWORD}', MASTER_AUTO_POSITION = 1; EOSQL - log_info 'Sourcing post-init.sh ...' - [ -f ${CONTAINER_SCRIPTS_PATH}/post-init.sh ] && source ${CONTAINER_SCRIPTS_PATH}/post-init.sh + # init files + process_extending_files ${APP_DATA}/mysql-init/ ${CONTAINER_SCRIPTS_PATH}/init/ # Restart the MySQL server with public IP bindings shutdown_local_mysql diff --git a/root-common/usr/libexec/container-setup b/root-common/usr/libexec/container-setup index 29c6ed25..6160d4e8 100755 --- a/root-common/usr/libexec/container-setup +++ b/root-common/usr/libexec/container-setup @@ -54,5 +54,6 @@ restorecon -R /var/lib/mysql # arbitrary UID # When only specifying user, group is 0, that's why /var/lib/mysql must have # owner mysql.0; that allows to avoid a+rwx for this dir -chmod g+w -R /var/lib/mysql ${MYSQL_CONFIG_FILE}.d +/usr/libexec/fix-permissions /var/lib/mysql ${MYSQL_CONFIG_FILE}.d ${APP_DATA}/.. +usermod -a -G root mysql diff --git a/root-common/usr/libexec/fix-permissions b/root-common/usr/libexec/fix-permissions new file mode 100755 index 00000000..820e7181 --- /dev/null +++ b/root-common/usr/libexec/fix-permissions @@ -0,0 +1,6 @@ +#!/bin/sh +# Fix permissions on the given directory to allow group read/write of +# regular files and execute of directories. +find $@ -exec chown mysql:0 {} \; +find $@ -exec chmod g+rw {} \; +find $@ -type d -exec chmod g+x {} + diff --git a/root-common/usr/share/container-scripts/mysql/my-paas.cnf.template b/root-common/usr/share/container-scripts/mysql/cnf/40-paas.cnf similarity index 100% rename from root-common/usr/share/container-scripts/mysql/my-paas.cnf.template rename to root-common/usr/share/container-scripts/mysql/cnf/40-paas.cnf diff --git a/root-common/usr/share/container-scripts/mysql/my-tuning.cnf.template b/root-common/usr/share/container-scripts/mysql/cnf/50-my-tuning.cnf similarity index 100% rename from root-common/usr/share/container-scripts/mysql/my-tuning.cnf.template rename to root-common/usr/share/container-scripts/mysql/cnf/50-my-tuning.cnf diff --git a/root-common/usr/share/container-scripts/mysql/common.sh b/root-common/usr/share/container-scripts/mysql/common.sh index 51c62b6c..19f75b83 100644 --- a/root-common/usr/share/container-scripts/mysql/common.sh +++ b/root-common/usr/share/container-scripts/mysql/common.sh @@ -41,6 +41,9 @@ function export_setting_variables() { fi } +# this stores whether the database was initialized from empty datadir +export MYSQL_DATADIR_FIRST_INIT=false + # Be paranoid and stricter than we should be. # https://dev.mysql.com/doc/refman/en/identifiers.html mysql_identifier_regex='^[a-zA-Z0-9_]+$' @@ -139,6 +142,9 @@ mysql $mysql_flags < sourcing $filename ..." + # Custom file is prefered + if [ -f $custom_dir/$filename ]; then + source $custom_dir/$filename + else + source $default_dir/$filename + fi + done <<<"$(get_matched_files "$custom_dir" "$default_dir" '*.sh' | sort -u)" +} + +# process extending config files in $1 and $2 directories +# - expand variables in *.cnf and copy the files into /etc/my.cnf.d directory +# (if there are files with same name source only file from $1) +function process_extending_config_files() { + local custom_dir default_dir + custom_dir=$1 + default_dir=$2 + + while read filename ; do + echo "=> sourcing $filename ..." + # Custom file is prefered + if [ -f $custom_dir/$filename ]; then + envsubst < $custom_dir/$filename > /etc/my.cnf.d/$filename + else + envsubst < $default_dir/$filename > /etc/my.cnf.d/$filename + fi + done <<<"$(get_matched_files "$custom_dir" "$default_dir" '*.cnf' | sort -u)" +} diff --git a/root-common/usr/share/container-scripts/mysql/init/50-passwd-change.sh b/root-common/usr/share/container-scripts/mysql/init/50-passwd-change.sh new file mode 100644 index 00000000..2514f2dc --- /dev/null +++ b/root-common/usr/share/container-scripts/mysql/init/50-passwd-change.sh @@ -0,0 +1,48 @@ +password_change() { + log_info 'Setting passwords ...' + + # Set the password for MySQL user and root everytime this container is started. + # This allows to change the password by editing the deployment configuration. + if [[ -v MYSQL_USER && -v MYSQL_PASSWORD ]]; then +mysql $mysql_flags < "5.6" ] ; then +mysql $mysql_flags < "5.6" ] ; then +mysql $mysql_flags < "5.6" ]] ; then - mysql $mysql_flags < /etc/my.cnf.d/base.cnf + diff --git a/root-common/usr/share/container-scripts/mysql/pre-init/60-replication-config.sh b/root-common/usr/share/container-scripts/mysql/pre-init/60-replication-config.sh new file mode 100644 index 00000000..a923476f --- /dev/null +++ b/root-common/usr/share/container-scripts/mysql/pre-init/60-replication-config.sh @@ -0,0 +1,17 @@ +# mysqld configuration for replication scenarios + +if [ -v MYSQL_RUNNING_AS_MASTER ] || [ -v MYSQL_RUNNING_AS_SLAVE ] ; then + log_info 'Processing basic MySQL configuration for replication (master and slave) files ...' + envsubst < ${CONTAINER_SCRIPTS_PATH}/pre-init/my-repl-gtid.cnf.template > /etc/my.cnf.d/repl-gtid.cnf +fi + +if [ -v MYSQL_RUNNING_AS_MASTER ] ; then + log_info 'Processing basic MySQL configuration for replication (master only) files ...' + envsubst < ${CONTAINER_SCRIPTS_PATH}/pre-init/my-master.cnf.template > /etc/my.cnf.d/master.cnf +fi + +if [ -v MYSQL_RUNNING_AS_SLAVE ] ; then + log_info 'Processing basic MySQL configuration for replication (slave only) files ...' + envsubst < ${CONTAINER_SCRIPTS_PATH}/pre-init/my-slave.cnf.template > /etc/my.cnf.d/slave.cnf +fi + diff --git a/root-common/usr/share/container-scripts/mysql/pre-init/70-s2i-config.sh b/root-common/usr/share/container-scripts/mysql/pre-init/70-s2i-config.sh new file mode 100644 index 00000000..7a8ae5af --- /dev/null +++ b/root-common/usr/share/container-scripts/mysql/pre-init/70-s2i-config.sh @@ -0,0 +1,6 @@ +# additional arbitrary mysqld configuration provided by user using s2i + +log_info 'Processing additional arbitrary MySQL configuration provided by s2i ...' + +process_extending_config_files ${APP_DATA}/mysql-cfg/ ${CONTAINER_SCRIPTS_PATH}/cnf/ + diff --git a/root-common/usr/share/container-scripts/mysql/my-base.cnf.template b/root-common/usr/share/container-scripts/mysql/pre-init/my-base.cnf.template similarity index 100% rename from root-common/usr/share/container-scripts/mysql/my-base.cnf.template rename to root-common/usr/share/container-scripts/mysql/pre-init/my-base.cnf.template diff --git a/root-common/usr/share/container-scripts/mysql/my-master.cnf.template b/root-common/usr/share/container-scripts/mysql/pre-init/my-master.cnf.template similarity index 100% rename from root-common/usr/share/container-scripts/mysql/my-master.cnf.template rename to root-common/usr/share/container-scripts/mysql/pre-init/my-master.cnf.template diff --git a/root-common/usr/share/container-scripts/mysql/my-repl-gtid.cnf.template b/root-common/usr/share/container-scripts/mysql/pre-init/my-repl-gtid.cnf.template similarity index 100% rename from root-common/usr/share/container-scripts/mysql/my-repl-gtid.cnf.template rename to root-common/usr/share/container-scripts/mysql/pre-init/my-repl-gtid.cnf.template diff --git a/root-common/usr/share/container-scripts/mysql/my-slave.cnf.template b/root-common/usr/share/container-scripts/mysql/pre-init/my-slave.cnf.template similarity index 100% rename from root-common/usr/share/container-scripts/mysql/my-slave.cnf.template rename to root-common/usr/share/container-scripts/mysql/pre-init/my-slave.cnf.template diff --git a/s2i-common/bin/assemble b/s2i-common/bin/assemble new file mode 100755 index 00000000..d65b7e0c --- /dev/null +++ b/s2i-common/bin/assemble @@ -0,0 +1,13 @@ +#!/bin/bash + +set -o errexit +set -o nounset +set -o pipefail + +shopt -s dotglob +echo "---> Installing application source ..." +mv /tmp/src/* ./ 2>/dev/null || true + +# Fix source directory permissions +/usr/libexec/fix-permissions ./ + diff --git a/s2i-common/bin/run b/s2i-common/bin/run new file mode 120000 index 00000000..4b21ab59 --- /dev/null +++ b/s2i-common/bin/run @@ -0,0 +1 @@ +/bin/run-mysqld \ No newline at end of file diff --git a/s2i-common/bin/usage b/s2i-common/bin/usage new file mode 100755 index 00000000..d6a3b9a7 --- /dev/null +++ b/s2i-common/bin/usage @@ -0,0 +1,8 @@ +#!/bin/sh + +set -o errexit +set -o nounset +set -o pipefail + +groff -t -man -ETascii /help.1 + diff --git a/test/run b/test/run index 66c2fea6..6e3ad6d8 100755 --- a/test/run +++ b/test/run @@ -10,10 +10,27 @@ set -o errexit set -o nounset shopt -s nullglob -IMAGE_NAME=${IMAGE_NAME-centos/mysql-${VERSION//.}-centos7-candidate} +TEST_LIST="\ +run_container_creation_tests +run_configuration_tests +run_general_tests +run_change_password_test +run_replication_test +run_doc_test +run_s2i_test +run_ssl_test +" + +if [ -e "${IMAGE_NAME:-}" ] ; then + echo "Error: IMAGE_NAME must be specified" + exit 1 +fi CIDFILE_DIR=$(mktemp --suffix=mysql_test_cidfiles -d) TESTSUITE_RESULT=1 +test_dir=$(readlink -zf $(dirname "${BASH_SOURCE[0]}")) + +s2i_args="--force-pull=false " function cleanup() { local cidfile @@ -58,7 +75,7 @@ function mysql_cmd() { local container_ip="$1"; shift local login="$1"; shift local password="$1"; shift - docker run --rm "$IMAGE_NAME" mysql --host "$container_ip" -u"$login" -p"$password" "$@" db + docker run --rm ${CONTAINER_EXTRA_ARGS:-} "$IMAGE_NAME" mysql --host "$container_ip" -u"$login" -p"$password" "$@" db } function test_connection() { @@ -164,6 +181,27 @@ function run_replication_test() { fi sleep 1 done + + # do some real work to test replication in practice + mysql_cmd "$master_ip" root root -e "CREATE TABLE t1 (a INT); INSERT INTO t1 VALUES (24);" + + # read value from slave and check whether it is expectd + for i in $(seq $max_attempts); do + set +e + result="$(mysql_cmd "${slave_ip}" root root -e "select * from t1 \G" | grep -e ^a | grep 24)" + set -e + if [[ ! -z "${result}" ]]; then + echo "${slave_ip} successfully got value from MASTER ${master_ip}" + break + fi + if [[ "${i}" == "${max_attempts}" ]]; then + echo "The ${slave_ip} failed to see value added on MASTER" + echo "Dumping logs for $(get_cid slave.cid)" + docker logs $(get_cid slave.cid) + return 1 + fi + sleep 1 + done } function assert_login_access() { @@ -403,28 +441,115 @@ run_doc_test() { echo } -# Tests. +_s2i_test_image() { + local container_name="$1" + local mount_opts="$2" + echo " Testing s2i app image with invalid configuration" + assert_container_creation_fails -e MYSQL_USER=root -e MYSQL_PASSWORD=pass -e MYSQL_DATABASE=db -e MYSQL_ROOT_PASSWORD=pass + echo " Testing s2i app image with correct configuration" + create_container \ + "$container_name" \ + --env MYSQL_USER=config_test_user \ + --env MYSQL_PASSWORD=config_test \ + --env MYSQL_DATABASE=db \ + --env MYSQL_OPERATIONS_USER=operations_user \ + --env MYSQL_OPERATIONS_PASSWORD=operations_pass \ + ${mount_opts} -run_container_creation_tests + test_connection "$container_name" operations_user operations_pass -run_configuration_tests + configuration="$(docker exec -t "$(get_cid $container_name)" bash -c 'set +f; shopt -s nullglob; egrep -hv "^(#|\!|\[|$)" /etc/my.cnf /etc/my.cnf.d/* /opt/rh/mysql*/root/etc/my.cnf /opt/rh/mysql*/root/etc/my.cnf.d/*' | sed 's,\(^[[:space:]]\+\|[[:space:]]\+$\),,' | sort -u)" -# Set lower buffer pool size to avoid running out of memory. -export CONTAINER_ARGS="run-mysqld --innodb_buffer_pool_size=5242880" + docker stop "$(get_cid $container_name)" >/dev/null +} -# Normal tests -USER=user PASS=pass run_tests no_root -USER=user1 PASS=pass1 ROOT_PASS=r00t run_tests root -# Test with arbitrary uid for the container -DOCKER_ARGS="-u 12345" USER=user PASS=pass run_tests no_root_altuid -DOCKER_ARGS="-u 12345" USER=user1 PASS=pass1 ROOT_PASS=r00t run_tests root_altuid +run_s2i_test() { + echo " Testing s2i usage" + s2i usage ${s2i_args} ${IMAGE_NAME} &>/dev/null -# Test the password change -run_change_password_test + echo " Testing s2i build" + s2i build file://${test_dir}/test-app ${IMAGE_NAME} ${IMAGE_NAME}-testapp + local image_name_backup=${IMAGE_NAME} + export IMAGE_NAME=${IMAGE_NAME}-testapp -# Replication tests -run_replication_test + local container_name=s2i_config_build + _s2i_test_image "s2i_config_build" "" -run_doc_test + # return back original value for IMAGE_NAME + export IMAGE_NAME=${image_name_backup} + + echo " Testing s2i mount" + test_app_dir=$(mktemp -d) + cp -Lr ${test_dir}/test-app ${test_app_dir}/ + chown -R 27:27 ${test_app_dir} + _s2i_test_image "_s2i_test_mount" "-v ${test_app_dir}/test-app:/opt/app-root/src/:z" + rm -rf ${test_app_dir} + echo " Success!" +} + +gen_self_signed_cert() { + local output_dir=$1 ; shift + local base_name=$1 ; shift + mkdir -p ${output_dir} + openssl req -newkey rsa:2048 -nodes -keyout ${output_dir}/${base_name}-key.pem -subj '/C=GB/ST=Berkshire/L=Newbury/O=My Server Company' > ${base_name}-req.pem + openssl req -new -x509 -nodes -key ${output_dir}/${base_name}-key.pem -batch > ${output_dir}/${base_name}-cert-selfsigned.pem +} + +run_ssl_test() { + echo " Testing ssl usage" + test_app_dir=$(mktemp -d) + mkdir -p ${test_app_dir}/{mysql-certs,mysql-cfg} + gen_self_signed_cert ${test_app_dir}/mysql-certs server + echo "[mysqld] +ssl-key=\${APP_DATA}/mysql-certs/server-key.pem +ssl-cert=\${APP_DATA}/mysql-certs/server-cert-selfsigned.pem +" >${test_app_dir}/mysql-cfg/ssl.cnf + chown -R 27:27 ${test_app_dir} + local ca_cert_path="/opt/app-root/src/mysql-certs/server-cert-selfsigned.pem" + + create_container \ + "_s2i_test_ssl" \ + --env MYSQL_USER=ssl_test_user \ + --env MYSQL_PASSWORD=ssl_test \ + --env MYSQL_DATABASE=db \ + -v ${test_app_dir}:/opt/app-root/src/:z + + test_connection "_s2i_test_ssl" ssl_test_user ssl_test + ip=$(get_container_ip _s2i_test_ssl) + + # At least MySQL 5.6 requires ssl-ca option on client side, otherwise the ssl is not used + CONTAINER_EXTRA_ARGS="-v ${test_app_dir}:/opt/app-root/src/:z" + if mysql_cmd "$ip" "ssl_test_user" "ssl_test" --ssl-mode=REQUIRED --ssl-ca=${ca_cert_path} -e 'show status like "Ssl_cipher" \G' | grep 'Value: [A-Z][A-Z0-9-]*' ; then + echo " Success!" + rm -rf ${test_app_dir} + else + echo " FAIL!" + mysql_cmd "$ip" "ssl_test_user" "ssl_test" --ssl-ca=${ca_cert_path} -e 'show status like "%ssl%" \G' + return 1 + fi +} + +function run_general_tests() { + # Set lower buffer pool size to avoid running out of memory. + export CONTAINER_ARGS="run-mysqld --innodb_buffer_pool_size=5242880" + + # Normal tests + USER=user PASS=pass run_tests no_root + USER=user1 PASS=pass1 ROOT_PASS=r00t run_tests root + # Test with arbitrary uid for the container + DOCKER_ARGS="-u 12345" USER=user PASS=pass run_tests no_root_altuid + DOCKER_ARGS="-u 12345" USER=user1 PASS=pass1 ROOT_PASS=r00t run_tests root_altuid +} + +function run_all_tests() { + for test_case in $TEST_LIST; do + : "Running test $test_case" + $test_case + done; +} + +# Run the chosen tests +TEST_LIST=${@:-$TEST_LIST} run_all_tests TESTSUITE_RESULT=0 + diff --git a/test/test-app b/test/test-app new file mode 120000 index 00000000..1c25ae34 --- /dev/null +++ b/test/test-app @@ -0,0 +1 @@ +../examples/extend-image/ \ No newline at end of file