From 4d33f59107617d71b991ec4a6908b28a2ffa58a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20G=C3=B6rn?= Date: Wed, 11 May 2016 21:41:39 +0200 Subject: [PATCH] This is to build static content services on OpenShift --- s2i-nginx/.s2i/bin/assemble | 79 +++++++++++++ s2i-nginx/.s2i/bin/run | 10 ++ s2i-nginx/.s2i/bin/usage | 12 ++ s2i-nginx/Dockerfile | 38 ++++++ s2i-nginx/Makefile | 10 ++ s2i-nginx/Readme.md | 43 +++++++ s2i-nginx/etc/nginx.conf | 19 +++ s2i-nginx/etc/nginx.server.sample.conf | 16 +++ s2i-nginx/test/run | 154 +++++++++++++++++++++++++ 9 files changed, 381 insertions(+) create mode 100755 s2i-nginx/.s2i/bin/assemble create mode 100755 s2i-nginx/.s2i/bin/run create mode 100755 s2i-nginx/.s2i/bin/usage create mode 100644 s2i-nginx/Dockerfile create mode 100644 s2i-nginx/Makefile create mode 100644 s2i-nginx/Readme.md create mode 100644 s2i-nginx/etc/nginx.conf create mode 100644 s2i-nginx/etc/nginx.server.sample.conf create mode 100755 s2i-nginx/test/run diff --git a/s2i-nginx/.s2i/bin/assemble b/s2i-nginx/.s2i/bin/assemble new file mode 100755 index 00000000..18cd7286 --- /dev/null +++ b/s2i-nginx/.s2i/bin/assemble @@ -0,0 +1,79 @@ +#!/bin/bash -e +# +# S2I assemble script for the 's2i-nginx' image. +# The 'assemble' script builds your application source ready to run. +# +# For more information refer to the documentation: +# https://github.com/openshift/source-to-image/blob/master/docs/builder_image.md +# + +if [ "$1" = "-h" ]; then + # If the 's2i-nginx' assemble script is executed with '-h' flag, + # print the usage. + exec /usr/libexec/s2i/usage +fi + +NGINX_STATIC_DIR=${NGINX_STATIC_DIR-html} +NGINX_CONF_FILE=${NGINX_CONF_FILE-} +NGINX_CONF_DIR=${NGINX_CONF_DIR-conf.d} +NGINX_AUX_DIR=${NGINX_AUX_DIR-aux} + +copy_static_files() { + echo "---> Copying static files" + if [ -d /tmp/src/"${NGINX_STATIC_DIR}" ]; then + cp -Rf /tmp/src/"${NGINX_STATIC_DIR}"/. ./html + else + cp -Rf /tmp/src/. ./html + fi +} + +copy_default_config() { + cp /opt/app-root/etc/nginx.server.sample.conf /opt/app-root/etc/nginx.conf.d/default.conf +} + +copy_server_configs() { + echo "---> Copying nginx config" + rm /opt/app-root/etc/nginx.conf.d/default.conf + if [ ! -d /tmp/src/"${NGINX_STATIC_DIR}" ]; then + copy_default_config + elif [ -n "${NGINX_SERVER_CONF_FILE}" -a -f /tmp/src/"${NGINX_SERVER_CONF_FILE}" ]; then + cp /tmp/src/"${NGINX_SERVER_CONF_FILE}" /opt/app-root/etc/nginx.conf.d + elif [ -n "${NGINX_CONF_DIR}" -a -d /tmp/src/"${NGINX_CONF_DIR}" ]; then + cp -Rf /tmp/src/"${NGINX_CONF_DIR}"/. /opt/app-root/etc/nginx.conf.d + else + copy_default_config + fi +} + +copy_aux_dir() { + if [ -d /tmp/src/"${NGINX_AUX_DIR}" ]; then + echo "---> Copying auxiliary files" + mkdir /opt/app-root/etc/aux + cp -Rf /tmp/src/"${NGINX_AUX_DIR}"/. /opt/app-root/etc/aux + fi +} + +test_config() { + echo "---> testing config" + if ! $NGINX_BASE_DIR/usr/sbin/nginx -c /opt/app-root/etc/nginx.conf -t ; then + echo "nginx configuration not valid." + echo + for f in /opt/app-root/etc/nginx.conf.d/*; do + echo "==> $f <==" + cat $f + done + exit 1 + fi +} + +cleanup() { + echo "---> cleanup" + rm -f /opt/app-root/run/nginx.pid $NGINX_VAR_DIR/log/nginx/error.log +} + +copy_static_files +copy_server_configs +copy_aux_dir + +test_config +cleanup diff --git a/s2i-nginx/.s2i/bin/run b/s2i-nginx/.s2i/bin/run new file mode 100755 index 00000000..fa53d69c --- /dev/null +++ b/s2i-nginx/.s2i/bin/run @@ -0,0 +1,10 @@ +#!/bin/bash -e +# +# S2I run script for the 's2i-nginx' image. +# The run script executes the server that runs your application. +# +# For more information see the documentation: +# https://github.com/openshift/source-to-image/blob/master/docs/builder_image.md +# +NGINX_CONF=${NGINX_CONF-/opt/app-root/etc/nginx.conf} +exec /usr/sbin/nginx -c "$NGINX_CONF" diff --git a/s2i-nginx/.s2i/bin/usage b/s2i-nginx/.s2i/bin/usage new file mode 100755 index 00000000..fa54877c --- /dev/null +++ b/s2i-nginx/.s2i/bin/usage @@ -0,0 +1,12 @@ +#!/bin/bash -e +cat < s2i-nginx + +You can then run the resulting image via: +docker run +EOF diff --git a/s2i-nginx/Dockerfile b/s2i-nginx/Dockerfile new file mode 100644 index 00000000..7891de64 --- /dev/null +++ b/s2i-nginx/Dockerfile @@ -0,0 +1,38 @@ +FROM fedora + +MAINTAINER http://fedoraproject.org/wiki/Cloud + +ENV NGINX_VERSION="1.8" + +LABEL io.k8s.description="Platform for building nginx-based application (aka static content)" \ + io.k8s.display-name="Nginx 1.8 builder" \ + io.openshift.expose-services="8080:http" \ + io.openshift.tags="builder,nginx,nginx18" \ + Name="fedora-cloud/s2i-nginx" \ + Version="1.8" \ + Release="1" \ + Architecture="x86_64" + +RUN dnf install --setopt=tsflags=nodocs -y bcrypt nginx && \ + dnf update -y && \ + dnf clean all -y && \ + mkdir -p /opt/app-root/etc/nginx.conf.d && \ + chmod -R a+rx /var/lib/nginx && \ + chmod -R a+rwX /var/lib/nginx/tmp \ + /var/log \ + /var/run + +ENV STI_SCRIPTS_PATH="/usr/libexec/s2i" \ + STI_SCRIPTS_URL="image:///usr/libexec/s2i" + +COPY ./etc/ /opt/app-root/etc +COPY ./.s2i/bin/ ${STI_SCRIPTS_PATH} + +RUN cp /opt/app-root/etc/nginx.server.sample.conf /opt/app-root/etc/nginx.conf.d/default.conf && \ + chown -R 1001:1001 /opt/app-root + +USER 1001 + +EXPOSE 8080 + +CMD ["usage"] diff --git a/s2i-nginx/Makefile b/s2i-nginx/Makefile new file mode 100644 index 00000000..b97b160e --- /dev/null +++ b/s2i-nginx/Makefile @@ -0,0 +1,10 @@ + +IMAGE_NAME = s2i-nginx + +build: + docker build -t $(IMAGE_NAME) . + +.PHONY: test +test: + docker build -t $(IMAGE_NAME)-candidate . + IMAGE_NAME=$(IMAGE_NAME)-candidate test/run diff --git a/s2i-nginx/Readme.md b/s2i-nginx/Readme.md new file mode 100644 index 00000000..97706434 --- /dev/null +++ b/s2i-nginx/Readme.md @@ -0,0 +1,43 @@ +# Source-to-image builder for static nginx containers + +## Basic use case + +Have a git repo with a directory `html` (or `NGINX_STATIC_DIR`), in which all +static files to serve are. + +s2i-nginx will take all files within, copy them into the docker image and take +a basic nginx config that will simply serve these files. + +If there is no `html` directory, it will just copy all files in the repo. +In that case you will not be able to customize the nginx config. + + +## Configuring nginx + +You can supply a nginx.conf-snippet that will be used by the built container. + +If there is a directory `conf.d` containing (possibly multiple) nginx `server` +snippets these will be used. It will _not_ copy the default config, so be +sure to include the right files. See `etc/nginx.server.sample.conf` for the +default config. + + +## Auxiliary files + +You can put auxiliary files in a directory `aux` (or `NGINX_AUX_DIR`) to copy +them to the resulting image, e.g. htpasswd files. + +These will be copied to `/opt/app-root/etc/aux`. + + +## Environment variables + +There are some environment variables you can set to influence **build** behavior. + +`NGINX_STATIC_DIR` sets the repo subdir to use for static files, default +`html`. + +Either `NGINX_CONF_FILE` sets a config snippet to use or `NGINX_CONF_DIR` +will copy config from this dir (defaults to `conf.d`). + +`NGINX_AUX_DIR` sets the aux directory for auxiliary files. diff --git a/s2i-nginx/etc/nginx.conf b/s2i-nginx/etc/nginx.conf new file mode 100644 index 00000000..0de636d3 --- /dev/null +++ b/s2i-nginx/etc/nginx.conf @@ -0,0 +1,19 @@ +worker_processes 1; +pid /opt/app-root/run/nginx.pid; +error_log stderr notice; +daemon off; +events { + worker_connections 1024; +} +http { + include /etc/opt/rh/rh-nginx18/nginx/mime.types; + default_type application/octet-stream; + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + access_log off; + sendfile on; + keepalive_timeout 65; + + include /opt/app-root/etc/nginx.conf.d/*.conf; +} diff --git a/s2i-nginx/etc/nginx.server.sample.conf b/s2i-nginx/etc/nginx.server.sample.conf new file mode 100644 index 00000000..1bb0044f --- /dev/null +++ b/s2i-nginx/etc/nginx.server.sample.conf @@ -0,0 +1,16 @@ +server { + listen 8080; + server_name localhost; + location / { + root /opt/app-root/src/html; + index index.html index.htm; + } + error_page 404 /404.html; + location = /40x.html { + root /opt/app-root/src/html; + } + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /opt/app-root/src/html; + } +} diff --git a/s2i-nginx/test/run b/s2i-nginx/test/run new file mode 100755 index 00000000..84fb9431 --- /dev/null +++ b/s2i-nginx/test/run @@ -0,0 +1,154 @@ + +#!/bin/bash +# +# The 'run' performs a simple test that verifies the S2I image. +# The main focus here is to exercise the S2I scripts. +# +# For more information see the documentation: +# https://github.com/openshift/source-to-image/blob/master/docs/builder_image.md +# +# IMAGE_NAME specifies a name of the candidate image used for testing. +# The image has to be available before this script is executed. +# +IMAGE_NAME=${IMAGE_NAME-s2i-nginx-candidate} + +# Determining system utility executables (darwin compatibility check) +READLINK_EXEC="readlink" +MKTEMP_EXEC="mktemp" +if (echo "$OSTYPE" | egrep -qs 'darwin'); then + ! type -a "greadlink" &>"/dev/null" || READLINK_EXEC="greadlink" + ! type -a "gmktemp" &>"/dev/null" || MKTEMP_EXEC="gmktemp" +fi + +test_dir="$($READLINK_EXEC -zf $(dirname "${BASH_SOURCE[0]}"))" +image_dir=$($READLINK_EXEC -zf ${test_dir}/..) +scripts_url="file://${image_dir}/.s2i/bin" +cid_file=$($MKTEMP_EXEC -u --suffix=.cid) + +# Since we built the candidate image locally, we don't want S2I to attempt to pull +# it from Docker hub +s2i_args="--force-pull=false -s ${scripts_url} --loglevel=2" + +# Port the image exposes service to be tested +test_port=8080 + +image_exists() { + docker inspect $1 &>/dev/null +} + +container_exists() { + image_exists $(cat $cid_file) +} + +container_ip() { + docker inspect --format="{{ .NetworkSettings.IPAddress }}" $(cat $cid_file) +} + +run_s2i_build() { + s2i build --incremental=true ${s2i_args} file://${test_dir}/test-app ${IMAGE_NAME} ${IMAGE_NAME}-testapp +} + +prepare() { + if ! image_exists ${IMAGE_NAME}; then + echo "ERROR: The image ${IMAGE_NAME} must exist before this script is executed." + exit 1 + fi + # s2i build requires the application is a valid 'GIT' repository + pushd ${test_dir}/test-app >/dev/null + git init + git config user.email "build@localhost" && git config user.name "builder" + git add -A && git commit -m "Sample commit" + popd >/dev/null + run_s2i_build +} + +run_test_application() { + docker run --rm --cidfile=${cid_file} -p ${test_port} ${IMAGE_NAME}-testapp +} + +cleanup() { + if [ -f $cid_file ]; then + if container_exists; then + docker stop $(cat $cid_file) + fi + fi + if image_exists ${IMAGE_NAME}-testapp; then + docker rmi ${IMAGE_NAME}-testapp + fi +} + +check_result() { + local result="$1" + if [[ "$result" != "0" ]]; then + echo "S2I image '${IMAGE_NAME}' test FAILED (exit code: ${result})" + cleanup + exit $result + fi +} + +wait_for_cid() { + local max_attempts=10 + local sleep_time=1 + local attempt=1 + local result=1 + while [ $attempt -le $max_attempts ]; do + [ -f $cid_file ] && break + echo "Waiting for container to start..." + attempt=$(( $attempt + 1 )) + sleep $sleep_time + done +} + +test_usage() { + echo "Testing 's2i usage'..." + s2i usage ${s2i_args} ${IMAGE_NAME} &>/dev/null +} + +test_connection() { + echo "Testing HTTP connection..." + local max_attempts=10 + local sleep_time=1 + local attempt=1 + local result=1 + while [ $attempt -le $max_attempts ]; do + echo "Sending GET request to http://$(container_ip):${test_port}/" + if (echo "$OSTYPE" | egrep -qs 'darwin'); then + echo "Warning for OSX users: if you can't access the container's IP ${container_ip} directly (because you use boot2docker for example)" + echo "you should run the curl command in a container, for example using:" + echo "docker run --rm -it sequenceiq/alpine-curl curl -s -w %{http_code} -o /dev/null http://$(container_ip):${test_port}/" + fi + response_code=$(curl -s -w %{http_code} -o /dev/null http://$(container_ip):${test_port}/) + status=$? + if [ $status -eq 0 ]; then + if [ $response_code -eq 200 ]; then + result=0 + fi + break + fi + attempt=$(( $attempt + 1 )) + sleep $sleep_time + done + return $result +} + +# Build the application image twice to ensure the 'save-artifacts' and +# 'restore-artifacts' scripts are working properly +prepare +run_s2i_build +check_result $? + +# Verify the 'usage' script is working properly +test_usage +check_result $? + +# Verify that the HTTP connection can be established to test application container +run_test_application & + +# Wait for the container to write its CID file +wait_for_cid + +test_connection +check_result $? + +cleanup +