Commit 8e3dcdf3 authored by caiyanming's avatar caiyanming

add docker command to jenkins image

Change-Id: I18142dee82caa6361d9b56fc87d64029e02572ec
parent f7698f2e
# Issues and Contributing
Please note that only issues related to this Docker image will be addressed here.
* If you have Docker related issues, please ask in the [Docker user mailing list](https://groups.google.com/forum/#!forum/docker-user).
* If you have Jenkins related issues, please ask in the [Jenkins mailing lists](https://jenkins-ci.org/content/mailing-lists).
* If you are not sure, then this is probably not the place to create an issue and you should use any of the previously mentioned mailing lists.
If after going through the previous checklist you still think you should create an issue here please provide:
* Docker commands that you execute
* Actual result
* Expected outcome
* Have you tried a non-dockerized Jenkins and get the expected outcome?
* Output of `docker version`
* Other relevant information
#
# Copyright (C) 2017 C-SKY Microsystems Co., Ltd.
# Copyright (C) 2018 XueTong Tech., Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
......@@ -14,14 +14,90 @@
# limitations under the License.
#
FROM jenkins
FROM registry.cn-hangzhou.aliyuncs.com/schbrain/jdk
MAINTAINER xt
USER root
COPY sources.list /etc/apt/sources.list
RUN apt-get update && apt-get install sudo -y --allow-unauthenticated \
&& rm -rf /var/lib/apt/lists/* \
&& echo "jenkins ALL=NOPASSWD: ALL" >> /etc/sudoers
USER jenkins
RUN apt-get update \
&& apt-get install -y git curl \
&& apt-get -y install apt-transport-https ca-certificates software-properties-common \
&& curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | apt-key add - \
&& add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable" \
&& apt-get -y update \
&& apt-get -y install docker-ce \
&& rm -rf /var/lib/apt/lists/*
ARG user=jenkins
ARG group=jenkins
ARG uid=1000
ARG gid=1000
ARG http_port=8080
ARG agent_port=50000
ENV JENKINS_HOME /var/jenkins_home
ENV JENKINS_SLAVE_AGENT_PORT ${agent_port}
# Jenkins is 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 ${gid} ${group} \
&& useradd -d "$JENKINS_HOME" -u ${uid} -g ${gid} -m -s /bin/bash ${user} \
&& usermod -aG docker ${user}
# Jenkins home directory is a volume, so configuration and build history
# can be persisted and survive image upgrades
VOLUME /var/jenkins_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
ENV TINI_VERSION 0.14.0
ENV TINI_SHA 6c41ec7d33e857d4779f14d9c74924cab0c7973485d2972419a3b7c7620ff5fd
# Use tini as subreaper in Docker container to adopt zombie processes
RUN curl -fsSL https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini-static-amd64 -o /bin/tini && chmod +x /bin/tini \
&& echo "$TINI_SHA /bin/tini" | sha256sum -c -
COPY init.groovy /usr/share/jenkins/ref/init.groovy.d/tcp-slave-agent-port.groovy
# jenkins version being bundled in this docker image
ARG JENKINS_VERSION
ENV JENKINS_VERSION ${JENKINS_VERSION:-2.60.3}
# jenkins.war checksum, download will be validated using it
ARG JENKINS_SHA=2d71b8f87c8417f9303a73d52901a59678ee6c0eefcf7325efed6035ff39372a
# Can be used to customize where jenkins.war get downloaded from
ARG JENKINS_URL=https://repo.jenkins-ci.org/public/org/jenkins-ci/main/jenkins-war/${JENKINS_VERSION}/jenkins-war-${JENKINS_VERSION}.war
# 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 ${JENKINS_URL} -o /usr/share/jenkins/jenkins.war \
&& echo "${JENKINS_SHA} /usr/share/jenkins/jenkins.war" | sha256sum -c -
ENV JENKINS_UC https://updates.jenkins.io
ENV JENKINS_UC_EXPERIMENTAL=https://updates.jenkins.io/experimental
RUN chown -R ${user} "$JENKINS_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 $JENKINS_HOME/copy_reference_file.log
USER ${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
COPY daemon.json /etc/docker/daemon.json
# DO NOT UPGRADE alpine until https://github.com/jenkinsci/docker/issues/508 is fixed
FROM openjdk:8u121-jdk-alpine
RUN apk add --no-cache git openssh-client curl unzip bash ttf-dejavu coreutils
ARG user=jenkins
ARG group=jenkins
ARG uid=1000
ARG gid=1000
ARG http_port=8080
ARG agent_port=50000
ENV JENKINS_HOME /var/jenkins_home
ENV JENKINS_SLAVE_AGENT_PORT ${agent_port}
# Jenkins is 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 addgroup -g ${gid} ${group} \
&& adduser -h "$JENKINS_HOME" -u ${uid} -G ${group} -s /bin/bash -D ${user}
# Jenkins home directory is a volume, so configuration and build history
# can be persisted and survive image upgrades
VOLUME /var/jenkins_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
ENV TINI_VERSION 0.14.0
ENV TINI_SHA 6c41ec7d33e857d4779f14d9c74924cab0c7973485d2972419a3b7c7620ff5fd
# Use tini as subreaper in Docker container to adopt zombie processes
RUN curl -fsSL https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini-static-amd64 -o /bin/tini && chmod +x /bin/tini \
&& echo "$TINI_SHA /bin/tini" | sha256sum -c -
COPY init.groovy /usr/share/jenkins/ref/init.groovy.d/tcp-slave-agent-port.groovy
# jenkins version being bundled in this docker image
ARG JENKINS_VERSION
ENV JENKINS_VERSION ${JENKINS_VERSION:-2.60.3}
# jenkins.war checksum, download will be validated using it
ARG JENKINS_SHA=2d71b8f87c8417f9303a73d52901a59678ee6c0eefcf7325efed6035ff39372a
# Can be used to customize where jenkins.war get downloaded from
ARG JENKINS_URL=https://repo.jenkins-ci.org/public/org/jenkins-ci/main/jenkins-war/${JENKINS_VERSION}/jenkins-war-${JENKINS_VERSION}.war
# 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 ${JENKINS_URL} -o /usr/share/jenkins/jenkins.war \
&& echo "${JENKINS_SHA} /usr/share/jenkins/jenkins.war" | sha256sum -c -
ENV JENKINS_UC https://updates.jenkins.io
RUN chown -R ${user} "$JENKINS_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 $JENKINS_HOME/copy_reference_file.log
USER ${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
#!/usr/bin/env groovy
properties([
buildDiscarder(logRotator(numToKeepStr: '5', artifactNumToKeepStr: '5')),
pipelineTriggers([cron('@daily')]),
])
node('docker') {
deleteDir()
stage('Checkout') {
checkout scm
}
if (!infra.isTrusted()) {
stage('shellcheck') {
// newer versions of the image don't have cat installed and docker pipeline fails
docker.image('koalaman/shellcheck:v0.4.6').inside() {
// run shellcheck ignoring error SC1091
// Not following: /usr/local/bin/jenkins-support was not specified as input
sh "shellcheck -e SC1091 *.sh"
}
}
/* Outside of the trusted.ci environment, we're building and testing
* the Dockerfile in this repository, but not publishing to docker hub
*/
stage('Build') {
docker.build('jenkins')
docker.build('jenkins:alpine', '--file Dockerfile-alpine .')
}
stage('Test') {
sh """
git submodule update --init --recursive
git clone https://github.com/sstephenson/bats.git
bats/bin/bats tests
"""
}
} else {
/* In our trusted.ci environment we only want to be publishing our
* containers from artifacts
*/
stage('Publish') {
infra.withDockerCredentials {
sh './publish.sh'
sh './publish.sh --variant alpine'
}
}
}
}
The MIT License
Copyright (c) 2014-, Michael Neale, Nicolas de Loof, Carlos Sanchez, and a number of other of contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
# Official Jenkins Docker image
The Jenkins Continuous Integration and Delivery server.
This is a fully functional Jenkins server.
[http://jenkins.io/](http://jenkins.io/).
<img src="http://jenkins-ci.org/sites/default/files/jenkins_logo.png"/>
# Usage
```
docker run -p 8080:8080 -p 50000:50000 jenkins/jenkins:lts
```
NOTE: read below the _build executors_ part for the role of the `50000` port mapping.
This will store the workspace in /var/jenkins_home. All Jenkins data lives in there - including plugins and configuration.
You will probably want to make that an explicit volume so you can manage it and attach to another container for upgrades :
```
docker run -p 8080:8080 -p 50000:50000 -v jenkins_home:/var/jenkins_home jenkins/jenkins:lts
```
this will automatically create a 'jenkins_home' volume on docker host, that will survive container stop/restart/deletion.
Avoid using a bind mount from a folder on host into `/var/jenkins_home`, as this might result in file permission issue. If you _really_ need to bind mount jenkins_home, ensure that directory on host is accessible by the jenkins user in container (jenkins user - uid 1000) or use `-u some_other_user` parameter with `docker run`.
## Backing up data
If you bind mount in a volume - you can simply back up that directory
(which is jenkins_home) at any time.
This is highly recommended. Treat the jenkins_home directory as you would a database - in Docker you would generally put a database on a volume.
If your volume is inside a container - you can use ```docker cp $ID:/var/jenkins_home``` command to extract the data, or other options to find where the volume data is.
Note that some symlinks on some OSes may be converted to copies (this can confuse jenkins with lastStableBuild links etc)
For more info check Docker docs section on [Managing data in containers](https://docs.docker.com/engine/tutorials/dockervolumes/)
# Setting the number of executors
You can specify and set the number of executors of your Jenkins master instance using a groovy script. By default its set to 2 executors, but you can extend the image and change it to your desired number of executors :
`executors.groovy`
```
import jenkins.model.*
Jenkins.instance.setNumExecutors(5)
```
and `Dockerfile`
```
FROM jenkins/jenkins:lts
COPY executors.groovy /usr/share/jenkins/ref/init.groovy.d/executors.groovy
```
# Attaching build executors
You can run builds on the master out of the box.
But if you want to attach build slave servers **through JNLP (Java Web Start)**: make sure you map the port: ```-p 50000:50000``` - which will be used when you connect a slave agent.
If you are only using [SSH slaves](https://wiki.jenkins-ci.org/display/JENKINS/SSH+Slaves+plugin), then you do **NOT** need to put that port mapping.
# Passing JVM parameters
You might need to customize the JVM running Jenkins, typically to pass system properties or tweak heap memory settings. Use JAVA_OPTS environment
variable for this purpose :
```
docker run --name myjenkins -p 8080:8080 -p 50000:50000 --env JAVA_OPTS=-Dhudson.footerURL=http://mycompany.com jenkins/jenkins:lts
```
# Configuring logging
Jenkins logging can be configured through a properties file and `java.util.logging.config.file` Java property.
For example:
```
mkdir data
cat > data/log.properties <<EOF
handlers=java.util.logging.ConsoleHandler
jenkins.level=FINEST
java.util.logging.ConsoleHandler.level=FINEST
EOF
docker run --name myjenkins -p 8080:8080 -p 50000:50000 --env JAVA_OPTS="-Djava.util.logging.config.file=/var/jenkins_home/log.properties" -v `pwd`/data:/var/jenkins_home jenkins/jenkins:lts
```
# Configuring reverse proxy
If you want to install Jenkins behind a reverse proxy with prefix, example: mysite.com/jenkins, you need to add environnement variable `JENKINS_OPTS="--prefix=/jenkins"` and then follow the below procedures to configure your reverse proxy, which will depend if you have Apache or Nginx:
- [Apache](https://wiki.jenkins-ci.org/display/JENKINS/Running+Jenkins+behind+Apache)
- [Nginx](https://wiki.jenkins-ci.org/display/JENKINS/Jenkins+behind+an+NGinX+reverse+proxy)
# Passing Jenkins launcher parameters
Argument you pass to docker running the jenkins image are passed to jenkins launcher, so you can run for sample :
```
docker run jenkins/jenkins:lts --version
```
This will dump Jenkins version, just like when you run jenkins as an executable war.
You also can define jenkins arguments as `JENKINS_OPTS`. This is usefull to define a set of arguments to pass to jenkins launcher as you
define a derived jenkins image based on the official one with some customized settings. The following sample Dockerfile uses this option
to force use of HTTPS with a certificate included in the image
```
FROM jenkins/jenkins:lts
COPY https.pem /var/lib/jenkins/cert
COPY https.key /var/lib/jenkins/pk
ENV JENKINS_OPTS --httpPort=-1 --httpsPort=8083 --httpsCertificate=/var/lib/jenkins/cert --httpsPrivateKey=/var/lib/jenkins/pk
EXPOSE 8083
```
You can also change the default slave agent port for jenkins by defining `JENKINS_SLAVE_AGENT_PORT` in a sample Dockerfile.
```
FROM jenkins/jenkins:lts
ENV JENKINS_SLAVE_AGENT_PORT 50001
```
or as a parameter to docker,
```
docker run --name myjenkins -p 8080:8080 -p 50001:50001 --env JENKINS_SLAVE_AGENT_PORT=50001 jenkins/jenkins:lts
```
# Installing more tools
You can run your container as root - and install via apt-get, install as part of build steps via jenkins tool installers, or you can create your own Dockerfile to customise, for example:
```
FROM jenkins/jenkins:lts
# if we want to install via apt
USER root
RUN apt-get update && apt-get install -y ruby make more-thing-here
# drop back to the regular jenkins user - good practice
USER jenkins
```
In such a derived image, you can customize your jenkins instance with hook scripts or additional plugins.
For this purpose, use `/usr/share/jenkins/ref` as a place to define the default JENKINS_HOME content you
wish the target installation to look like :
```
FROM jenkins/jenkins:lts
COPY custom.groovy /usr/share/jenkins/ref/init.groovy.d/custom.groovy
```
## Preinstalling plugins
You can rely on the `install-plugins.sh` script to pass a set of plugins to download with their dependencies.
This script will perform downloads from update centers, an internet access is required for the default update centers.
### Setting update centers
During the download, the script will use update centers defined by the following environment variables:
* `JENKINS_UC` - Main update center.
This update center may offer plugin versions depending on the Jenkins LTS Core versions.
Default value: https://updates.jenkins.io
* `JENKINS_UC_EXPERIMENTAL` - [Experimental Update Center](https://jenkins.io/blog/2013/09/23/experimental-plugins-update-center/).
This center offers Alpha and Beta versions of plugins.
Default value: https://updates.jenkins.io/experimental
It is possible to override the environment variables in images.
:exclamation: Note that changing this variables **will not** change the Update Center being used by Jenkins runtime.
### Plugin version format
Use plugin artifact ID, without `-plugin` extension, and append the version if needed separated by `:`.
Dependencies that are already included in the Jenkins war will only be downloaded if their required version is newer than the one included.
There are also custom version specifiers:
* `latest` - download the latest version from the main update center.
For Jenkins LTS images
(example: `git:latest`)
* `experimental` - download the latest version from the experimental update center defined by the `JENKINS_UC_EXPERIMENTAL` environment variable (example: `filesystem_scm:experimental`)
### Script usage
You can run the script manually in Dockerfile:
```Dockerfile
FROM jenkins/jenkins:lts
RUN /usr/local/bin/install-plugins.sh docker-slaves github-branch-source:1.8
```
Furthermore it is possible to pass a file that contains this set of plugins (with or without line breaks).
```Dockerfile
FROM jenkins/jenkins:lts
COPY plugins.txt /usr/share/jenkins/ref/plugins.txt
RUN /usr/local/bin/install-plugins.sh < /usr/share/jenkins/ref/plugins.txt
```
When jenkins container starts, it will check `JENKINS_HOME` has this reference content, and copy them
there if required. It will not override such files, so if you upgraded some plugins from UI they won't
be reverted on next start.
In case you *do* want to override, append '.override' to the name of the reference file. E.g. a file named
`/usr/share/jenkins/ref/config.xml.override` will overwrite an existing `config.xml` file in JENKINS_HOME.
Also see [JENKINS-24986](https://issues.jenkins-ci.org/browse/JENKINS-24986)
Here is an example to get the list of plugins from an existing server:
```
JENKINS_HOST=username:password@myhost.com:port
curl -sSL "http://$JENKINS_HOST/pluginManager/api/xml?depth=1&xpath=/*/*/shortName|/*/*/version&wrapper=plugins" | perl -pe 's/.*?<shortName>([\w-]+).*?<version>([^<]+)()(<\/\w+>)+/\1 \2\n/g'|sed 's/ /:/'
```
Example Output:
```
cucumber-testresult-plugin:0.8.2
pam-auth:1.1
matrix-project:1.4.1
script-security:1.13
...
```
For 2.x-derived images, you may also want to
RUN echo 2.0 > /usr/share/jenkins/ref/jenkins.install.UpgradeWizard.state
to indicate that this Jenkins installation is fully configured.
Otherwise a banner will appear prompting the user to install additional plugins,
which may be inappropriate.
# Upgrading
All the data needed is in the /var/jenkins_home directory - so depending on how you manage that - depends on how you upgrade. Generally - you can copy it out - and then "docker pull" the image again - and you will have the latest LTS - you can then start up with -v pointing to that data (/var/jenkins_home) and everything will be as you left it.
As always - please ensure that you know how to drive docker - especially volume handling!
## Upgrading plugins
By default, plugins will be upgraded if they haven't been upgraded manually and if the version from the docker image is newer than the version in the container. Versions installed by the docker image are tracked through a marker file.
The default behaviour when upgrading from a docker image that didn't write marker files is to leave existing plugins in place. If you want to upgrade existing plugins without marker you may run the docker image with `-e TRY_UPGRADE_IF_NO_MARKER=true`. Then plugins will be upgraded if the version provided by the docker image is newer.
# Building
Build with the usual
docker build -t jenkins/jenkins .
Tests are written using [bats](https://github.com/sstephenson/bats) under the `tests` dir
bats tests
Bats can be easily installed with `brew install bats` on OS X
# Questions?
Jump on irc.freenode.net and the #jenkins room. Ask!
{
"registry-mirrors": ["https://fwajlt71.mirror.aliyuncs.com"]
}
master:
build: .
environment:
JAVA_OPTS: "-Djava.awt.headless=true"
ports:
- "50000:50000"
- "8080:8080"
volumes:
- /var/jenkins_home
import hudson.model.*;
import jenkins.model.*;
Thread.start {
sleep 10000
println "--> setting agent port for jnlp"
def env = System.getenv()
int port = env['JENKINS_SLAVE_AGENT_PORT'].toInteger()
Jenkins.instance.setSlaveAgentPort(port)
println "--> setting agent port for jnlp... done"
}
#!/bin/bash -eu
# Resolve dependencies and download plugins given on the command line
#
# FROM jenkins
# RUN install-plugins.sh docker-slaves github-branch-source
set -o pipefail
REF_DIR=${REF:-/usr/share/jenkins/ref/plugins}
FAILED="$REF_DIR/failed-plugins.txt"
. /usr/local/bin/jenkins-support
getLockFile() {
printf '%s' "$REF_DIR/${1}.lock"
}
getArchiveFilename() {
printf '%s' "$REF_DIR/${1}.jpi"
}
download() {
local plugin originalPlugin version lock ignoreLockFile
plugin="$1"
version="${2:-latest}"
ignoreLockFile="${3:-}"
lock="$(getLockFile "$plugin")"
if [[ $ignoreLockFile ]] || mkdir "$lock" &>/dev/null; then
if ! doDownload "$plugin" "$version"; then
# some plugin don't follow the rules about artifact ID
# typically: docker-plugin
originalPlugin="$plugin"
plugin="${plugin}-plugin"
if ! doDownload "$plugin" "$version"; then
echo "Failed to download plugin: $originalPlugin or $plugin" >&2
echo "Not downloaded: ${originalPlugin}" >> "$FAILED"
return 1
fi
fi
if ! checkIntegrity "$plugin"; then
echo "Downloaded file is not a valid ZIP: $(getArchiveFilename "$plugin")" >&2
echo "Download integrity: ${plugin}" >> "$FAILED"
return 1
fi
resolveDependencies "$plugin"
fi
}
doDownload() {
local plugin version url jpi
plugin="$1"
version="$2"
jpi="$(getArchiveFilename "$plugin")"
# If plugin already exists and is the same version do not download
if test -f "$jpi" && unzip -p "$jpi" META-INF/MANIFEST.MF | tr -d '\r' | grep "^Plugin-Version: ${version}$" > /dev/null; then
echo "Using provided plugin: $plugin"
return 0
fi
if [[ "$version" == "latest" && -n "$JENKINS_UC_LATEST" ]]; then
# If version-specific Update Center is available, which is the case for LTS versions,
# use it to resolve latest versions.
url="$JENKINS_UC_LATEST/latest/${plugin}.hpi"
elif [[ "$version" == "experimental" && -n "$JENKINS_UC_EXPERIMENTAL" ]]; then
# Download from the experimental update center
url="$JENKINS_UC_EXPERIMENTAL/latest/${plugin}.hpi"
else
JENKINS_UC_DOWNLOAD=${JENKINS_UC_DOWNLOAD:-"$JENKINS_UC/download"}
url="$JENKINS_UC_DOWNLOAD/plugins/$plugin/$version/${plugin}.hpi"
fi
echo "Downloading plugin: $plugin from $url"
curl --connect-timeout "${CURL_CONNECTION_TIMEOUT:-20}" --retry "${CURL_RETRY:-5}" --retry-delay "${CURL_RETRY_DELAY:-0}" --retry-max-time "${CURL_RETRY_MAX_TIME:-60}" -s -f -L "$url" -o "$jpi"
return $?
}
checkIntegrity() {
local plugin jpi
plugin="$1"
jpi="$(getArchiveFilename "$plugin")"
unzip -t -qq "$jpi" >/dev/null
return $?
}
resolveDependencies() {
local plugin jpi dependencies
plugin="$1"
jpi="$(getArchiveFilename "$plugin")"
dependencies="$(unzip -p "$jpi" META-INF/MANIFEST.MF | tr -d '\r' | tr '\n' '|' | sed -e 's#| ##g' | tr '|' '\n' | grep "^Plugin-Dependencies: " | sed -e 's#^Plugin-Dependencies: ##')"
if [[ ! $dependencies ]]; then
echo " > $plugin has no dependencies"
return
fi
echo " > $plugin depends on $dependencies"
IFS=',' read -r -a array <<< "$dependencies"
for d in "${array[@]}"
do
plugin="$(cut -d':' -f1 - <<< "$d")"
if [[ $d == *"resolution:=optional"* ]]; then
echo "Skipping optional dependency $plugin"
else
local pluginInstalled
if pluginInstalled="$(echo "${bundledPlugins}" | grep "^${plugin}:")"; then
pluginInstalled="${pluginInstalled//[$'\r']}"
local versionInstalled; versionInstalled=$(versionFromPlugin "${pluginInstalled}")
local minVersion; minVersion=$(versionFromPlugin "${d}")
if versionLT "${versionInstalled}" "${minVersion}"; then
echo "Upgrading bundled dependency $d ($minVersion > $versionInstalled)"
download "$plugin" &
else
echo "Skipping already bundled dependency $d ($minVersion <= $versionInstalled)"
fi
else
download "$plugin" &
fi
fi
done
wait
}
bundledPlugins() {
local JENKINS_WAR=/usr/share/jenkins/jenkins.war
if [ -f $JENKINS_WAR ]
then
TEMP_PLUGIN_DIR=/tmp/plugintemp.$$
for i in $(jar tf $JENKINS_WAR | grep -E '[^detached-]plugins.*\..pi' | sort)
do
rm -fr $TEMP_PLUGIN_DIR
mkdir -p $TEMP_PLUGIN_DIR
PLUGIN=$(basename "$i"|cut -f1 -d'.')
(cd $TEMP_PLUGIN_DIR;jar xf "$JENKINS_WAR" "$i";jar xvf "$TEMP_PLUGIN_DIR/$i" META-INF/MANIFEST.MF >/dev/null 2>&1)
VER=$(grep -E -i Plugin-Version "$TEMP_PLUGIN_DIR/META-INF/MANIFEST.MF"|cut -d: -f2|sed 's/ //')
echo "$PLUGIN:$VER"
done
rm -fr $TEMP_PLUGIN_DIR
else
rm -f "$TEMP_ALREADY_INSTALLED"
echo "ERROR file not found: $JENKINS_WAR"
exit 1
fi
}
versionFromPlugin() {
local plugin=$1
if [[ $plugin =~ .*:.* ]]; then
echo "${plugin##*:}"
else
echo "latest"
fi
}
installedPlugins() {
for f in "$REF_DIR"/*.jpi; do
echo "$(basename "$f" | sed -e 's/\.jpi//'):$(get_plugin_version "$f")"
done
}
jenkinsMajorMinorVersion() {
local JENKINS_WAR
JENKINS_WAR=/usr/share/jenkins/jenkins.war
if [[ -f "$JENKINS_WAR" ]]; then
local version major minor
version="$(java -jar /usr/share/jenkins/jenkins.war --version)"
major="$(echo "$version" | cut -d '.' -f 1)"
minor="$(echo "$version" | cut -d '.' -f 2)"
echo "$major.$minor"
else
echo "ERROR file not found: $JENKINS_WAR"
return 1
fi
}
main() {
local plugin pluginVersion jenkinsVersion
local plugins=()
mkdir -p "$REF_DIR" || exit 1
# Read plugins from stdin or from the command line arguments
if [[ ($# -eq 0) ]]; then
while read -r line; do
plugins+=("${line}")
done
else
plugins=("$@")
fi
# Create lockfile manually before first run to make sure any explicit version set is used.
echo "Creating initial locks..."
for plugin in "${plugins[@]}"; do
mkdir "$(getLockFile "${plugin%%:*}")"
done
echo "Analyzing war..."
bundledPlugins="$(bundledPlugins)"
# Check if there's a version-specific update center, which is the case for LTS versions
jenkinsVersion="$(jenkinsMajorMinorVersion)"
if curl -fsL -o /dev/null "$JENKINS_UC/$jenkinsVersion"; then
JENKINS_UC_LATEST="$JENKINS_UC/$jenkinsVersion"
echo "Using version-specific update center: $JENKINS_UC_LATEST..."
else
JENKINS_UC_LATEST=
fi
echo "Downloading plugins..."
for plugin in "${plugins[@]}"; do
pluginVersion=""
if [[ $plugin =~ .*:.* ]]; then
pluginVersion=$(versionFromPlugin "${plugin}")
plugin="${plugin%%:*}"
fi
download "$plugin" "$pluginVersion" "true" &
done
wait
echo
echo "WAR bundled plugins:"
echo "${bundledPlugins}"
echo
echo "Installed plugins:"
installedPlugins
if [[ -f $FAILED ]]; then
echo "Some plugins failed to download!" "$(<"$FAILED")" >&2
exit 1
fi
echo "Cleaning up locks"
rm -r "$REF_DIR"/*.lock
}
main "$@"
#!/bin/bash -eu
# compare if version1 < version2
versionLT() {
local v1; v1=$(echo "$1" | cut -d '-' -f 1 )
local q1; q1=$(echo "$1" | cut -s -d '-' -f 2- )
local v2; v2=$(echo "$2" | cut -d '-' -f 1 )
local q2; q2=$(echo "$2" | cut -s -d '-' -f 2- )
if [ "$v1" = "$v2" ]; then
if [ "$q1" = "$q2" ]; then
return 1
else
if [ -z "$q1" ]; then
return 1
else
if [ -z "$q2" ]; then
return 0
else
[ "$q1" = "$(echo -e "$q1\n$q2" | sort -V | head -n1)" ]
fi
fi
fi
else
[ "$v1" = "$(echo -e "$v1\n$v2" | sort -V | head -n1)" ]
fi
}
# returns a plugin version from a plugin archive
get_plugin_version() {
local archive; archive=$1
local version; version=$(unzip -p "$archive" META-INF/MANIFEST.MF | grep "^Plugin-Version: " | sed -e 's#^Plugin-Version: ##')
version=${version%%[[:space:]]}
echo "$version"
}
# Copy files from /usr/share/jenkins/ref into $JENKINS_HOME
# So the initial JENKINS-HOME is set with expected content.
# Don't override, as this is just a reference setup, and use from UI
# can then change this, upgrade plugins, etc.
copy_reference_file() {
f="${1%/}"
b="${f%.override}"
rel="${b:23}"
version_marker="${rel}.version_from_image"
dir=$(dirname "${b}")
local action;
local reason;
local container_version;
local image_version;
local marker_version;
local log; log=false
if [[ ${rel} == plugins/*.jpi ]]; then
container_version=$(get_plugin_version "$JENKINS_HOME/${rel}")
image_version=$(get_plugin_version "${f}")
if [[ -e $JENKINS_HOME/${version_marker} ]]; then
marker_version=$(cat "$JENKINS_HOME/${version_marker}")
if versionLT "$marker_version" "$container_version"; then
action="SKIPPED"
reason="Installed version ($container_version) has been manually upgraded from initial version ($marker_version)"
log=true
else
if [[ "$image_version" == "$container_version" ]]; then
action="SKIPPED"
reason="Version from image is the same as the installed version $image_version"
else
if versionLT "$image_version" "$container_version"; then
action="SKIPPED"
log=true
reason="Image version ($image_version) is older than installed version ($container_version)"
else
action="UPGRADED"
log=true
reason="Image version ($image_version) is newer than installed version ($container_version)"
fi
fi
fi
else
if [[ -n "$TRY_UPGRADE_IF_NO_MARKER" ]]; then
if [[ "$image_version" == "$container_version" ]]; then
action="SKIPPED"
reason="Version from image is the same as the installed version $image_version (no marker found)"
# Add marker for next time
echo "$image_version" > "$JENKINS_HOME/${version_marker}"
else
if versionLT "$image_version" "$container_version"; then
action="SKIPPED"
log=true
reason="Image version ($image_version) is older than installed version ($container_version) (no marker found)"
else
action="UPGRADED"
log=true
reason="Image version ($image_version) is newer than installed version ($container_version) (no marker found)"
fi
fi
fi
fi
if [[ ! -e $JENKINS_HOME/${rel} || "$action" == "UPGRADED" || $f = *.override ]]; then
action=${action:-"INSTALLED"}
log=true
mkdir -p "$JENKINS_HOME/${dir:23}"
cp -r "${f}" "$JENKINS_HOME/${rel}";
# pin plugins on initial copy
touch "$JENKINS_HOME/${rel}.pinned"
echo "$image_version" > "$JENKINS_HOME/${version_marker}"
reason=${reason:-$image_version}
else
action=${action:-"SKIPPED"}
fi
else
if [[ ! -e $JENKINS_HOME/${rel} || $f = *.override ]]
then
action="INSTALLED"
log=true
mkdir -p "$JENKINS_HOME/${dir:23}"
cp -r "${f}" "$JENKINS_HOME/${rel}";
else
action="SKIPPED"
fi
fi
if [[ -n "$VERBOSE" || "$log" == "true" ]]; then
if [ -z "$reason" ]; then
echo "$action $rel" >> "$COPY_REFERENCE_FILE_LOG"
else
echo "$action $rel : $reason" >> "$COPY_REFERENCE_FILE_LOG"
fi
fi
}
\ No newline at end of file
#! /bin/bash -e
: "${JENKINS_HOME:="/var/jenkins_home"}"
touch "${COPY_REFERENCE_FILE_LOG}" || { echo "Can not write to ${COPY_REFERENCE_FILE_LOG}. Wrong volume permissions?"; exit 1; }
echo "--- Copying files at $(date)" >> "$COPY_REFERENCE_FILE_LOG"
find /usr/share/jenkins/ref/ \( -type f -o -type l \) -exec bash -c '. /usr/local/bin/jenkins-support; for arg; do copy_reference_file "$arg"; done' _ {} +
# if `docker run` first argument start with `--` the user is passing jenkins launcher arguments
if [[ $# -lt 1 ]] || [[ "$1" == "--"* ]]; then
# read JAVA_OPTS and JENKINS_OPTS into arrays to avoid need for eval (and associated vulnerabilities)
java_opts_array=()
while IFS= read -r -d '' item; do
java_opts_array+=( "$item" )
done < <([[ $JAVA_OPTS ]] && xargs printf '%s\0' <<<"$JAVA_OPTS")
jenkins_opts_array=( )
while IFS= read -r -d '' item; do
jenkins_opts_array+=( "$item" )
done < <([[ $JENKINS_OPTS ]] && xargs printf '%s\0' <<<"$JENKINS_OPTS")
exec java "${java_opts_array[@]}" -jar /usr/share/jenkins/jenkins.war "${jenkins_opts_array[@]}" "$@"
fi
# As argument is not jenkins, assume user want to run his own process, for example a `bash` shell to explore this image
exec "$@"
#! /bin/bash
# Parse a support-core plugin -style txt file as specification for jenkins plugins to be installed
# in the reference directory, so user can define a derived Docker image with just :
#
# FROM jenkins
# COPY plugins.txt /plugins.txt
# RUN /usr/local/bin/plugins.sh /plugins.txt
#
# Note: Plugins already installed are skipped
#
set -e
echo "WARN: plugins.sh is deprecated, please switch to install-plugins.sh"
if [ -z "$1" ]
then
echo "
USAGE:
Parse a support-core plugin -style txt file as specification for jenkins plugins to be installed
in the reference directory, so user can define a derived Docker image with just :
FROM jenkins
COPY plugins.txt /plugins.txt
RUN /usr/local/bin/plugins.sh /plugins.txt
Note: Plugins already installed are skipped
"
exit 1
else
JENKINS_INPUT_JOB_LIST=$1
if [ ! -f "$JENKINS_INPUT_JOB_LIST" ]
then
echo "ERROR File not found: $JENKINS_INPUT_JOB_LIST"
exit 1
fi
fi
# the war includes a # of plugins, to make the build efficient filter out
# the plugins so we dont install 2x - there about 17!
if [ -d "$JENKINS_HOME" ]
then
TEMP_ALREADY_INSTALLED=$JENKINS_HOME/preinstalled.plugins.$$.txt
else
echo "ERROR $JENKINS_HOME not found"
exit 1
fi
JENKINS_PLUGINS_DIR=/var/jenkins_home/plugins
if [ -d "$JENKINS_PLUGINS_DIR" ]
then
echo "Analyzing: $JENKINS_PLUGINS_DIR"
for i in "$JENKINS_PLUGINS_DIR"/*/; do
JENKINS_PLUGIN=$(basename "$i")
JENKINS_PLUGIN_VER=$(grep -E -i Plugin-Version "$i/META-INF/MANIFEST.MF"|cut -d: -f2|sed 's/ //')
echo "$JENKINS_PLUGIN:$JENKINS_PLUGIN_VER"
done >"$TEMP_ALREADY_INSTALLED"
else
JENKINS_WAR=/usr/share/jenkins/jenkins.war
if [ -f "$JENKINS_WAR" ]
then
echo "Analyzing war: $JENKINS_WAR"
TEMP_PLUGIN_DIR=/tmp/plugintemp.$$
while read -r i <&3; do
rm -fr "$TEMP_PLUGIN_DIR"
mkdir -p "$TEMP_PLUGIN_DIR"
PLUGIN=$(basename "$i"|cut -f1 -d'.')
(cd "$TEMP_PLUGIN_DIR" || exit; jar xf "$JENKINS_WAR" "$i"; jar xvf "$TEMP_PLUGIN_DIR/$i" META-INF/MANIFEST.MF >/dev/null 2>&1)
VER=$(grep -E -i Plugin-Version "$TEMP_PLUGIN_DIR/META-INF/MANIFEST.MF"|cut -d: -f2|sed 's/ //')
echo "$PLUGIN:$VER"
done 3< <(jar tf "$JENKINS_WAR" | grep -E '[^detached-]plugins.*\..pi' | sort) > "$TEMP_ALREADY_INSTALLED"
rm -fr "$TEMP_PLUGIN_DIR"
else
rm -f "$TEMP_ALREADY_INSTALLED"
echo "ERROR file not found: $JENKINS_WAR"
exit 1
fi
fi
REF=/usr/share/jenkins/ref/plugins
mkdir -p $REF
COUNT_PLUGINS_INSTALLED=0
while read -r spec || [ -n "$spec" ]; do
plugin=()
IFS=' ' read -r -a plugin <<< "${spec//:/ }"
[[ ${plugin[0]} =~ ^# ]] && continue
[[ ${plugin[0]} =~ ^[[:space:]]*$ ]] && continue
[[ -z ${plugin[1]} ]] && plugin[1]="latest"
if [ -z "$JENKINS_UC_DOWNLOAD" ]; then
JENKINS_UC_DOWNLOAD=$JENKINS_UC/download
fi
if ! grep -q "${plugin[0]}:${plugin[1]}" "$TEMP_ALREADY_INSTALLED"
then
echo "Downloading ${plugin[0]}:${plugin[1]}"
curl --retry 3 --retry-delay 5 -sSL -f "${JENKINS_UC_DOWNLOAD}/plugins/${plugin[0]}/${plugin[1]}/${plugin[0]}.hpi" -o "$REF/${plugin[0]}.jpi"
unzip -qqt "$REF/${plugin[0]}.jpi"
(( COUNT_PLUGINS_INSTALLED += 1 ))
else
echo " ... skipping already installed: ${plugin[0]}:${plugin[1]}"
fi
done < "$JENKINS_INPUT_JOB_LIST"
echo "---------------------------------------------------"
if (( "$COUNT_PLUGINS_INSTALLED" > 0 ))
then
echo "INFO: Successfully installed $COUNT_PLUGINS_INSTALLED plugins."
if [ -d $JENKINS_PLUGINS_DIR ]
then
echo "INFO: Please restart the container for changes to take effect!"
fi
else
echo "INFO: No changes, all plugins previously installed."
fi
echo "---------------------------------------------------"
#cleanup
rm "$TEMP_ALREADY_INSTALLED"
exit 0
#!/bin/bash -eu
# Publish any versions of the docker image not yet pushed to jenkins/jenkins
# Arguments:
# -n dry run, do not build or publish images
# -d debug
set -o pipefail
sort-versions() {
if [ "$(uname)" == 'Darwin' ]; then
gsort --version-sort
else
sort --version-sort
fi
}
# Try tagging with and without -f to support all versions of docker
docker-tag() {
local from="jenkins/jenkins:$1"
local to="$2/jenkins:$3"
local out
docker pull "$from"
if out=$(docker tag -f "$from" "$to" 2>&1); then
echo "$out"
else
docker tag "$from" "$to"
fi
}
login-token() {
# could use jq .token
curl -q -sSL "https://auth.docker.io/token?service=registry.docker.io&scope=repository:jenkins/jenkins:pull" | grep -o '"token":"[^"]*"' | cut -d':' -f 2 | xargs echo
}
is-published() {
get-manifest "$1" &> /dev/null
}
get-manifest() {
local tag=$1
local opts=""
if [ "$debug" = true ]; then
opts="-v"
fi
curl $opts -q -fsSL -H "Accept: application/vnd.docker.distribution.manifest.v2+json" -H "Authorization: Bearer $TOKEN" "https://index.docker.io/v2/jenkinsci/jenkins/manifests/$tag"
}
get-digest() {
local manifest
manifest=$(get-manifest "$1")
#get-manifest "$1" | jq .config.digest
if [ "$debug" = true ]; then
>&2 echo "DEBUG: Manifest for $1: $manifest"
fi
echo "$manifest" | grep -A 10 -o '"config".*' | grep digest | head -1 | cut -d':' -f 2,3 | xargs echo
}
get-latest-versions() {
curl -q -fsSL https://repo.jenkins-ci.org/releases/org/jenkins-ci/main/jenkins-war/maven-metadata.xml | grep '<version>.*</version>' | grep -E -o '[0-9]+(\.[0-9]+)+' | sort-versions | uniq | tail -n 20
}
publish() {
local version=$1
local variant=$2
local tag="${version}${variant}"
local sha
local build_opts=(--no-cache --pull)
if [ "$dry_run" = true ]; then
build_opts=()
fi
local dir=war
# lts is in a different dir
if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
dir=war-stable
fi
sha=$(curl -q -fsSL "http://mirrors.jenkins.io/${dir}/${version}/jenkins.war.sha256" | cut -d' ' -f 1)
docker build --file "Dockerfile$variant" \
--build-arg "JENKINS_VERSION=$version" \
--build-arg "JENKINS_SHA=$sha" \
--tag "jenkins/jenkins:${tag}" \
--tag "jenkinsci/jenkins:${tag}" \
"${build_opts[@]}" .
if [ ! "$dry_run" = true ]; then
docker push "jenkins/jenkins:${tag}"
docker push "jenkinsci/jenkins:${tag}"
fi
}
tag-and-push() {
local source=$1
local target=$2
local digest_source
local digest_target
if [ "$debug" = true ]; then
>&2 echo "DEBUG: Getting digest for ${source}"
fi
# if tag doesn't exist yet, ie. dry run
if ! digest_source=$(get-digest "${source}"); then
echo "Unable to get digest for ${source} ${digest_source}"
digest_source=""
fi
if [ "$debug" = true ]; then
>&2 echo "DEBUG: Getting digest for ${target}"
fi
if ! digest_target=$(get-digest "${target}"); then
echo "Unable to get digest for ${target} ${digest_target}"
digest_target=""
fi
if [ "$digest_source" == "$digest_target" ] && [ -n "${digest_target}" ]; then
echo "Images ${source} [$digest_source] and ${target} [$digest_target] are already the same, not updating tags"
else
echo "Creating tag ${target} pointing to ${source}"
docker-tag "${source}" "jenkins" "${target}"
docker-tag "${source}" "jenkinsci" "${target}"
if [ ! "$dry_run" = true ]; then
echo "Pushing jenkins/jenkins:${target}"
docker push "jenkins/jenkins:${target}"
docker push "jenkinsci/jenkins:${target}"
else
echo "Would push jenkins/jenkins:${target}"
fi
fi
}
publish-latest() {
local version=$1
local variant=$2
# push latest (for master) or the name of the branch (for other branches)
if [ -z "${variant}" ]; then
tag-and-push "${version}${variant}" "latest"
else
tag-and-push "${version}${variant}" "${variant#-}"
fi
}
publish-lts() {
local version=$1
local variant=$2
tag-and-push "${version}${variant}" "lts${variant}"
}
# Process arguments
dry_run=false
debug=false
variant=""
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
-n)
dry_run=true
;;
-d)
debug=true
;;
-v|--variant)
variant="-"$2
shift
;;
*)
echo "Unknown option: $key"
return 1
;;
esac
shift
done
if [ "$dry_run" = true ]; then
echo "Dry run, will not publish images"
fi
TOKEN=$(login-token)
lts_version=""
version=""
for version in $(get-latest-versions); do
if is-published "$version$variant"; then
echo "Tag is already published: $version$variant"
else
echo "Publishing version: $version$variant"
publish "$version" "$variant"
fi
# Update lts tag
if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
lts_version="${version}"
fi
done
publish-latest "${version}" "${variant}"
if [ -n "${lts_version}" ]; then
publish-lts "${lts_version}" "${variant}"
fi
#!/bin/bash -eu
# Generate the Docker official-images file
sha() {
local branch=$1
git rev-parse "$branch"
}
version_from_dockerfile() {
local branch=$1
git show "$branch:Dockerfile" | grep JENKINS_VERSION: | sed -e 's/.*:-\(.*\)}/\1/'
}
master_sha=$(sha master)
alpine_sha=$(sha alpine)
master_version=$(version_from_dockerfile master)
alpine_version=$(version_from_dockerfile alpine)
if ! [ "$master_version" == "$alpine_version" ]; then
echo "Master version '$master_version' does not match alpine version '$alpine_version'"
exit 1
fi
cat << EOF > ../official-images/library/jenkins
# maintainer: Nicolas De Loof <nicolas.deloof@gmail.com> (@ndeloof)
# maintainer: Michael Neale <mneale@cloudbees.com> (@michaelneale)
# maintainer: Carlos Sanchez <csanchez@cloudbees.com> (@carlossg)
latest: git://github.com/jenkinsci/jenkins-ci.org-docker@$master_sha
$master_version: git://github.com/jenkinsci/jenkins-ci.org-docker@$master_sha
alpine: git://github.com/jenkinsci/jenkins-ci.org-docker@$alpine_sha
$alpine_version-alpine: git://github.com/jenkinsci/jenkins-ci.org-docker@$alpine_sha
EOF
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment