#!/bin/bash set -e . $(dirname $0)/jenkins-helpers.sh # Start a local docker instance with the requested arch and distro # This script is meant to be executed from Jenkins jobs inside TCWG # lab. It prints shell commands meant to be executed in the parent # shell, consisting in: # - definition of ${CONTAINER}, used to prefix commands that you want # to run inside the container. # - definition of ${CONTAINER_CLEANUP}, a cleanup statement remove the # container on exit for instance # - definition of ${session_host} and ${session_port}, can be used for # a remote connexion to the container usage() { echo "Usage: $0 [--arch container-arch] --distro flavour [--label label] [--node node] [--prefix prefix] [--session-host host] [--session-name name] [--task {build|test}] [--weight weight]" echo echo " container-arch: architecture (eg: amd64, i386, arm64, armhf)" echo " distro: distribution (eg: trusty)" echo " label: jenkins label; container is started on least-busy node; also sets container architecture" echo " node: jenkins node; container is started on host mapped to the node" echo " prefix: prefix to prepend to output variables and functions" echo " session-host: hostname where the container will run, defaults to localhost" echo " useful if the name resolution does not work correctly" echo " session-name: session, in case the default '$BUILD_NUMBER-$JOB_NAME' is not suitable" echo " task: type of container (build or test, default=build)" echo " weight: container weight, reserves resources. Default=1" exit 1 } # Save stdout/stderr file descriptors exec 3>&1 4>&2 # Make sure all output goes to stderr exec 1>&2 container_arch= distro= label= node= prefix= session_host= session_name= task="build" weight=1 while [ $# -ge 1 ] do case $1 in --arch) container_arch=$2 [ x${container_arch} = x ] && usage shift 2 ;; --distro) distro=$2 [ x${distro} = x ] && usage shift 2 ;; --label) label=$2 [ x${label} = x ] && usage shift 2 ;; --node) node=$2 [ x${node} = x ] && usage shift 2 ;; --prefix) prefix=$2 [ x${prefix} = x ] && usage shift 2 ;; --session-host) session_host=$2 [ x${session_host} = x ] && usage shift 2 ;; --session-name) session_name=$2 [ x${session_name} = x ] && usage shift 2 ;; --task) task=$2 [ x${task} = x ] && usage shift 2 ;; --weight) weight=$2 [ x${weight} = x ] && usage shift 2 ;; *) echo "Unsupported option: $1" usage ;; esac done if [ x"$label" != x"" ]; then if [ x"$node" = x"" ]; then node=$(print_node_with_least_containers $label) if [ x"$node" = x"" ]; then echo "ERROR: Cannot find node for $label" exit 1 fi fi if [ x"$container_arch" != x"" ]; then echo "--arch conflicts with --label" usage fi container_arch=$(print_arch_for_label $label) fi if [ x"$node" != x"" ]; then if [ x"$session_host" != x"" ]; then echo "--session_host conflicts with --node" usage fi session_host=$(print_host_for_node $node) fi [ x${container_arch} = x ] && usage [ x${distro} = x ] && usage [ x"$session_host" = x ] && session_host=localhost # Append .tcwglab if not already present case ${session_host} in *.tcwglab) ;; *) session_host=${session_host}.tcwglab esac if [ x"$session_name" = x ]; then # Set the default session_name, using BUILD_NUMBER and JOB_NAME, # as set by Jenkins. if [ "x$BUILD_NUMBER" = "x" ]; then echo "BUILD_NUMBER is not defined. Are you executing this from Jenkins ?" exit 1 fi if [ "x$JOB_NAME" = "x" ]; then echo "JOB_NAME is not defined. Are you executing this from Jenkins ?" exit 1 fi session_name=$(echo $BUILD_NUMBER-$JOB_NAME | sed -e "s#[/=,+]#-#g") fi # We want to bind-mount WORKSPACE, so make sure it is defined if [ "x$WORKSPACE" = "x" ]; then echo "WORKSPACE is not defined. Are you executing this from Jenkins ?" exit 1 fi image=linaro/ci-${container_arch}-tcwg-${task}-ubuntu:${distro} DOCKER="docker -H $session_host:2375" $DOCKER pull $image SECURITY="--cap-add=SYS_PTRACE" # We need this because of a bug in libgo's configure script: # it would crash when testing "whether setcontext clobbers TLS # variables", and report neither "no" nor "yes", later making # configure fail. # Also, because the sanitizers need to disable ASLR during the tests # and docker needs to explicitly enable the process to do that on all # architectures. SECURITY="${SECURITY} --security-opt seccomp:unconfined" # Reserve resources according to weight and task case $task in build) memory=$(( $weight * 7500 )) ;; # 7.5GB per executor test) memory=$(( $weight * 750 )) ;; # 0.75GB per executor esac pids=$(( $weight * 5000 )) # 5000 processes per executor cpus=$(( $weight * 1000 )) # 1000 cpu shares per executor case $task in build) bind_mounts=( $HOME/snapshots-ref:$HOME/snapshots-ref:ro $HOME/llvm-reference:$HOME/llvm-reference:ro $WORKSPACE:$WORKSPACE ) ;; *) bind_mounts=() ;; esac bind_mounts_opt=() for bind_mount in "${bind_mounts[@]}"; do # Make sure all bind-mount directories exist. # If a host bind-mount dir doesn't exist, then docker creates it on # the host with root:root owner, which can't be removed by cleanup job. dir="${bind_mount%%:*}" ssh $session_host mkdir -p "$dir" bind_mounts_opt=("${bind_mounts_opt[@]}" "-v" "$bind_mount") done set -x session_id=$($DOCKER run --name $session_name -dtP \ "${bind_mounts_opt[@]}" \ ${SECURITY} \ --memory=${memory}M \ --pids-limit=${pids} \ --cpu-shares=${cpus} \ $image) set +x if [ x"$session_id" = x ]; then exit 1 fi # Remove the docker instance we have just created in case something # goes wrong. CONTAINER_CLEANUP="$DOCKER rm -fv ${session_id}" trap "exec 1>&3 2>&4 ; ${CONTAINER_CLEANUP}" EXIT session_port=$($DOCKER port $session_id 22 | cut -d: -f 2) # Wait until the ssh server is ready to serve connexions # Make sure connexion messages go to stderr, so that in case of # success stdout contains only the connexion info expected by the # caller. count=20 while [ $count -gt 0 ] do ssh -p $session_port $session_host true && break echo SSH server not ready, waiting..... sleep 5 count=$((count - 1)) done if [ $count -eq 0 ]; then echo SSH server did not respond, exiting exit 1 fi # Do not remove the container upon exit: it is now ready trap EXIT # Restore stdout/stderr exec 1>&3 2>&4 cat <