blob: 3c134cc0dd4d468f42f047fb29dc70c2e12aac6f [file] [log] [blame]
#!/bin/bash
################################################################################
#
# Script used to create a Jenkins master that can run amd64 or ppc64le. It can
# be used to launch the Jenkins master as a Docker container locally or as a
# Kubernetes Deployment in a Kubernetes cluster.
#
################################################################################
#
# Script Variables:
# build_scripts_dir The path of the openbmc-build-scripts directory.
# Default: The directory containing this script
# workspace The directory that holds files used to build the Jenkins
# master master image and volumes used to deploy it.
# Default: "~/jenkins-build-${RANDOM}"
#
# Jenkins Dockerfile Variables:
# agent_port The port used as the Jenkins slave agent port.
# Default: "50000"
# http_port The port used as Jenkins UI port.
# Default: "8080"
# img_name The name given to the Docker image when it is built.
# Default: "openbmc/jenkins-master-${ARCH}:${JENKINS_VRSN}"
# img_tag The tag of the OpenJDK image used as the base image.
# Default: "/8-jdk"
# j_gid Jenkins group ID the container will use to run Jenkins.
# Default: "1000"
# j_group Group name tag the container will use to run Jenkins.
# Default: "jenkins"
# j_home Directory used as the Jenkins Home in the container.
# Default: "/var/jenkins_home"
# j_uid Jenkins user ID the container will use to run Jenkins.
# Default: "1000"
# j_user Username tag the container will use to run Jenkins.
# Default: "jenkins"
# j_vrsn The version of the Jenkins war file you wish to use.
# Default: "2.60.3"
# tini_vrsn The version of Tini to use in the Dockerfile, 0.16.1 is
# the first release with ppc64le release support.
# Default: "0.16.1"
#
# Deployment Variables:
# cont_import_mnt The directory on the container used to import extra files.
# Default: "/mnt/jenkins_import", ignored if above not set
# home_mnt The directory on the host used as the Jenkins home.
# Default: "${WORKSPACE}/jenkins_home"
# host_import_mnt The directory on the host used to import extra files.
# Default: "", import mount is ignored if not set
# java_options What will be passed as the environment variable for the
# JAVA_OPTS environment variable.
# Default: "-Xmx4096m"
# jenkins_options What will be passed as the environment variable for the
# JENKINS_OPTS environment variable.
# Default: "--prefix=/jenkins"
# launch docker|k8s
# Method in which the container will be launched. Either as
# a Docker container launched via Docker, or by using a
# helper script to launch into a Kubernetes cluster.
# Default: "docker"
#
################################################################################
set -xeo pipefail
ARCH=$(uname -m)
# Script Variables
build_scripts_dir=${build_scripts_dir:-"$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"}
workspace=${workspace:-${HOME}/jenkins-build-${RANDOM}}
# Jenkins Dockerfile Variables
agent_port=${agent_port:-50000}
http_port=${http_port:-8080}
img_name=${img_name:-openbmc/jenkins-master-${ARCH}:${j_vrsn}}
j_gid=${j_gid:-1000}
j_group=${j_group:-jenkins}
j_home=${j_home:-/var/jenkins_home}
j_uid=${j_uid:-1000}
j_user=${j_user:-jenkins}
j_vrsn=${j_vrsn:-2.60.3}
img_tag=${img_tag:-8-jdk}
tini_vrsn=${tini_vrsn:-0.16.1}
# Deployment Variables
cont_import_mnt=${cont_import_mnt:-/mnt/jenkins_import}
home_mnt=${home_mnt:-${workspace}/jenkins_home}
host_import_mnt=${host_import_mnt:-}
java_options=${java_options:-"-Xmx4096m"}
jenkins_options=${jenkins_options:-"--prefix=/jenkins"}
launch=${launch:-docker}
# Save the Jenkins.war URL to a variable and SHA if we care about verification
j_url=https://repo.jenkins-ci.org/public/org/jenkins-ci/main/jenkins-war/${j_vrsn}/jenkins-war-${j_vrsn}.war
# Make or Clean WORKSPACE
if [[ -d "${workspace}" ]]; then
rm -rf "${workspace}/Dockerfile" \
"${workspace}/docker-jenkins" \
"${workspace}/plugins.*" \
"${workspace}/install-plugins.sh" \
"${workspace}/jenkins.sh" \
"${workspace}/jenkins-support" \
"${workspace}/init.groovy"
else
mkdir -p "${workspace}"
fi
# Determine the prefix of the Dockerfile's base image
case ${ARCH} in
"ppc64le")
docker_base="ppc64le/"
tini_arch="ppc64el"
;;
"x86_64")
docker_base=""
tini_arch="amd64"
;;
*)
echo "Unsupported system architecture(${ARCH}) found for docker image"
exit 1
esac
# Move Into the WORKSPACE
cd "${workspace}"
# Make the Dockerfile
################################################################################
cat >> Dockerfile << EOF
FROM ${docker_base}openjdk:${img_tag}
RUN apt-get update && apt-get install -y git curl
ENV JENKINS_HOME ${j_home}
ENV JENKINS_SLAVE_AGENT_PORT ${agent_port}
# Jenkins will default to run with user jenkins, uid = 1000
# If you bind mount a volume from the host or a data container,
# ensure you use the same uid
RUN groupadd -g ${j_gid} ${j_group} && \
useradd -d ${j_home} -u ${j_uid} -g ${j_gid} -m -s /bin/bash ${j_user}
# Jenkins home directory is a volume, so configuration and build history
# can be persisted and survive image upgrades
VOLUME ${j_home}
# /usr/share/jenkins/ref/ contains all reference configuration we want
# to set on a fresh new installation. Use it to bundle additional plugins
# or config file with your custom jenkins Docker image.
RUN mkdir -p /usr/share/jenkins/ref/init.groovy.d
# Use tini as subreaper in Docker container to adopt zombie processes
RUN curl -fsSL https://github.com/krallin/tini/releases/download/v${tini_vrsn}/tini-static-${tini_arch} \
-o /bin/tini && \
chmod +x /bin/tini
COPY init.groovy /usr/share/jenkins/ref/init.groovy.d/tcp-slave-agent-port.groovy
# could use ADD but this one does not check Last-Modified header neither does it allow to control checksum
# see https://github.com/docker/docker/issues/8331
RUN curl -fsSL ${j_url} -o /usr/share/jenkins/jenkins.war
ENV JENKINS_UC https://updates.jenkins.io
ENV JENKINS_UC_EXPERIMENTAL=https://updates.jenkins.io/experimental
RUN chown -R ${j_user} ${j_home} /usr/share/jenkins/ref
# for main web interface:
EXPOSE ${http_port}
# will be used by attached slave agents:
EXPOSE ${agent_port}
ENV COPY_REFERENCE_FILE_LOG ${j_home}/copy_reference_file.log
USER ${j_user}
COPY jenkins-support /usr/local/bin/jenkins-support
COPY jenkins.sh /usr/local/bin/jenkins.sh
ENTRYPOINT ["/bin/tini", "--", "/usr/local/bin/jenkins.sh"]
# from a derived Dockerfile, can use RUN plugins.sh active.txt to setup /usr/share/jenkins/ref/plugins from a support bundle
COPY plugins.sh /usr/local/bin/plugins.sh
COPY install-plugins.sh /usr/local/bin/install-plugins.sh
# Install plugins.txt plugins
COPY plugins.txt /usr/share/jenkins/ref/plugins.txt
RUN /usr/local/bin/install-plugins.sh < /usr/share/jenkins/ref/plugins.txt
EOF
################################################################################
# Clone in the jenkinsci docker jenkins repo and copy some files into WORKSPACE
git clone https://github.com/jenkinsci/docker.git docker-jenkins
cp docker-jenkins/init.groovy .
cp docker-jenkins/jenkins-support .
cp docker-jenkins/jenkins.sh .
cp docker-jenkins/plugins.sh .
cp docker-jenkins/install-plugins.sh .
# Generate Plugins.txt, the plugins you want installed automatically go here
################################################################################
cat >> plugins.txt << EOF
kubernetes
EOF
################################################################################
# Build the image
docker build -t "${img_name}" .
if [[ "${launch}" == "docker" ]]; then
# Ensure directories that will be mounted exist
if [[ -n "${host_import_mnt}" && ! -d "${host_import_mnt}" ]]; then
mkdir -p "${host_import_mnt}"
fi
if [[ ! -d "${home_mnt}" ]]; then
mkdir -p "${home_mnt}"
fi
# Ensure directories that will be mounted are owned by the jenkins user
if [[ "$(id -u)" != 0 ]]; then
echo "Not running as root:"
echo "Checking if j_gid and j_uid are the owners of mounted directories"
# shellcheck disable=SC2012 # use ls to get permissions.
test_1="$(ls -nd "${home_mnt}" | awk '{print $3 " " $4}')"
if [[ "${test_1}" != "${j_uid} ${j_gid}" ]]; then
echo "Owner of ${home_mnt} is not the jenkins user"
echo "${test_1} != ${j_uid} ${j_gid}"
will_fail=1
fi
if [[ -n "${host_import_mnt}" ]]; then
# shellcheck disable=SC2012 # use ls to get permissions.
test_2="$(ls -nd "${host_import_mnt}" | awk '{print $3 " " $4}' )"
if [[ "${test_2}" != "${j_uid} ${j_gid}" ]]; then
echo "Owner of ${host_import_mnt} is not the jenkins user"
echo "${test_2} != ${j_uid} ${j_gid}"
will_fail=1
fi
fi
if [[ "${will_fail}" == 1 ]]; then
echo "Failing before attempting to launch container"
echo "Try again as root or use correct uid/gid pairs"
exit 1
fi
else
if [[ -n "${host_import_mnt}" ]]; then
chown -R "${j_uid}:${j_gid}" "${host_import_mnt}"
fi
chown -R "${j_uid}:${j_gid}" "${home_mnt}"
fi
#If we don't have import mount don't add to docker command
if [[ -n "${host_import_mnt}" ]]; then
import_vol_cmd="-v ${host_import_mnt}:${cont_import_mnt}"
fi
# Launch the jenkins image with Docker
# shellcheck disable=SC2086 # import_vol_cmd is intentially word-split.
docker run -d \
${import_vol_cmd} \
-v "${home_mnt}:${j_home}" \
-p "${http_port}:8080" \
-p "${agent_port}:${agent_port}" \
--env JAVA_OPTS=\""${java_options}"\" \
--env JENKINS_OPTS=\""${jenkins_options}"\" \
"${img_name}"
elif [[ ${launch} == "k8s" ]]; then
# launch using the k8s template
# shellcheck source=kubernetes/kubernetes-launch.sh
source "${build_scripts_dir}/kubernetes/kubernetes-launch.sh" Build-Jenkins false false
fi