b2c2bf8d |
#!/bin/bash
|
9897da95 |
# WARNING: The script modifies the host that docker is running on. It |
38798f2a |
# attempts to load the overlay and openvswitch modules. If this modification |
9897da95 |
# is undesirable consider running docker in a VM. |
b2c2bf8d |
#
# Overview
# ========
#
# This script manages the lifecycle of an openshift dev cluster
# deployed to docker-in-docker containers. Once 'start' has been used
# to successfully create a dind cluster, 'docker exec' can be used to
# access the created containers (named
# openshift-{master,node-1,node-2}) as if they were VMs.
#
# Dependencies
# ------------
# |
9897da95 |
# This script has been tested on Fedora 24, but should work on any |
b2c2bf8d |
# release. Docker is assumed to be installed. At this time,
# boot2docker is not supported.
#
# SELinux
# -------
#
# Docker-in-docker's use of volumes is not compatible with selinux set
# to enforcing mode. Set selinux to permissive or disable it
# entirely.
# |
b8a2ba83 |
# OpenShift Configuration
# -----------------------
#
# By default, a dind openshift cluster stores its configuration |
9897da95 |
# (openshift.local.*) in /tmp/openshift-dind-cluster/openshift. It's
# possible to run multiple dind clusters simultaneously by overriding
# the instance prefix. The following command would ensure
# configuration was stored at /tmp/openshift-dind/cluster/my-cluster: |
b8a2ba83 |
# |
9897da95 |
# OPENSHIFT_CLUSTER_ID=my-cluster hack/dind-cluster.sh [command] |
b8a2ba83 |
# |
21e7e1d2 |
# Suggested Workflow
# ------------------
#
# When making changes to the deployment of a dind cluster or making
# breaking golang changes, the 'restart' command will ensure that an
# existing cluster is cleaned up before deploying a new cluster.
# |
b8a2ba83 |
# Running Tests
# -------------
# |
630e7d0c |
# The extended tests can be run against a dind cluster as follows: |
b2c2bf8d |
# |
630e7d0c |
# OPENSHIFT_CONFIG_ROOT=dind test/extended/networking.sh |
b2c2bf8d |
set -o errexit
set -o nounset
set -o pipefail
|
9897da95 |
source "$(dirname "${BASH_SOURCE}")/lib/init.sh"
source "${OS_ROOT}/images/dind/node/openshift-dind-lib.sh" |
d24e93fc |
|
b2c2bf8d |
function start() { |
9897da95 |
local origin_root=$1
local config_root=$2
local deployed_config_root=$3
local cluster_id=$4
local network_plugin=$5
local wait_for_cluster=$6
local node_count=$7
|
b2c2bf8d |
# docker-in-docker's use of volumes is not compatible with SELinux
check-selinux
|
9897da95 |
echo "Starting dind cluster '${cluster_id}' with plugin '${network_plugin}'" |
b2c2bf8d |
|
9897da95 |
# Ensuring compatible host configuration
#
# Running in a container ensures that the docker host will be affected even
# if docker is running remotely. The openshift/dind-node image was chosen
# due to its having sysctl installed.
${DOCKER_CMD} run --privileged --net=host --rm -v /lib/modules:/lib/modules \
openshift/dind-node bash -e -c \ |
38798f2a |
'/usr/sbin/modprobe openvswitch;
/usr/sbin/modprobe overlay 2> /dev/null || true;' |
9897da95 |
# Initialize the cluster config path
mkdir -p "${config_root}"
echo "OPENSHIFT_NETWORK_PLUGIN=${network_plugin}" > "${config_root}/network-plugin"
copy-runtime "${origin_root}" "${config_root}/"
local volumes="-v ${config_root}:${deployed_config_root}"
local run_cmd="${DOCKER_CMD} run -dt ${volumes} --privileged"
# Create containers
${run_cmd} --name="${MASTER_NAME}" --hostname="${MASTER_NAME}" "${MASTER_IMAGE}" > /dev/null |
b2c2bf8d |
for name in "${NODE_NAMES[@]}"; do |
9897da95 |
${run_cmd} --name="${name}" --hostname="${name}" "${NODE_IMAGE}" > /dev/null |
b2c2bf8d |
done |
de201098 |
|
9897da95 |
local rc_file="dind-${cluster_id}.rc"
local admin_config
admin_config="$(get-admin-config "${CONFIG_ROOT}")"
local bin_path
bin_path="$(os::build::get-bin-output-path "${OS_ROOT}")" |
a96ea3b4 |
cat >"${rc_file}" <<EOF
export KUBECONFIG=${admin_config}
export PATH=\$PATH:${bin_path}
EOF |
0cb8c66e |
|
9897da95 |
if [[ -n "${wait_for_cluster}" ]]; then
wait-for-cluster "${config_root}" "${node_count}" |
0cb8c66e |
fi |
26766a0d |
|
6305a427 |
if [[ "${KUBECONFIG:-}" != "${admin_config}" ||
":${PATH}:" != *":${bin_path}:"* ]]; then |
26766a0d |
echo ""
echo "Before invoking the openshift cli, make sure to source the
cluster's rc file to configure the bash environment:
$ . ${rc_file}
$ oc get nodes
"
fi |
b2c2bf8d |
}
function stop() { |
9897da95 |
local config_root=$1
local cluster_id=$2
echo "Stopping dind cluster '${cluster_id}'" |
d331b4c5 |
|
9897da95 |
local master_cid
master_cid="$(${DOCKER_CMD} ps -qa --filter "name=${MASTER_NAME}")" |
b2c2bf8d |
if [[ "${master_cid}" ]]; then |
9897da95 |
${DOCKER_CMD} rm -f "${master_cid}" > /dev/null |
b2c2bf8d |
fi
|
9897da95 |
local node_cids
node_cids="$(${DOCKER_CMD} ps -qa --filter "name=${NODE_PREFIX}")" |
b2c2bf8d |
if [[ "${node_cids}" ]]; then
node_cids=(${node_cids//\n/ })
for cid in "${node_cids[@]}"; do |
9897da95 |
${DOCKER_CMD} rm -f "${cid}" > /dev/null |
b2c2bf8d |
done
fi
|
9897da95 |
# Cleaning up configuration to avoid conflict with a future cluster |
cb3f10e0 |
# The container will have created configuration as root |
9897da95 |
sudo rm -rf "${config_root}"/openshift.local.etcd
sudo rm -rf "${config_root}"/openshift.local.config |
cb3f10e0 |
|
b2c2bf8d |
# Cleanup orphaned volumes
#
# See: https://github.com/jpetazzo/dind#important-warning-about-disk-usage
# |
9897da95 |
for volume in $( ${DOCKER_CMD} volume ls -qf dangling=true ); do
${DOCKER_CMD} volume rm "${volume}" > /dev/null
done
}
function check-selinux() {
if [[ "$(getenforce)" = "Enforcing" ]]; then
>&2 echo "Error: This script is not compatible with SELinux enforcing mode."
exit 1 |
adce7c75 |
fi |
b2c2bf8d |
}
|
9897da95 |
function get-network-plugin() {
local plugin=$1 |
21e7e1d2 |
|
9897da95 |
local subnet_plugin="redhat/openshift-ovs-subnet"
local multitenant_plugin="redhat/openshift-ovs-multitenant" |
9810e7b8 |
local default_plugin="${multitenant_plugin}" |
21e7e1d2 |
|
9897da95 |
if [[ "${plugin}" != "${subnet_plugin}" &&
"${plugin}" != "${multitenant_plugin}" &&
"${plugin}" != "cni" ]]; then
if [[ -n "${plugin}" ]]; then
>&2 echo "Invalid network plugin: ${plugin}"
fi
plugin="${default_plugin}" |
21e7e1d2 |
fi |
9897da95 |
echo "${plugin}"
} |
21e7e1d2 |
|
9897da95 |
function get-docker-ip() {
local cid=$1
${DOCKER_CMD} inspect --format '{{ .NetworkSettings.IPAddress }}' "${cid}"
}
function get-admin-config() {
local config_root=$1
echo "${config_root}/openshift.local.config/master/admin.kubeconfig"
}
function copy-runtime() {
local origin_root=$1
local target=$2
|
1b13c58e |
cp "$(os::util::find::built_binary openshift)" "${target}"
cp "$(os::util::find::built_binary host-local)" "${target}"
cp "$(os::util::find::built_binary loopback)" "${target}"
cp "$(os::util::find::built_binary sdn-cni-plugin)" "${target}/openshift-sdn" |
cf69a41b |
local osdn_plugin_path="${origin_root}/pkg/sdn/plugin"
cp "${osdn_plugin_path}/bin/openshift-sdn-ovs" "${target}"
cp "${osdn_plugin_path}/sdn-cni-plugin/80-openshift-sdn.conf" "${target}" |
9897da95 |
}
function wait-for-cluster() {
local config_root=$1
local expected_node_count=$2
# Increment the node count to ensure that the sdn node also reports readiness
(( expected_node_count++ ))
local kubeconfig
kubeconfig="$(get-admin-config "${config_root}")"
local oc |
1b13c58e |
oc="$(os::util::find::built_binary oc)" |
9897da95 |
|
6c6ec1ad |
# wait for healthz to report ok before trying to get nodes
os::util::wait-for-condition "ok" "${oc} get --config=${kubeconfig} --raw=/healthz" "120"
|
9897da95 |
local msg="${expected_node_count} nodes to report readiness"
local condition="nodes-are-ready ${kubeconfig} ${oc} ${expected_node_count}" |
ea684426 |
local timeout=120 |
1218ef36 |
os::util::wait-for-condition "${msg}" "${condition}" "${timeout}" |
21e7e1d2 |
}
|
2aa9ff85 |
function nodes-are-ready() { |
9897da95 |
local kubeconfig=$1
local oc=$2
local expected_node_count=$3
# TODO - do not count any node whose name matches the master node e.g. 'node-master' |
6dad4aef |
read -d '' template <<'EOF'
{{range $item := .items}} |
9897da95 |
{{range .status.conditions}}
{{if eq .type "Ready"}}
{{if eq .status "True"}}
{{printf "%s\\n" $item.metadata.name}} |
6dad4aef |
{{end}}
{{end}}
{{end}}
{{end}}
EOF
# Remove formatting before use
template="$(echo "${template}" | tr -d '\n' | sed -e 's/} \+/}/g')" |
9897da95 |
local count
count="$("${oc}" --config="${kubeconfig}" get nodes \
--template "${template}" 2> /dev/null | \
wc -l)"
test "${count}" -ge "${expected_node_count}" |
2aa9ff85 |
}
|
9897da95 |
function build-images() {
local origin_root=$1
echo "Building container images"
build-image "${origin_root}/images/dind/" "${BASE_IMAGE}"
build-image "${origin_root}/images/dind/node" "${NODE_IMAGE}"
build-image "${origin_root}/images/dind/master" "${MASTER_IMAGE}"
}
function build-image() {
local build_root=$1
local image_name=$2
pushd "${build_root}" > /dev/null
${DOCKER_CMD} build -t "${image_name}" .
popd > /dev/null |
2aa9ff85 |
}
|
9897da95 |
DOCKER_CMD=${DOCKER_CMD:-"sudo docker"}
CLUSTER_ID="${OPENSHIFT_CLUSTER_ID:-openshift}"
TMPDIR="${TMPDIR:-"/tmp"}"
CONFIG_ROOT="${OPENSHIFT_CONFIG_ROOT:-${TMPDIR}/openshift-dind-cluster/${CLUSTER_ID}}"
DEPLOYED_CONFIG_ROOT="/data"
MASTER_NAME="${CLUSTER_ID}-master"
NODE_PREFIX="${CLUSTER_ID}-node-"
NODE_COUNT=2
NODE_NAMES=()
for (( i=1; i<=NODE_COUNT; i++ )); do
NODE_NAMES+=( "${NODE_PREFIX}${i}" )
done
BASE_IMAGE="openshift/dind"
NODE_IMAGE="openshift/dind-node"
MASTER_IMAGE="openshift/dind-master"
|
b2c2bf8d |
case "${1:-""}" in
start) |
9897da95 |
BUILD=
BUILD_IMAGES=
WAIT_FOR_CLUSTER=1
NETWORK_PLUGIN=
REMOVE_EXISTING_CLUSTER=
OPTIND=2
while getopts ":bin:rs" opt; do
case $opt in
b)
BUILD=1
;;
i)
BUILD_IMAGES=1
;;
n)
NETWORK_PLUGIN="${OPTARG}"
;;
r)
REMOVE_EXISTING_CLUSTER=1
;;
s)
WAIT_FOR_CLUSTER=
;;
\?)
echo "Invalid option: -${OPTARG}" >&2
exit 1
;;
:)
echo "Option -${OPTARG} requires an argument." >&2
exit 1
;;
esac
done
if [[ -n "${REMOVE_EXISTING_CLUSTER}" ]]; then
stop "${CONFIG_ROOT}" "${CLUSTER_ID}"
fi
# Build origin if requested or required |
1b13c58e |
if [[ -n "${BUILD}" ]] || ! os::util::find::built_binary 'oc' >/dev/null 2>&1; then |
9897da95 |
"${OS_ROOT}/hack/build-go.sh"
fi
# Build images if requested or required
if [[ -n "${BUILD_IMAGES}" ||
-z "$(${DOCKER_CMD} images -q ${MASTER_IMAGE})" ]]; then
build-images "${OS_ROOT}"
fi
NETWORK_PLUGIN="$(get-network-plugin "${NETWORK_PLUGIN}")"
start "${OS_ROOT}" "${CONFIG_ROOT}" "${DEPLOYED_CONFIG_ROOT}" \
"${CLUSTER_ID}" "${NETWORK_PLUGIN}" "${WAIT_FOR_CLUSTER}" \
"${NODE_COUNT}" "${NODE_PREFIX}" |
b2c2bf8d |
;;
stop) |
9897da95 |
stop "${CONFIG_ROOT}" "${CLUSTER_ID}" |
21e7e1d2 |
;; |
2aa9ff85 |
wait-for-cluster) |
9897da95 |
wait-for-cluster "${CONFIG_ROOT}" "${NODE_COUNT}" |
2aa9ff85 |
;; |
388e0a19 |
build-images) |
9897da95 |
build-images "${OS_ROOT}" |
1f867ed2 |
;; |
b2c2bf8d |
*) |
9897da95 |
>&2 echo "Usage: $0 {start|stop|wait-for-cluster|build-images}
start accepts the following arguments:
-n [net plugin] the name of the network plugin to deploy
-b build origin before starting the cluster
-i build container images before starting the cluster
-r remove an existing cluster
-s skip waiting for nodes to become ready
" |
b2c2bf8d |
exit 2
esac |