From 90b8c3d6015baf2f00cb172c2ff32f1056a099e9 Mon Sep 17 00:00:00 2001 From: yuvipanda Date: Wed, 5 Apr 2017 11:20:29 -0700 Subject: [PATCH] Initial commit - Simple virtualenv based docker builder - Not re-using saved artifacts yet - No application tester - Installs Jupyter Notebooks and JupyterHub by default --- .gitignore | 1 + ubuntu1610-python35-venv/Dockerfile | 27 +++ ubuntu1610-python35-venv/Makefile | 11 ++ ubuntu1610-python35-venv/README.md | 11 ++ ubuntu1610-python35-venv/s2i/bin/assemble | 26 +++ ubuntu1610-python35-venv/s2i/bin/run | 10 ++ .../s2i/bin/save-artifacts | 10 ++ ubuntu1610-python35-venv/s2i/bin/usage | 12 ++ ubuntu1610-python35-venv/test/run | 160 ++++++++++++++++++ .../test/test-app/index.html | 11 ++ 10 files changed, 279 insertions(+) create mode 100644 .gitignore create mode 100644 ubuntu1610-python35-venv/Dockerfile create mode 100644 ubuntu1610-python35-venv/Makefile create mode 100755 ubuntu1610-python35-venv/README.md create mode 100755 ubuntu1610-python35-venv/s2i/bin/assemble create mode 100755 ubuntu1610-python35-venv/s2i/bin/run create mode 100755 ubuntu1610-python35-venv/s2i/bin/save-artifacts create mode 100755 ubuntu1610-python35-venv/s2i/bin/usage create mode 100755 ubuntu1610-python35-venv/test/run create mode 100755 ubuntu1610-python35-venv/test/test-app/index.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..64598c69 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.#* diff --git a/ubuntu1610-python35-venv/Dockerfile b/ubuntu1610-python35-venv/Dockerfile new file mode 100644 index 00000000..fefae9e0 --- /dev/null +++ b/ubuntu1610-python35-venv/Dockerfile @@ -0,0 +1,27 @@ +FROM ubuntu:16.10 + +MAINTAINER Yuvi Panda + +LABEL io.openshift.s2i.scripts-url=image:///usr/libexec/s2i + +ENV APP_DIR /srv/app + +RUN apt-get update +RUN apt-get install --yes python3 python3-venv tar + + +RUN adduser --disabled-password --gecos "Default Jupyter user" jovyan + +RUN mkdir -p ${APP_DIR} && chown -R jovyan:jovyan /srv/app + +WORKDIR ${APP_DIR} + +USER jovyan +RUN python3 -m venv venv + +# FIXME: Specify actual versions here +RUN ${APP_DIR}/venv/bin/pip install --no-cache-dir jupyter jupyterhub + +COPY ./s2i/bin/ /usr/libexec/s2i + +EXPOSE 8888 diff --git a/ubuntu1610-python35-venv/Makefile b/ubuntu1610-python35-venv/Makefile new file mode 100644 index 00000000..a37b4445 --- /dev/null +++ b/ubuntu1610-python35-venv/Makefile @@ -0,0 +1,11 @@ + +IMAGE_NAME = ubuntu1610-python35-venv + +.PHONY: build +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/ubuntu1610-python35-venv/README.md b/ubuntu1610-python35-venv/README.md new file mode 100755 index 00000000..d37c8ee4 --- /dev/null +++ b/ubuntu1610-python35-venv/README.md @@ -0,0 +1,11 @@ + +# Ubuntu 16.10 Python3 Virtualenv Jupyter Builder + +This is a builder image for use with [s2i](https://github.com/openshift/source-to-image). It +builds a source repository (such as a github repository) into a usable Docker image, without +the user having to use or understand Docker. + +It is based off Ubuntu 16.10, and uses [virtualenv](https://pypi.python.org/pypi/virtualenv) to +provide a custom python3.5 environment. + +It looks for a `requirements.txt` file in the source repository, and installs it into the virtualenv. diff --git a/ubuntu1610-python35-venv/s2i/bin/assemble b/ubuntu1610-python35-venv/s2i/bin/assemble new file mode 100755 index 00000000..3b0cb785 --- /dev/null +++ b/ubuntu1610-python35-venv/s2i/bin/assemble @@ -0,0 +1,26 @@ +#!/bin/bash -e +# +# S2I assemble script for the 'ubuntu1610-python35-venv' image. +# The 'assemble' script builds your application source so that it is ready to run. +# +# For more information refer to the documentation: +# https://github.com/openshift/source-to-image/blob/master/docs/builder_image.md +# + +# If the 'ubuntu1610-python35-venv' assemble script is executed with the '-h' flag, print the usage. +if [[ "$1" == "-h" ]]; then + exec /usr/libexec/s2i/usage +fi + +# Restore artifacts from the previous build (if they exist). +# +if [ "$(ls /tmp/artifacts/ 2>/dev/null)" ]; then + echo "---> Restoring build artifacts..." + mv /tmp/artifacts/. ./ +fi + +echo "---> Installing application source..." +cp -Rf /tmp/src/. ./ + +echo "---> Building application from source..." +${APP_DIR}/venv/bin/pip install --no-cache-dir -r requirements.txt diff --git a/ubuntu1610-python35-venv/s2i/bin/run b/ubuntu1610-python35-venv/s2i/bin/run new file mode 100755 index 00000000..cc98dccb --- /dev/null +++ b/ubuntu1610-python35-venv/s2i/bin/run @@ -0,0 +1,10 @@ +#!/bin/bash -e +# +# S2I run script for the 'ubuntu1610-python35-venv' 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 +# + +exec ${APP_DIR}/venv/bin/jupyter notebook --ip=0.0.0.0 diff --git a/ubuntu1610-python35-venv/s2i/bin/save-artifacts b/ubuntu1610-python35-venv/s2i/bin/save-artifacts new file mode 100755 index 00000000..7359dfb5 --- /dev/null +++ b/ubuntu1610-python35-venv/s2i/bin/save-artifacts @@ -0,0 +1,10 @@ +#!/bin/sh -e +# +# S2I save-artifacts script for the 'ubuntu1610-python35-venv' image. +# The save-artifacts script streams a tar archive to standard output. +# The archive contains the files and folders you want to re-use in the next build. +# +# For more information see the documentation: +# https://github.com/openshift/source-to-image/blob/master/docs/builder_image.md +# +# tar cf - diff --git a/ubuntu1610-python35-venv/s2i/bin/usage b/ubuntu1610-python35-venv/s2i/bin/usage new file mode 100755 index 00000000..2aff1f76 --- /dev/null +++ b/ubuntu1610-python35-venv/s2i/bin/usage @@ -0,0 +1,12 @@ +#!/bin/bash -e +cat < ubuntu1610-python35-venv + +You can then run the resulting image via: +docker run +EOF diff --git a/ubuntu1610-python35-venv/test/run b/ubuntu1610-python35-venv/test/run new file mode 100755 index 00000000..57c952a4 --- /dev/null +++ b/ubuntu1610-python35-venv/test/run @@ -0,0 +1,160 @@ +#!/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-ubuntu1610-python35-venv-candidate} + +# Determining system utility executables (darwin compatibility check) +READLINK_EXEC="readlink" +MKTEMP_EXEC="mktemp" +if [[ "$OSTYPE" =~ '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="--pull-policy=never --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() { + if [ ! -z "$DOCKER_HOST" ] && [[ "$OSTYPE" =~ 'darwin' ]]; then + docker-machine ip + else + docker inspect --format="{{ .NetworkSettings.IPAddress }}" $(cat $cid_file) + fi +} + +container_port() { + if [ ! -z "$DOCKER_HOST" ] && [[ "$OSTYPE" =~ 'darwin' ]]; then + docker inspect --format="{{(index .NetworkSettings.Ports \"$test_port/tcp\" 0).HostPort}}" "$(cat "${cid_file}")" + else + echo $test_port + fi +} + +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 (http://$(container_ip):$(container_port))" + 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):$(container_port)/" + response_code=$(curl -s -w %{http_code} -o /dev/null http://$(container_ip):$(container_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 + diff --git a/ubuntu1610-python35-venv/test/test-app/index.html b/ubuntu1610-python35-venv/test/test-app/index.html new file mode 100755 index 00000000..1d35c559 --- /dev/null +++ b/ubuntu1610-python35-venv/test/test-app/index.html @@ -0,0 +1,11 @@ + + + + + Hello World! + + +

Hello World!

+ + + \ No newline at end of file