Initial commit
Simple virtualenv based docker builder
Not re-using saved artifacts yet
No application tester
Installs Jupyter Notebooks and JupyterHub by default
ADD
.gitignore
+.#*

ADD
ubuntu1610-python35-venv/Dockerfile
+FROM ubuntu:16.10
+
+MAINTAINER Yuvi Panda <yuvipanda@gmail.com>
+
+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

ADD
ubuntu1610-python35-venv/Makefile
+
+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

ADD
ubuntu1610-python35-venv/README.md
+
+# 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.

ADD
ubuntu1610-python35-venv/s2i/bin/assemble
+#!/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

ADD
ubuntu1610-python35-venv/s2i/bin/run
+#!/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

ADD
ubuntu1610-python35-venv/s2i/bin/save-artifacts
+#!/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 - <list of files and folders>

ADD
ubuntu1610-python35-venv/s2i/bin/usage
+#!/bin/bash -e
+cat <<EOF
+This is the ubuntu1610-python35-venv S2I image:
+To use it, install S2I: https://github.com/openshift/source-to-image
+
+Sample invocation:
+
+s2i build <source code path/URL> ubuntu1610-python35-venv <application image>
+
+You can then run the resulting image via:
+docker run <application image>
+EOF

ADD
ubuntu1610-python35-venv/test/run
+#!/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
+

ADD
ubuntu1610-python35-venv/test/test-app/index.html
+
+<!doctype html>
+<html>
+ <head>
+ <title>Hello World!</title>
+ </head>
+ <body>
+ <h1>Hello World!</h1>
+ </body>
+</html>
+
\ No newline at end of file

Initial commit

- Simple virtualenv based docker builder
- Not re-using saved artifacts yet
- No application tester
- Installs Jupyter Notebooks and JupyterHub by default