summaryrefslogtreecommitdiffstats
path: root/ansible
diff options
context:
space:
mode:
Diffstat (limited to 'ansible')
-rw-r--r--ansible/docker/.gitignore3
-rw-r--r--ansible/docker/Dockerfile34
-rwxr-xr-xansible/docker/build_ansible_image.sh52
-rwxr-xr-xansible/docker/create_docker_chroot.sh220
-rwxr-xr-xansible/docker/run_chroot.sh465
-rw-r--r--ansible/library/json_add.py90
-rw-r--r--ansible/library/rancher_k8s_environment.py341
-rw-r--r--ansible/roles/certificates/tasks/main.yml100
-rw-r--r--ansible/roles/certificates/tasks/upload_root_ca.yml10
-rw-r--r--ansible/roles/certificates/templates/v3.ext.j29
-rw-r--r--ansible/roles/kubectl/tasks/main.yml7
-rw-r--r--ansible/roles/nfs/defaults/main.yml5
-rw-r--r--ansible/roles/nfs/tasks/main.yml33
-rw-r--r--ansible/roles/nfs/templates/exports.j23
-rw-r--r--ansible/roles/rancher/tasks/main.yml2
-rw-r--r--ansible/roles/rancher/tasks/rancher_agent.yml13
-rw-r--r--ansible/roles/rancher/tasks/rancher_server.yml51
-rw-r--r--ansible/roles/rancher/templates/kube_config.j219
18 files changed, 1457 insertions, 0 deletions
diff --git a/ansible/docker/.gitignore b/ansible/docker/.gitignore
new file mode 100644
index 00000000..7df2d855
--- /dev/null
+++ b/ansible/docker/.gitignore
@@ -0,0 +1,3 @@
+ansible_docker.tar
+ansible_chroot.tgz
+ansible_chroot
diff --git a/ansible/docker/Dockerfile b/ansible/docker/Dockerfile
new file mode 100644
index 00000000..b0172709
--- /dev/null
+++ b/ansible/docker/Dockerfile
@@ -0,0 +1,34 @@
+FROM alpine:3.8
+
+ARG ansible_version=2.6.3
+LABEL ansible_version=$ansible_version vendor=Samsung
+
+# Install Ansible build dependencies
+RUN apk --no-cache update \
+&& apk --no-cache --update add --virtual build-dependencies \
+ gcc \
+ make \
+ musl-dev \
+ libffi-dev \
+ openssl-dev \
+ python3-dev \
+&& apk add --no-cache \
+ python3 \
+ py3-pip \
+ openssh-client \
+ openssl \
+ py3-openssl \
+ openssh \
+ sshpass \
+&& pip3 install --no-cache-dir --upgrade pip \
+&& pip3 install --no-cache-dir \
+ ansible==$ansible_version \
+ jmespath \
+&& apk del build-dependencies && rm -rf /var/cache/apk/*
+
+ENV ANSIBLE_HOST_KEY_CHECKING false
+ENV ANSIBLE_RETRY_FILES_ENABLED false
+
+WORKDIR /ansible
+
+ENTRYPOINT ["ansible-playbook"]
diff --git a/ansible/docker/build_ansible_image.sh b/ansible/docker/build_ansible_image.sh
new file mode 100755
index 00000000..d54ddc43
--- /dev/null
+++ b/ansible/docker/build_ansible_image.sh
@@ -0,0 +1,52 @@
+#! /usr/bin/env bash
+
+# COPYRIGHT NOTICE STARTS HERE
+
+# Copyright 2018 © Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# COPYRIGHT NOTICE ENDS HERE
+
+
+set -e
+
+ansible_version="$1"
+image_name="${2:-ansible:latest}"
+
+script_path=$(readlink -f "$0")
+script_dir=$(dirname "$script_path")
+
+git_commit=$(git rev-parse --revs-only HEAD)
+build_date=$(date -I)
+
+if [ -z "$ansible_version" ]; then
+ docker build "$script_dir" -t "${image_name}" --label "git-commit=$git_commit" --label "build-date=$build_date"
+else
+ docker build "$script_dir" -t "${image_name}" --label "git-commit=$git_commit" --label "build-date=$build_date" --build-arg ansible_version="$ansible_version"
+fi
+
+# Export docker image into chroot and tararchive it. It takes ~40M of space and is packaged together with sw.
+if "${script_dir}"/create_docker_chroot.sh convert "${image_name}" "${script_dir}"/ansible_chroot ; then
+ cd "$script_dir"
+ echo INFO: "Tarring and zipping the chroot directory..." >&2
+ tar -czf ansible_chroot.tgz ansible_chroot
+ rm -rf "${script_dir}"/ansible_chroot
+ echo INFO: "Finished: ${script_dir}/ansible_chroot.tgz" >&2
+ cd -
+else
+ echo ERROR: "I failed to create a chroot environment" >&2
+ exit 1
+fi
+
+exit 0 \ No newline at end of file
diff --git a/ansible/docker/create_docker_chroot.sh b/ansible/docker/create_docker_chroot.sh
new file mode 100755
index 00000000..f8e256da
--- /dev/null
+++ b/ansible/docker/create_docker_chroot.sh
@@ -0,0 +1,220 @@
+#!/bin/sh
+
+# COPYRIGHT NOTICE STARTS HERE
+
+# Copyright 2018 © Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# COPYRIGHT NOTICE ENDS HERE
+
+
+set -e
+
+CMD=$(basename "$0")
+
+help()
+{
+ echo "
+NAME:
+ ${CMD} - create a chroot directory from docker image
+
+DESCRIPTION:
+ It will export docker image into a directory capable of chrooting.
+ It needs and will run these commands (requires docker service):
+ docker create
+ docker export
+
+USAGE:
+ ${CMD} [-h|--help|help]
+ This help
+
+ ${CMD} convert <docker-name> <name-of-directory>
+
+ It will convert docker image into directory - no chroot yet.
+ The name of the docker image must be imported already (not a file):
+ docker image ls
+
+ The directory will be created and so this command will fail if some
+ directory or a file of this name (filepath) already exists!
+ There is another script run_chroot.sh with which you can do chroot
+ on this newly created directory - so it is expected that this
+ directory is kept clean and as it is.
+ If you don't care about this feature (run_chroot.sh) and you know
+ what are you doing, then do necessary mounts and execute:
+ chroot <name-of-directory>/chroot /bin/sh -l
+"
+}
+
+#
+# PLEASE DON'T TOUCH ME
+#
+
+# readme file for run_chroot.sh
+readme()
+{
+ md_codequote='```'
+
+cat > "$CHROOT_METADIR"/README.md <<EOF
+# RUN CHROOT COMMAND
+
+# usage:
+
+${md_codequote}
+run_chroot.sh help
+${md_codequote}
+
+**Don't modify insides of this directory (where this README.md lies).**
+
+The structure is needed as it is.
+
+If you wish to just run chroot by yourself, you can do:
+${md_codequote}
+chroot ./chroot /bin/sh -l
+${md_codequote}
+
+# requirements:
+
+* root privileges
+* docker service
+
+# directory structure:
+${md_codequote}
+ README.md
+ chroot/
+ .overlay
+ .workdir
+ .merged
+${md_codequote}
+EOF
+}
+
+# arg: <docker-name>
+check_docker_image()
+{
+ image="$1"
+ match=$(docker image ls --no-trunc -q "$image" | wc -l)
+
+ case $match in
+ 0)
+ echo ERROR: "Docker image does not exist: ${DOCKER_IMAGE}" >&2
+ exit 1
+ ;;
+ 1)
+ :
+ ;;
+ *)
+ echo ERROR: "Multiple results for this docker name: ${DOCKER_IMAGE}" >&2
+ exit 1
+ ;;
+ esac
+
+ return 0
+}
+
+cleanup()
+{
+ if [ -n "$DOCKER_CONTAINER" ] ; then
+ echo INFO: "Delete the export container: ${DOCKER_CONTAINER}" >&2
+ if ! docker rm "$DOCKER_CONTAINER" > /dev/null ; then
+ echo ERROR: "Failed to delete: ${DOCKER_CONTAINER}" >&2
+ fi
+ fi
+}
+
+on_exit()
+{
+ set +e
+ cleanup
+}
+
+action=nil
+case "$1" in
+ ''|-h|--help|help)
+ help
+ exit 0
+ ;;
+ convert)
+ action=convert
+ DOCKER_IMAGE="$2"
+ CHROOT_METADIR="$3"
+ ;;
+ *)
+ echo ERROR: "Bad usage" >&2
+ help >&2
+ exit 1
+ ;;
+esac
+
+
+case "$action" in
+ ''|nil)
+ echo ERROR: "Nothing to do - missing command" >&2
+ help >&2
+ exit 1
+ ;;
+ convert)
+ if [ -z "$DOCKER_IMAGE" ] || [ -z "$CHROOT_METADIR" ] ; then
+ echo ERROR: "Missing argument" >&2
+ help >&2
+ exit 1
+ fi
+
+ if [ -e "$CHROOT_METADIR" ] ; then
+ echo ERROR: "Filepath already exists: ${CHROOT_METADIR}" >&2
+ echo ERROR: "Please rename it, remove it or use different name" >&2
+ echo ERROR: "I need my working directory empty, thanks" >&2
+ exit 1
+ fi
+
+ # check if docker image is there
+ check_docker_image "$DOCKER_IMAGE"
+
+ # we must be root
+ if [ "$(id -u)" -ne 0 ] ; then
+ echo ERROR: "I need root privileges and you are not root: $(id -nu)" >&2
+ exit 1
+ fi
+
+ # making sure that CHROOT_METADIR is absolute path
+ CHROOT_METADIR=$(readlink -f "$CHROOT_METADIR")
+
+ # set trap
+ trap on_exit INT QUIT TERM EXIT
+
+ # making readme
+ mkdir -p "$CHROOT_METADIR"/
+ readme
+
+ # create container
+ DOCKER_CONTAINER=$(docker create "$DOCKER_IMAGE")
+ if [ -z "$DOCKER_CONTAINER" ] ; then
+ echo ERROR: "I could not create a container from: ${DOCKER_IMAGE}" >&2
+ exit 1
+ fi
+
+ # unpacking of image
+ mkdir -p "$CHROOT_METADIR"/chroot
+ echo INFO: "Export started - it can take a while to finish..." >&2
+ if ! docker export "$DOCKER_CONTAINER" | tar -C "$CHROOT_METADIR"/chroot -xf - ; then
+ echo ERROR: "Unpacking failed - permissions?" >&2
+ exit 1
+ else
+ echo INFO: "Export success: $CHROOT_METADIR/chroot" >&2
+ echo INFO: "Checkout the README file: $CHROOT_METADIR/README.md" >&2
+ fi
+ ;;
+esac
+
+exit 0
+
diff --git a/ansible/docker/run_chroot.sh b/ansible/docker/run_chroot.sh
new file mode 100755
index 00000000..b38c1295
--- /dev/null
+++ b/ansible/docker/run_chroot.sh
@@ -0,0 +1,465 @@
+#!/bin/sh
+
+# COPYRIGHT NOTICE STARTS HERE
+
+# Copyright 2018 © Samsung Electronics Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# COPYRIGHT NOTICE ENDS HERE
+
+
+set -e
+
+CMD=$(basename "$0")
+UMOUNT_TIMEOUT=120 # 2mins
+
+
+#
+# functions
+#
+
+help()
+{
+ echo "
+NAME:
+ ${CMD} - run command in chrooted directory
+
+DESCRIPTION:
+ It will do necessary steps to be able chroot, optional mounts and it will
+ run commands inside the requested chroot directory.
+
+ It does overlay mount so nothing inside the chroot is modified - if there
+ is no way to do overlay mount it will just do chroot directly - which means
+ that user has power to render chroot useless - beware...
+
+ The chroot is run in it's own namespace for better containerization.
+ Therefore the utility 'unshare' is necessary requirement.
+
+ After exiting the chroot all of those necessary steps are undone.
+
+USAGE:
+ ${CMD} [-h|--help|help]
+ This help
+
+ ${CMD} [OPTIONS] execute <chroot-directory> [<command with args>...]
+
+ It will do some necessary steps after which it will execute chroot
+ command and gives you prompt inside the chroot. When you leave the
+ prompt it will undo those steps.
+ On top of the ordinary chroot it will make overlay, so every change
+ inside the chroot is only temporary and chroot is kept stateless -
+ like inside a docker container. If there is no way to do overlay -
+ ordinary chroot is done.
+ Default command is: /bin/sh -l
+
+ OPTIONS:
+
+ --mount (ro|rw):<src-dir>:<inner-dir>
+ This option will mount 'src-dir' which is full path on the host
+ system into the relative path 'inner-dir' within the chroot
+ directory.
+ It can be mounted as read-only (ro) or read-write (rw).
+ Multiple usage of this argument can be used to create complex
+ hierarchy. Order is significant.
+ For example:
+ --mount ro:/scripts/ANSIBLE_DIR:/ansible \
+ --mount rw:/scripts/ANSIBLE_DIR/app:/ansible/app
+ This will mount directory ansible as read-only into chroot,
+ but it's subdirectory 'app' will be writeable.
+
+ --workdir <inner-dir>
+ This will set working directory (PWD) inside the chroot.
+
+EXAMPLE:
+ ${CMD} --mount ro:/scripts/ansible:ansible \
+ --mount rw:/scripts/ansible/app:ansible/app \
+ --workdir /ansible execute /tmp/ansible_chroot
+ # pwd
+ /ansible
+ # mount
+ overlay on / type overlay ...
+ /dev/disk on /ansible type ext4 (ro,relatime,errors=remount-ro)
+ /dev/disk on /ansible/application type ext4 (rw,relatime,errors=remount-ro)
+ none on /proc type proc (rw,relatime)
+ none on /sys type sysfs (rw,relatime)
+ none on /dev/shm type tmpfs (rw,relatime)
+
+ Directory /ansible inside the chroot is not writable but subdirectory
+ /ansible/app is.
+
+ Rest of the chroot is under overlay and all changes will be lost when
+ chroot command ends. Only changes in app directory persists bacause it
+ was bind mounted as read-write and is not part of overlay.
+
+ Note: as you can see app directory is mounted over itself but read-write.
+"
+}
+
+# arg: <directory>
+is_mounted()
+{
+ mountpoint=$(echo "$1" | sed 's#//*#/#g')
+
+ LANG=C mount | grep -q "^[^[:space:]]\+[[:space:]]\+on[[:space:]]\+${mountpoint}[[:space:]]\+type[[:space:]]\+"
+}
+
+# layers are right to left! First is on the right, top/last is on the left
+do_overlay_mount()
+{
+ if [ -d "$overlay" ] && is_mounted "$overlay" ; then
+ echo ERROR: "The overlay directory is already mounted: $overlay" >&2
+ echo ERROR: "Fix the issue - cannot proceed" >&2
+ exit 1
+ fi
+
+ # prepare dirs
+ rm -rf "$overlay" "$upperdir" "$workdir"
+ mkdir -p "$overlay"
+ mkdir -p "$upperdir"
+ mkdir -p "$workdir"
+
+ # finally overlay mount
+ if ! mount -t overlay --make-rprivate \
+ -o lowerdir="$lowerdir",upperdir="$upperdir",workdir="$workdir" \
+ overlay "$overlay" ;
+ then
+ echo ERROR: "Failed to do overlay mount!" >&2
+ echo ERROR: "Please check that your system supports overlay!" >&2
+ echo NOTE: "Continuing with the ordinary chroot without overlay!"
+
+ CHROOT_DIR="$lowerdir"
+ return 1
+ fi
+
+ CHROOT_DIR="$overlay"
+
+ return 0
+}
+
+cleanup()
+{
+ case "$OVERLAY_MOUNT" in
+ yes)
+ echo INFO: "Umounting overlay..." >&2
+ if ! umount_retry "$CHROOT_DIR" ; then
+ echo ERROR: "Cannot umount chroot: $CHROOT_DIR" >&2
+ return 1
+ fi
+
+ ;;
+ no)
+ echo INFO: "No overlay to umount" >&2
+ ;;
+ esac
+
+ if ! is_mounted "$overlay" ; then
+ echo INFO: "Deleting of temp directories..." >&2
+ rm -rf "$overlay" "$upperdir" "$workdir"
+ else
+ echo ERROR: "Overlay is still mounted: $CHROOT_DIR" >&2
+ echo ERROR: "Cannot delete: $overlay" >&2
+ echo ERROR: "Cannot delete: $upperdir" >&2
+ echo ERROR: "Cannot delete: $workdir" >&2
+ return 1
+ fi
+}
+
+check_external_mounts()
+{
+ echo "$EXTERNAL_MOUNTS" | sed '/^[[:space:]]*$/d' | while read -r mountexpr ; do
+ mount_type=$(echo "$mountexpr" | awk 'BEGIN{FS=":"}{print $1;}')
+ external=$(echo "$mountexpr" | awk 'BEGIN{FS=":"}{print $2;}')
+ internal=$(echo "$mountexpr" | awk 'BEGIN{FS=":"}{print $3;}' | sed -e 's#^/*##' -e 's#//*#/#g')
+
+ case "$mount_type" in
+ ro|rw)
+ :
+ ;;
+ *)
+ echo ERROR: "Wrong mount type (should be 'ro' or 'rw') in: ${mountexpr}" >&2
+ exit 1
+ ;;
+ esac
+
+ if ! [ -d "$external" ] ; then
+ echo ERROR: "Directory for mounting does not exist: ${external}" >&2
+ exit 1
+ fi
+
+ if echo "$internal" | grep -q '^/*$' ; then
+ echo ERROR: "Unacceptable internal path: ${internal}" >&2
+ exit 1
+ fi
+ done
+}
+
+do_external_mounts()
+{
+ echo INFO: "Bind mounting of external mounts..." >&2
+ echo "$EXTERNAL_MOUNTS" | sed '/^[[:space:]]*$/d' | while read -r mountexpr ; do
+ mount_type=$(echo "$mountexpr" | awk 'BEGIN{FS=":"}{print $1;}')
+ external=$(echo "$mountexpr" | awk 'BEGIN{FS=":"}{print $2;}')
+ internal=$(echo "$mountexpr" | awk 'BEGIN{FS=":"}{print $3;}' | sed -e 's#^/*##' -e 's#//*#/#g')
+
+ if is_mounted "${CHROOT_DIR}/${internal}" ; then
+ echo ERROR: "Mountpoint is already mounted: ${CHROOT_DIR}/${internal}" >&2
+ echo ERROR: "Fix the issue - cannot proceed" >&2
+ exit 1
+ fi
+
+ if ! mkdir -p "${CHROOT_DIR}/${internal}" ; then
+ echo ERROR: "Cannot create mountpoint: ${CHROOT_DIR}/${internal}" >&2
+ exit 1
+ fi
+
+ if ! mount --make-rprivate -o bind,${mount_type} "$external" "${CHROOT_DIR}/${internal}" ; then
+ echo ERROR: "Failed to mount: ${external} -> ${internal}" >&2
+ exit 1
+ else
+ echo INFO: "Mount: ${external} -> ${internal}" >&2
+ fi
+ done
+}
+
+# arg: <mountpoint>
+umount_retry()
+{
+ mountpoint=$(echo "$1" | sed 's#//*#/#g')
+ timeout=${UMOUNT_TIMEOUT}
+
+ umount "$mountpoint" 2>/dev/null
+ while is_mounted "$mountpoint" && [ $timeout -gt 0 ] ; do
+ umount "$mountpoint" 2>/dev/null
+ sleep 1
+ timeout=$(( timeout - 1 ))
+ done
+
+ if ! is_mounted "$mountpoint" ; then
+ return 0
+ fi
+
+ return 1
+}
+
+undo_external_mounts()
+{
+ echo INFO: "Umount external mount points..." >&2
+ echo "$EXTERNAL_MOUNTS" | tac | sed '/^[[:space:]]*$/d' | while read -r mountexpr ; do
+ mount_type=$(echo "$mountexpr" | awk 'BEGIN{FS=":"}{print $1;}')
+ external=$(echo "$mountexpr" | awk 'BEGIN{FS=":"}{print $2;}')
+ internal=$(echo "$mountexpr" | awk 'BEGIN{FS=":"}{print $3;}' | sed -e 's#^/*##' -e 's#//*#/#g')
+ if umount_retry "${CHROOT_DIR}/${internal}" ; then
+ echo INFO: "Unmounted: ${CHROOT_DIR}/${internal}" >&2
+ else
+ echo ERROR: "Failed to umount: ${CHROOT_DIR}/${internal}" >&2
+ fi
+ done
+}
+
+install_wrapper()
+{
+ cat > "$CHROOT_DIR"/usr/local/bin/fakeshell.sh <<EOF
+#!/bin/sh
+
+PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+export PATH
+
+gid_tty=\$(getent group | sed -n '/^tty:/p' | cut -d: -f 3)
+
+mount -t proc proc /proc
+mount -t sysfs none /sys
+mount -t tmpfs none /dev
+
+mkdir -p /dev/shm
+mkdir -p /dev/pts
+mount -t devpts -o gid=\${gid_tty},mode=620 none /dev/pts
+
+[ -e /dev/full ] || mknod -m 666 /dev/full c 1 7
+[ -e /dev/ptmx ] || mknod -m 666 /dev/ptmx c 5 2
+[ -e /dev/random ] || mknod -m 644 /dev/random c 1 8
+[ -e /dev/urandom ] || mknod -m 644 /dev/urandom c 1 9
+[ -e /dev/zero ] || mknod -m 666 /dev/zero c 1 5
+[ -e /dev/tty ] || mknod -m 666 /dev/tty c 5 0
+[ -e /dev/console ] || mknod -m 622 /dev/console c 5 1
+[ -e /dev/null ] || mknod -m 666 /dev/null c 1 3
+
+chown root:tty /dev/console
+chown root:tty /dev/ptmx
+chown root:tty /dev/tty
+
+mkdir -p "\$1" || exit 1
+cd "\$1" || exit 1
+shift
+
+exec "\$@"
+
+EOF
+ chmod +x "$CHROOT_DIR"/usr/local/bin/fakeshell.sh
+}
+
+on_exit()
+{
+ set +e
+ echo
+
+ if [ -n "$OVERLAY_MOUNT" ] ; then
+ undo_external_mounts
+ fi
+ cleanup
+}
+
+
+#
+# parse arguments
+#
+
+state=nil
+action=nil
+EXTERNAL_MOUNTS=''
+CHROOT_WORKDIR=''
+CHROOT_METADIR=''
+CHROOT_DIR=''
+COMMAND=''
+while [ -n "$1" ] ; do
+ case "$state" in
+ nil)
+ case "$1" in
+ ''|-h|--help|help)
+ help
+ exit 0
+ ;;
+ --mount)
+ EXTERNAL_MOUNTS=$(printf "%s\n%s\n" "$EXTERNAL_MOUNTS" "${2}")
+ state=next
+ ;;
+ --workdir)
+ if [ -z "$CHROOT_WORKDIR" ] ; then
+ CHROOT_WORKDIR="$2"
+ state=next
+ else
+ echo ERROR: "Multiple working directory argument" >&2
+ help >&2
+ exit 1
+ fi
+ ;;
+ execute)
+ action=execute
+ state=execute
+ ;;
+ *)
+ echo ERROR: "Bad usage" >&2
+ help >&2
+ exit 1
+ ;;
+ esac
+ ;;
+ next)
+ state=nil
+ ;;
+ execute)
+ CHROOT_METADIR="$1"
+ shift
+ break
+ ;;
+ esac
+ shift
+done
+
+
+case "$action" in
+ ''|nil)
+ echo ERROR: "Nothing to do - missing command" >&2
+ help >&2
+ exit 1
+ ;;
+ execute)
+ # firstly do sanity checking ...
+
+ if [ -z "$CHROOT_METADIR" ] ; then
+ echo ERROR: "Missing argument" >&2
+ help >&2
+ exit 1
+ fi
+
+ # making sure that CHROOT_METADIR is absolute path
+ CHROOT_METADIR=$(readlink -f "$CHROOT_METADIR")
+
+ if ! [ -d "$CHROOT_METADIR"/chroot ] ; then
+ echo ERROR: "Filepath does not exist: ${CHROOT_METADIR}/chroot" >&2
+ exit 1
+ fi
+
+ # check external mounts if there are any
+ check_external_mounts
+
+ # check workdir
+ if [ -n "$CHROOT_WORKDIR" ] ; then
+ CHROOT_WORKDIR=$(echo "$CHROOT_WORKDIR" | sed -e 's#^/*##' -e 's#//*#/#g')
+ fi
+
+ # we must be root
+ if [ "$(id -u)" -ne 0 ] ; then
+ echo ERROR: "Need to be root and you are not: $(id -nu)" >&2
+ exit 1
+ fi
+
+ if ! which unshare >/dev/null 2>/dev/null ; then
+ echo ERROR: "'unshare' system command is missing - ABORT" >&2
+ echo INFO: "Try to install 'util-linux' package" >&2
+ exit 1
+ fi
+
+ # ... sanity checking done
+
+ # setup paths
+ lowerdir="$CHROOT_METADIR"/chroot
+ upperdir="$CHROOT_METADIR"/.overlay
+ workdir="$CHROOT_METADIR"/.workdir
+ overlay="$CHROOT_METADIR"/.merged
+
+ # set trap
+ trap on_exit QUIT TERM EXIT
+
+ # mount overlay
+ OVERLAY_MOUNT=''
+ if do_overlay_mount ; then
+ # overlay chroot
+ OVERLAY_MOUNT=yes
+ else
+ # non overlay mount
+ OVERLAY_MOUNT=no
+ fi
+
+ # do the user-specific mounts
+ do_external_mounts
+
+ # I need this wrapper to do some setup inside the chroot...
+ install_wrapper
+
+ # execute chroot
+ # copy resolv.conf
+ cp -a /etc/resolv.conf "$CHROOT_DIR"/etc/resolv.conf
+
+ if [ -n "$1" ] ; then
+ :
+ else
+ set -- /bin/sh -l
+ fi
+ unshare -mfpi --propagation private \
+ chroot "$CHROOT_DIR" /usr/local/bin/fakeshell.sh "${CHROOT_WORKDIR:-/}" "$@"
+ ;;
+esac
+
+exit 0
+
diff --git a/ansible/library/json_add.py b/ansible/library/json_add.py
new file mode 100644
index 00000000..6aad2d7c
--- /dev/null
+++ b/ansible/library/json_add.py
@@ -0,0 +1,90 @@
+#!/usr/bin/python
+
+from ansible.module_utils.basic import AnsibleModule
+import json
+import os
+
+DOCUMENTATION="""
+---
+module: json_add
+descritption:
+ - This module will search top level objects in json and adds specified
+ value into list for specified key.
+ - If file does not exists module will create it automatically.
+
+options:
+ path:
+ required: true
+ aliases=[name, destfile, dest]
+ description:
+ - The json file to modify.
+ key:
+ required: true
+ description:
+ - Top level object.
+ value:
+ required: true
+ description:
+ - Value to add to specified key.
+"""
+
+def load_json(path):
+ if os.path.exists(path):
+ with open(path, 'r') as f:
+ return json.load(f)
+ else:
+ return {}
+
+def value_is_set(path, key, value, json_obj):
+ return value in json_obj.get(key, [])
+
+def insert_to_json(path, key, value, check_mode=False):
+ json_obj = load_json(path)
+ if not value_is_set(path, key, value, json_obj):
+ if not check_mode:
+ json_obj.setdefault(key, []).append(value)
+ store_json(path, json_obj)
+ return True, 'Value %s added to %s.' % (value, key)
+ else:
+ return False, ''
+
+def store_json(path, json_obj):
+ with open(path, 'w') as f:
+ json.dump(json_obj, f, indent=4)
+
+def check_file_attrs(module, changed, message, diff):
+ file_args = module.load_file_common_arguments(module.params)
+ if module.set_fs_attributes_if_different(file_args, False, diff=diff):
+
+ if changed:
+ message += ' '
+ changed = True
+ message += 'File attributes changed.'
+
+ return changed, message
+
+def run_module():
+ module = AnsibleModule(
+ argument_spec=dict(
+ path=dict(type='path', required=True, aliases=['name', 'destfile', 'dest']),
+ key=dict(type='str', required=True),
+ value=dict(type='str', required=True),
+ ),
+ add_file_common_args=True,
+ supports_check_mode=True
+ )
+ params = module.params
+ path = params['path']
+ key = params['key']
+ value = params['value']
+ try:
+ changed, msg = insert_to_json(path, key, value, module.check_mode)
+ fs_diff = {}
+ changed, msg = check_file_attrs(module, changed, msg, fs_diff)
+ module.exit_json(changed=changed, msg=msg, file_attr_diff=fs_diff)
+ except IOError as e:
+ module.fail_json(msg=e.msg)
+
+if __name__ == '__main__':
+ run_module()
+
diff --git a/ansible/library/rancher_k8s_environment.py b/ansible/library/rancher_k8s_environment.py
new file mode 100644
index 00000000..d3d8ac02
--- /dev/null
+++ b/ansible/library/rancher_k8s_environment.py
@@ -0,0 +1,341 @@
+#!/usr/bin/python
+
+DOCUMENTATION='''
+---
+module: rancher_k8s_environment
+description:
+ - This module will create or delete Kubernetes environment.
+ - It will also delete other environments when variables are set accordingly.
+notes:
+ - It identifies environment only by name. Expect problems with same named environments.
+ - All hosts running Kubernetes cluster should have same OS otherwise there
+ is possibility of misbehavement.
+options:
+ server:
+ required: true
+ description:
+ - Url of rancher server i.e. "http://10.0.0.1:8080".
+ name:
+ required: true
+ descritpion:
+ - Name of the environment to create/remove.
+ descr:
+ description:
+ - Description of environment to create.
+ state:
+ description:
+ - If "present" environment will be created or setup depending if it exists.
+ With multiple environments with same name expect error.
+ If "absent" environment will be removed. If multiple environments have same
+ name all will be deleted.
+ default: present
+ choices: [present, absent]
+ delete_not_k8s:
+ description:
+ - Indicates if environments with different orchestration than Kubernetes should
+ be deleted.
+ type: bool
+ default: yes
+ delete_other_k8s:
+ description:
+ - Indicates if environments with different name than specified should
+ be deleted.
+ type: bool
+ default: no
+ force:
+ description:
+ - Indicates if environment should be deleted and recreated.
+ type: bool
+ default: yes
+ host_os:
+ required: true
+ description:
+ - OS (family from ansible_os_family variable) of the hosts running cluster. If
+ "RedHat" then datavolume fix will be applied.
+ Fix described here:
+ https://github.com/rancher/rancher/issues/10015
+'''
+
+import json
+import time
+
+import requests
+from ansible.module_utils.basic import AnsibleModule
+
+
+
+def get_existing_environments(rancher_address):
+ req = requests.get('{}/v2-beta/projects'.format(rancher_address))
+ envs = req.json()['data']
+ return envs
+
+
+def not_k8s_ids(environments):
+ envs = filter(lambda x: x['orchestration'] != 'kubernetes', environments)
+ return [env['id'] for env in envs]
+
+
+def other_k8s_ids(environments, name):
+ envs = filter(lambda x: x['orchestration'] == 'kubernetes' and x['name'] != name,
+ environments)
+ return [env['id'] for env in envs]
+
+
+def env_ids_by_name(environments, name):
+ envs = filter(lambda x: x['name'] == name, environments)
+ return [env['id'] for env in envs]
+
+
+def env_info_by_id(environments, env_id):
+ env = filter(lambda x: x['id'] == env_id, environments)
+ return [{'id': x['id'], 'name': x['name']} for x in env][0]
+
+
+def delete_multiple_environments(rancher_address, env_ids):
+ deleted = []
+ for env_id in env_ids:
+ deleted.append(delete_environment(rancher_address, env_id))
+ return deleted
+
+
+def delete_environment(rancher_address, env_id):
+ req = requests.delete('{}/v2-beta/projects/{}'.format(rancher_address, env_id))
+ deleted = req.json()['data'][0]
+ return {'id': deleted['id'],
+ 'name': deleted['name'],
+ 'orchestration': deleted['orchestration']}
+
+
+def create_k8s_environment(rancher_address, name, descr):
+ k8s_template_id = None
+ for _ in range(10):
+ k8s_template = requests.get(
+ '{}/v2-beta/projecttemplates?name=Kubernetes'.format(rancher_address)).json()
+ if k8s_template['data']:
+ k8s_template_id = k8s_template['data'][0]['id']
+ break
+ time.sleep(3)
+ if k8s_template_id is None:
+ raise ValueError('Template for kubernetes not found.')
+ body = {
+ 'name': name,
+ 'description': descr,
+ 'projectTemplateId': k8s_template_id,
+ 'allowSystemRole': False,
+ 'members': [],
+ 'virtualMachine': False,
+ 'servicesPortRange': None,
+ 'projectLinks': []
+ }
+
+ body_json = json.dumps(body)
+ req = requests.post('{}/v2-beta/projects'.format(rancher_address), data=body_json)
+ created = req.json()
+ return {'id': created['id'], 'name': created['name']}
+
+
+def get_kubelet_service(rancher_address, env_id):
+ for _ in range(10):
+ response = requests.get(
+ '{}/v2-beta/projects/{}/services/?name=kubelet'.format(rancher_address,
+ env_id))
+
+ if response.status_code >= 400:
+ # too early or too late for obtaining data
+ # small delay will improve our chances to collect it
+ time.sleep(1)
+ continue
+
+ content = response.json()
+
+ if content['data']:
+ return content['data'][0]
+
+ # this is unfortunate, response from service api received but data
+ # not available, lets try again
+ time.sleep(5)
+
+ return None
+
+
+def fix_datavolume_rhel(rancher_address, env_id):
+ kubelet_svc = get_kubelet_service(rancher_address, env_id)
+ if kubelet_svc:
+ try:
+ data_volume_index = kubelet_svc['launchConfig']['dataVolumes'].index(
+ '/sys:/sys:ro,rprivate')
+ except ValueError:
+ return 'Already changed'
+ kubelet_svc['launchConfig']['dataVolumes'][
+ data_volume_index] = '/sys/fs/cgroup:/sys/fs/cgroup:ro,rprivate'
+ body = {
+ 'inServiceStrategy': {
+ 'batchSize': 1,
+ 'intervalMillis': 2000,
+ 'startFirst': False,
+ 'launchConfig': kubelet_svc['launchConfig'],
+ 'secondaryLaunchConfigs': []
+ }
+ }
+ body_json = json.dumps(body)
+ requests.post(
+ '{}/v2-beta/projects/{}/services/{}?action=upgrade'.format(rancher_address,
+ env_id,
+ kubelet_svc[
+ 'id']),
+ data=body_json)
+ for _ in range(10):
+ req_svc = requests.get(
+ '{}/v2-beta/projects/{}/services/{}'.format(rancher_address, env_id,
+ kubelet_svc['id']))
+ req_svc_content = req_svc.json()
+ if 'finishupgrade' in req_svc_content['actions']:
+ req_finish = requests.post(
+ req_svc_content['actions']['finishupgrade'])
+ return {
+ 'dataVolumes': req_finish.json()['upgrade']['inServiceStrategy'][
+ 'launchConfig']['dataVolumes']}
+ time.sleep(5)
+ else:
+ raise ValueError('Could not get kubelet service')
+
+
+def create_registration_tokens(rancher_address, env_id):
+ body = {'name': str(env_id)}
+ body_json = json.dumps(body)
+ response = requests.post(
+ '{}/v2-beta/projects/{}/registrationtokens'.format(rancher_address, env_id,
+ data=body_json))
+ for _ in range(10):
+ tokens = requests.get(response.json()['links']['self'])
+ tokens_content = tokens.json()
+ if tokens_content['image'] is not None and tokens_content[
+ 'registrationUrl'] is not None:
+ return {'image': tokens_content['image'],
+ 'reg_url': tokens_content['registrationUrl']}
+ time.sleep(3)
+ return None
+
+
+def get_registration_tokens(rancher_address, env_id):
+ reg_tokens = requests.get(
+ '{}/v2-beta/projects/{}/registrationtokens'.format(rancher_address, env_id))
+ reg_tokens_content = reg_tokens.json()
+ tokens = reg_tokens_content['data']
+ if not tokens:
+ return None
+ return {'image': tokens[0]['image'], 'reg_url': tokens[0]['registrationUrl']}
+
+
+def create_apikey(rancher_address, env_id):
+ body = {
+ 'name': 'kubectl_env_{}'.format(env_id),
+ 'description': "Provides access to kubectl"
+ }
+ body_json = json.dumps(body)
+ apikey_req = requests.post(
+ '{}/v2-beta/apikey'.format(rancher_address, env_id, data=body_json))
+ apikey_content = apikey_req.json()
+ return {'public': apikey_content['publicValue'],
+ 'private': apikey_content['secretValue']}
+
+
+def run_module():
+ module = AnsibleModule(
+ argument_spec=dict(
+ server=dict(type='str', required=True),
+ name=dict(type='str', required=True),
+ descr=dict(type='str'),
+ state=dict(type='str', choices=['present', 'absent'], default='present'),
+ delete_other_k8s=dict(type='bool', default=False),
+ delete_not_k8s=dict(type='bool', default=True),
+ force=dict(type='bool', default=True),
+ host_os=dict(type='str', required=True)
+ )
+ )
+
+ params = module.params
+ rancher_address = params['server']
+ name = params['name']
+ descr = params['descr']
+ delete_not_k8s = params['delete_not_k8s']
+ delete_other_k8s = params['delete_other_k8s']
+ force = params['force']
+ host_os = params['host_os']
+ state = params['state']
+
+ existing_envs = get_existing_environments(rancher_address)
+ same_name_ids = env_ids_by_name(existing_envs, name)
+
+ to_delete_ids = []
+ changes = {}
+
+ if delete_other_k8s:
+ to_delete_ids += other_k8s_ids(existing_envs, name)
+
+ if delete_not_k8s:
+ to_delete_ids += not_k8s_ids(existing_envs)
+ if force or state == 'absent':
+ to_delete_ids += same_name_ids
+
+ deleted = delete_multiple_environments(rancher_address, to_delete_ids)
+
+ if deleted:
+ changes['deleted'] = deleted
+ if state == 'absent':
+ module.exit_json(changed=True, deleted=changes['deleted'])
+ else:
+ if state == 'absent':
+ module.exit_json(changed=False)
+
+ if len(same_name_ids) > 1 and not force:
+ module.fail_json(msg='Multiple environments with same name. '
+ 'Use "force: yes" to delete '
+ 'all environments with same name.')
+
+ if same_name_ids and not force:
+ changes['environment'] = env_info_by_id(existing_envs, same_name_ids[0])
+ if host_os == 'RedHat':
+ try:
+ rhel_fix = fix_datavolume_rhel(rancher_address, same_name_ids[0])
+ changes['rhel_fix'] = rhel_fix
+ except ValueError as err:
+ module.fail_json(
+ msg='Error: {} Try to recreate k8s environment.'.format(err))
+
+ reg_tokens = get_registration_tokens(rancher_address, same_name_ids[0])
+ if not reg_tokens:
+ reg_tokens = create_registration_tokens(rancher_address, same_name_ids[0])
+ changes['registration_tokens'] = reg_tokens
+
+ apikey = create_apikey(rancher_address, same_name_ids[0])
+ changes['apikey'] = apikey
+ module.exit_json(changed=True, data=changes,
+ msg='New environment was not created. Only set up was done')
+ try:
+ new_env = create_k8s_environment(rancher_address, name, descr)
+ except ValueError as err:
+ module.fail_json(msg='Error: {} Try to recreate k8s environment.'.format(err))
+
+ if host_os == 'RedHat':
+ try:
+ rhel_fix = fix_datavolume_rhel(rancher_address, new_env['id'])
+ changes['rhel_fix'] = rhel_fix
+ except ValueError as err:
+ module.fail_json(msg='Error: {} Try to recreate k8s environment.'.format(
+ err))
+
+ reg_tokens = create_registration_tokens(rancher_address, new_env['id'])
+
+ apikey = create_apikey(rancher_address, new_env['id'])
+
+ changes['environment'] = new_env
+ changes['registration_tokens'] = reg_tokens
+ changes['apikey'] = apikey
+
+ module.exit_json(changed=True, data=changes)
+
+
+if __name__ == '__main__':
+ run_module()
+
diff --git a/ansible/roles/certificates/tasks/main.yml b/ansible/roles/certificates/tasks/main.yml
new file mode 100644
index 00000000..2e7dd88a
--- /dev/null
+++ b/ansible/roles/certificates/tasks/main.yml
@@ -0,0 +1,100 @@
+---
+# Some of task are delegated to Ansible container because unavailable
+# version of python-pyOpenSSL
+- name: Generate root CA private key
+ openssl_privatekey:
+ path: /certs/rootCA.key
+ size: 4096
+ delegate_to: localhost
+
+- name: Generate an OpenSSL CSR.
+ openssl_csr:
+ path: /certs/rootCA.csr
+ privatekey_path: /certs/rootCA.key
+ organization_name: "{{ certificates.organization_name }}"
+ state_or_province_name: "{{ certificates.state_or_province_name }}"
+ country_name: "{{ certificates.country_name }}"
+ locality_name: "{{ certificates.locality_name }}"
+ basic_constraints:
+ - CA:true
+ basic_constraints_critical: yes
+ key_usage:
+ - critical
+ - digitalSignature
+ - cRLSign
+ - keyCertSign
+ delegate_to: localhost
+
+- name: Generate root CA certificate
+ openssl_certificate:
+ provider: selfsigned
+ path: /certs/rootCA.crt
+ csr_path: /certs/rootCA.csr
+ privatekey_path: /certs/rootCA.key
+ key_usage:
+ - critical
+ - digitalSignature
+ - cRLSign
+ - keyCertSign
+ force: yes
+ delegate_to: localhost
+ notify: Restart Docker
+
+- name: Generate private Nexus key
+ openssl_privatekey:
+ path: /certs/nexus_server.key
+ size: 4096
+ force: False
+ delegate_to: localhost
+
+- name: Generate Nexus CSR (certificate signing request)
+ openssl_csr:
+ path: /certs/nexus_server.csr
+ privatekey_path: /certs/nexus_server.key
+ organization_name: "{{ certificates.organization_name }}"
+ state_or_province_name: "{{ certificates.state_or_province_name }}"
+ country_name: "{{ certificates.country_name }}"
+ locality_name: "{{ certificates.locality_name }}"
+ common_name: registry-1.docker.io
+ key_usage:
+ - keyAgreement
+ - nonRepudiation
+ - digitalSignature
+ - keyEncipherment
+ - dataEncipherment
+ extended_key_usage:
+ - serverAuth
+ subject_alt_name:
+ "{{ simulated_hosts | map('regex_replace', '(.*)', 'DNS:\\1') | list }}"
+ delegate_to: localhost
+
+- name: Generate v3 extension config file
+ template:
+ src: v3.ext.j2
+ dest: /certs/v3.ext
+ delegate_to: localhost
+
+# Signing certificate is added to Ansible in version 2.7 (release date 04.10.2018)
+# Currently using 2.6.3
+- name: Sign Nexus certificate
+ command: >
+ openssl
+ x509
+ -req
+ -in /certs/nexus_server.csr
+ -extfile /certs/v3.ext
+ -CA /certs/rootCA.crt
+ -CAkey /certs/rootCA.key
+ -CAcreateserial
+ -out /certs/nexus_server.crt
+ -days 3650
+ -sha256
+ delegate_to: localhost
+
+- name: Upload certificates to infrastructure server
+ copy:
+ src: /certs
+ directory_mode: yes
+ dest: "{{ app_data_path }}/"
+
+- import_tasks: upload_root_ca.yml
diff --git a/ansible/roles/certificates/tasks/upload_root_ca.yml b/ansible/roles/certificates/tasks/upload_root_ca.yml
new file mode 100644
index 00000000..5a59d27b
--- /dev/null
+++ b/ansible/roles/certificates/tasks/upload_root_ca.yml
@@ -0,0 +1,10 @@
+---
+- name: Copy root certificate
+ copy:
+ src: "/certs/rootCA.crt"
+ dest: /etc/pki/ca-trust/source/anchors/
+ notify: Restart Docker
+
+- name: Extract root certificate
+ command: /usr/bin/update-ca-trust extract
+ notify: Restart Docker
diff --git a/ansible/roles/certificates/templates/v3.ext.j2 b/ansible/roles/certificates/templates/v3.ext.j2
new file mode 100644
index 00000000..7be946fd
--- /dev/null
+++ b/ansible/roles/certificates/templates/v3.ext.j2
@@ -0,0 +1,9 @@
+authorityKeyIdentifier=keyid,issuer
+basicConstraints=CA:FALSE
+keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
+subjectAltName = @alt_names
+
+[alt_names]
+{% for name in all_simulated_hosts -%}
+ DNS.{{ loop.index }} = {{ name }}
+{% endfor %}
diff --git a/ansible/roles/kubectl/tasks/main.yml b/ansible/roles/kubectl/tasks/main.yml
new file mode 100644
index 00000000..7c77c3c5
--- /dev/null
+++ b/ansible/roles/kubectl/tasks/main.yml
@@ -0,0 +1,7 @@
+---
+- name: Install kubectl
+ copy:
+ src: "{{ app_data_path }}/downloads/kubectl"
+ dest: "{{ kubectl_bin_dir }}/kubectl"
+ remote_src: true
+ mode: 0755
diff --git a/ansible/roles/nfs/defaults/main.yml b/ansible/roles/nfs/defaults/main.yml
new file mode 100644
index 00000000..a5e1d2a5
--- /dev/null
+++ b/ansible/roles/nfs/defaults/main.yml
@@ -0,0 +1,5 @@
+---
+nfs_services:
+ RedHat:
+ - rpcbind
+ - nfs
diff --git a/ansible/roles/nfs/tasks/main.yml b/ansible/roles/nfs/tasks/main.yml
new file mode 100644
index 00000000..32194d13
--- /dev/null
+++ b/ansible/roles/nfs/tasks/main.yml
@@ -0,0 +1,33 @@
+---
+- name: Create nfs directory
+ file:
+ path: "{{ nfs_mount_path }}"
+ state: directory
+ mode: 0777
+
+- name: Setup nfs server
+ block:
+ - name: Start services
+ systemd:
+ name: "{{ item }}"
+ state: started
+ with_items: "{{ nfs_services[ansible_os_family] }}"
+
+ - name: Add hosts to exports
+ template:
+ src: exports.j2
+ dest: /etc/exports
+
+ - name: Export nfs
+ command: exportfs -ar
+ when:
+ - "'nfs-server' in group_names"
+
+- name: Mount dockerdata-nfs
+ mount:
+ path: "{{ nfs_mount_path }}"
+ src: "{{ hostvars[groups['nfs-server'].0].ansible_host }}:{{ nfs_mount_path }}"
+ fstype: nfs
+ state: mounted
+ when:
+ - "'nfs-server' not in group_names"
diff --git a/ansible/roles/nfs/templates/exports.j2 b/ansible/roles/nfs/templates/exports.j2
new file mode 100644
index 00000000..1f6956c2
--- /dev/null
+++ b/ansible/roles/nfs/templates/exports.j2
@@ -0,0 +1,3 @@
+{% for host in groups.kubernetes[1:] -%}
+ {{ nfs_mount_path }} {{ hostvars[host].ansible_host }}(rw,sync,no_root_squash,no_subtree_check)
+{% endfor %}
diff --git a/ansible/roles/rancher/tasks/main.yml b/ansible/roles/rancher/tasks/main.yml
new file mode 100644
index 00000000..1370a39f
--- /dev/null
+++ b/ansible/roles/rancher/tasks/main.yml
@@ -0,0 +1,2 @@
+---
+- include_tasks: "rancher_{{ rancher_role }}.yml"
diff --git a/ansible/roles/rancher/tasks/rancher_agent.yml b/ansible/roles/rancher/tasks/rancher_agent.yml
new file mode 100644
index 00000000..4c9cb8dd
--- /dev/null
+++ b/ansible/roles/rancher/tasks/rancher_agent.yml
@@ -0,0 +1,13 @@
+---
+- name: Add Rancher Agent
+ docker_container:
+ name: rancher_agent
+ image: "{{ server_hostvars.rancher_agent_image }}"
+ command: "{{ server_hostvars.rancher_agent_reg_url }}"
+ volumes:
+ - "/var/run/docker.sock:/var/run/docker.sock"
+ - "/var/lib/rancher:/var/lib/rancher"
+ auto_remove: yes
+ privileged: yes
+ vars:
+ server_hostvars: "{{ hostvars[groups.infrastructure.0] }}"
diff --git a/ansible/roles/rancher/tasks/rancher_server.yml b/ansible/roles/rancher/tasks/rancher_server.yml
new file mode 100644
index 00000000..9abf986b
--- /dev/null
+++ b/ansible/roles/rancher/tasks/rancher_server.yml
@@ -0,0 +1,51 @@
+---
+# DO NOT ADD SPACE AROUND ';'
+- name: Start rancher/server:v1.6.14
+ docker_container:
+ name: rancher_server
+ image: rancher/server:v1.6.14
+ command: ["sh", "-c", "/usr/sbin/update-ca-certificates;/usr/bin/entry /usr/bin/s6-svscan /service"]
+ ports: 8080:8080
+ state: started
+ restart_policy: unless-stopped
+ volumes:
+ - "{{ app_data_path }}/certs:/usr/local/share/ca-certificates/extra:ro"
+
+- name: Wait for rancher server to be ready
+ uri:
+ url: "{{ rancher_server_url }}/v2-beta"
+ register: response
+ retries: 10
+ delay: 30
+ until: not response.failed
+
+- name: Create kubernetes environment
+ rancher_k8s_environment:
+ name: "{{ app_name }}"
+ descr: "Kubernetes environment for {{ app_name }}"
+ server: "{{ rancher_server_url }}"
+ delete_other_k8s: "{{ rancher_remove_other_env }}"
+ force: "{{ rancher_redeploy_k8s_env }}"
+ host_os: "{{ ansible_os_family }}"
+ register: env
+ retries: 10
+ delay: 5
+ until: env.data is defined
+
+- name: Set apikey values
+ set_fact:
+ k8s_env_id: "{{ env.data.environment.id }}"
+ key_public: "{{ env.data.apikey.public }}"
+ key_private: "{{ env.data.apikey.private }}"
+ rancher_agent_image: "{{ env.data.registration_tokens.image }}"
+ rancher_agent_reg_url: "{{ env.data.registration_tokens.reg_url }}"
+
+- name: Ensure .kube directory exists
+ file:
+ path: "{{ kube_directory }}"
+ state: directory
+
+- name: Create kube config
+ template:
+ src: kube_config.j2
+ dest: "{{ kube_directory }}/config"
diff --git a/ansible/roles/rancher/templates/kube_config.j2 b/ansible/roles/rancher/templates/kube_config.j2
new file mode 100644
index 00000000..87f332e6
--- /dev/null
+++ b/ansible/roles/rancher/templates/kube_config.j2
@@ -0,0 +1,19 @@
+apiVersion: v1
+kind: Config
+clusters:
+- cluster:
+ api-version: v1
+ insecure-skip-tls-verify: true
+ server: "https://{{ ansible_host }}:8080/r/projects/{{ k8s_env_id }}/kubernetes:6443"
+ name: "{{ app_name }}"
+contexts:
+- context:
+ cluster: "{{ app_name }}"
+ user: "{{ app_name }}"
+ name: "{{ app_name }}"
+current-context: "{{ app_name }}"
+users:
+- name: "{{ app_name }}"
+ user:
+ token: "{{ (['Basic', [key_public, key_private] | join(':') | b64encode] | join(' ')) | b64encode }}"
+