Merge pull request #803 from activeobjects/master
Added support for jenkins official container
This commit is contained in:
commit
c78eb5228a
@ -517,6 +517,19 @@ services:
|
|||||||
- proxy
|
- proxy
|
||||||
- proxy2
|
- proxy2
|
||||||
|
|
||||||
|
### Jenkins ###################################################
|
||||||
|
jenkins:
|
||||||
|
build: ./jenkins
|
||||||
|
environment:
|
||||||
|
JAVA_OPTS: "-Djava.awt.headless=true"
|
||||||
|
ports:
|
||||||
|
- "${JENKINS_HOST_SLAVE_AGENT_PORT}:50000"
|
||||||
|
- "${JENKINS_HOST_HTTP_PORT}:8080"
|
||||||
|
privileged: true
|
||||||
|
volumes:
|
||||||
|
- ${JENKINS_HOME}:/var/jenkins_home
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
|
||||||
### Networks Setup ############################################
|
### Networks Setup ############################################
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
|
@ -146,6 +146,11 @@ VARNISH_PROXY2_SERVER=SERVER2
|
|||||||
### HAPROXY Container
|
### HAPROXY Container
|
||||||
HAPROXY_HOST_HTTP_PORT=8085
|
HAPROXY_HOST_HTTP_PORT=8085
|
||||||
|
|
||||||
|
### JENKINS Container
|
||||||
|
JENKINS_HOST_HTTP_PORT=8090
|
||||||
|
JENKINS_HOST_SLAVE_AGENT_PORT=50000
|
||||||
|
JENKINS_HOME=./jenkins/jenkins_home
|
||||||
|
|
||||||
### MISC
|
### MISC
|
||||||
# Replace with your Docker Host IP (will be appended to /etc/hosts)
|
# Replace with your Docker Host IP (will be appended to /etc/hosts)
|
||||||
DOCKER_HOST_IP=10.0.75.1
|
DOCKER_HOST_IP=10.0.75.1
|
||||||
|
23
jenkins/.github/ISSUE_TEMPLATE.md
vendored
Normal file
23
jenkins/.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# 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
|
||||||
|
|
6
jenkins/.gitmodules
vendored
Normal file
6
jenkins/.gitmodules
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[submodule "tests/test_helper/bats-support"]
|
||||||
|
path = tests/test_helper/bats-support
|
||||||
|
url = https://github.com/ztombol/bats-support
|
||||||
|
[submodule "tests/test_helper/bats-assert"]
|
||||||
|
path = tests/test_helper/bats-assert
|
||||||
|
url = https://github.com/ztombol/bats-assert
|
16
jenkins/CONTRIBUTING.md
Normal file
16
jenkins/CONTRIBUTING.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# 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
|
108
jenkins/Dockerfile
Normal file
108
jenkins/Dockerfile
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
FROM openjdk:8-jdk
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y git curl && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
ENV JENKINS_HOME /var/jenkins_home
|
||||||
|
ENV JENKINS_SLAVE_AGENT_PORT 50000
|
||||||
|
|
||||||
|
ARG user=jenkins
|
||||||
|
ARG group=jenkins
|
||||||
|
ARG uid=1000
|
||||||
|
ARG gid=1000
|
||||||
|
|
||||||
|
# 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}
|
||||||
|
|
||||||
|
# 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.13.2
|
||||||
|
ENV TINI_SHA afbf8de8a63ce8e4f18cb3f34dfdbbd354af68a1
|
||||||
|
|
||||||
|
# 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" | sha1sum -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.32.3}
|
||||||
|
|
||||||
|
# jenkins.war checksum, download will be validated using it
|
||||||
|
ARG JENKINS_SHA=a25b9a314ca9e76f9673da7309e1882e32674223
|
||||||
|
|
||||||
|
# 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" | sha1sum -c -
|
||||||
|
|
||||||
|
ENV JENKINS_UC https://updates.jenkins.io
|
||||||
|
RUN chown -R ${user} "$JENKINS_HOME" /usr/share/jenkins/ref
|
||||||
|
|
||||||
|
|
||||||
|
# Add jenkins to the correct group
|
||||||
|
# see http://stackoverflow.com/questions/42164653/docker-in-docker-permissions-error
|
||||||
|
# use "getent group docker | awk -F: '{printf "%d\n", $3}'" command on host to find correct value for gid or simply use 'id'
|
||||||
|
ARG DOCKER_GID=998
|
||||||
|
|
||||||
|
RUN groupadd -g ${DOCKER_GID} docker \
|
||||||
|
&& curl -sSL https://get.docker.com/ | sh \
|
||||||
|
&& apt-get -q autoremove \
|
||||||
|
&& apt-get -q clean -y \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/*.bin
|
||||||
|
|
||||||
|
# Install Docker-in-Docker from git@github.com:jpetazzo/dind.git
|
||||||
|
# RUN apt-get update -qq && apt-get install -qqy apt-transport-https ca-certificates curl lxc iptables
|
||||||
|
# Install Docker from Docker Inc. repositories.
|
||||||
|
RUN apt-get install -y curl && curl -sSL https://get.docker.com/ | sh
|
||||||
|
RUN usermod -aG docker jenkins
|
||||||
|
|
||||||
|
# Install Docker-Compose
|
||||||
|
RUN curl -L "https://github.com/docker/compose/releases/download/1.10.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||||
|
RUN chmod +x /usr/local/bin/docker-compose
|
||||||
|
|
||||||
|
|
||||||
|
# for main web interface:
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# will be used by attached slave agents:
|
||||||
|
EXPOSE 50000
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# Only need below if we are starting from empty jenkins_home
|
||||||
|
## Copy the RSA keys
|
||||||
|
#RUN mkdir -p /var/jenkins_home/.ssh
|
||||||
|
#RUN chown jenkins:jenkins /var/jenkins_home/.ssh
|
||||||
|
#COPY keys/id_rsa /var/jenkins_home/.ssh/id_rsa.pub
|
||||||
|
#COPY keys/id_rsa /var/jenkins_home/.ssh/id_rsa
|
||||||
|
#COPY keys/known_hosts /var/jenkins_home/.ssh/known_hosts
|
||||||
|
#
|
||||||
|
#USER root
|
||||||
|
#RUN chmod 600 /var/jenkins_home/.ssh/id_rsa
|
||||||
|
#RUN chmod 644 /var/jenkins_home/.ssh/id_rsa.pub
|
||||||
|
## ssh-keyscan -H github.com >> ~/.ssh/known_hosts
|
||||||
|
## ssh-keyscan -H bitbucket.org >> ~/.ssh/known_hosts
|
38
jenkins/Jenkinsfile
vendored
Normal file
38
jenkins/Jenkinsfile
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env groovy
|
||||||
|
|
||||||
|
properties([
|
||||||
|
buildDiscarder(logRotator(numToKeepStr: '5', artifactNumToKeepStr: '5')),
|
||||||
|
pipelineTriggers([cron('@daily')]),
|
||||||
|
])
|
||||||
|
|
||||||
|
node('docker') {
|
||||||
|
deleteDir()
|
||||||
|
|
||||||
|
stage('Checkout') {
|
||||||
|
checkout scm
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!infra.isTrusted()) {
|
||||||
|
/* Outside of the trusted.ci environment, we're building and testing
|
||||||
|
* the Dockerful in this repository, but not publishing to docker hub
|
||||||
|
*/
|
||||||
|
stage('Build') {
|
||||||
|
docker.build('jenkins')
|
||||||
|
}
|
||||||
|
|
||||||
|
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') {
|
||||||
|
sh './publish.sh'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
226
jenkins/README.md
Normal file
226
jenkins/README.md
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
# Official Jenkins Docker image
|
||||||
|
|
||||||
|
The Jenkins Continuous Integration and Delivery server.
|
||||||
|
|
||||||
|
This is a fully functional Jenkins server, based on the Long Term Support release.
|
||||||
|
[http://jenkins.io/](http://jenkins.io/).
|
||||||
|
|
||||||
|
For weekly releases check out [`jenkinsci/jenkins`](https://hub.docker.com/r/jenkinsci/jenkins/)
|
||||||
|
|
||||||
|
|
||||||
|
<img src="http://jenkins-ci.org/sites/default/files/jenkins_logo.png"/>
|
||||||
|
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run -p 8080:8080 -p 50000:50000 jenkins
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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 ou 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 --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:1.565.3
|
||||||
|
|
||||||
|
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:1.565.3
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# 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
|
||||||
|
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.
|
||||||
|
Use plugin artifact ID, whithout `-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.
|
||||||
|
|
||||||
|
```
|
||||||
|
FROM jenkins
|
||||||
|
RUN /usr/local/bin/install-plugins.sh docker-slaves github-branch-source:1.8
|
||||||
|
```
|
||||||
|
|
||||||
|
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 .
|
||||||
|
|
||||||
|
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!
|
14
jenkins/docker-compose.yml
Normal file
14
jenkins/docker-compose.yml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
master:
|
||||||
|
build: .
|
||||||
|
environment:
|
||||||
|
JAVA_OPTS: "-Djava.awt.headless=true"
|
||||||
|
ports:
|
||||||
|
- "50000:50000"
|
||||||
|
# Expose Jenkins to parent on port 8090
|
||||||
|
- "8090:8080"
|
||||||
|
# Allow Docker In Docker
|
||||||
|
privileged: true
|
||||||
|
volumes:
|
||||||
|
- ./jenkins_home:/var/jenkins_home
|
||||||
|
# Allow Docker In Docker to use parent docker container
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
12
jenkins/init.groovy
Normal file
12
jenkins/init.groovy
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
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"
|
||||||
|
}
|
205
jenkins/install-plugins.sh
Normal file
205
jenkins/install-plugins.sh
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
#!/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
|
||||||
|
|
||||||
|
JENKINS_UC_DOWNLOAD=${JENKINS_UC_DOWNLOAD:-"$JENKINS_UC/download"}
|
||||||
|
|
||||||
|
url="$JENKINS_UC_DOWNLOAD/plugins/$plugin/$version/${plugin}.hpi"
|
||||||
|
|
||||||
|
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 | egrep '[^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=$(egrep -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
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
local plugin version
|
||||||
|
|
||||||
|
mkdir -p "$REF_DIR" || exit 1
|
||||||
|
|
||||||
|
# Create lockfile manually before first run to make sure any explicit version set is used.
|
||||||
|
echo "Creating initial locks..."
|
||||||
|
for plugin in "$@"; do
|
||||||
|
mkdir "$(getLockFile "${plugin%%:*}")"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Analyzing war..."
|
||||||
|
bundledPlugins="$(bundledPlugins)"
|
||||||
|
|
||||||
|
echo "Downloading plugins..."
|
||||||
|
for plugin in "$@"; do
|
||||||
|
version=""
|
||||||
|
|
||||||
|
if [[ $plugin =~ .*:.* ]]; then
|
||||||
|
version=$(versionFromPlugin "${plugin}")
|
||||||
|
plugin="${plugin%%:*}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
download "$plugin" "$version" "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 "$@"
|
127
jenkins/jenkins-support
Normal file
127
jenkins/jenkins-support
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
#!/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
|
||||||
|
}
|
26
jenkins/jenkins.sh
Normal file
26
jenkins/jenkins.sh
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#! /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 -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 "$@"
|
28
jenkins/jenkins_home/.gitignore
vendored
Normal file
28
jenkins/jenkins_home/.gitignore
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# File Patterns to Ignore
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
*.log.*
|
||||||
|
*.swp
|
||||||
|
|
||||||
|
# Ignore SSH Config
|
||||||
|
.ssh/*
|
||||||
|
!.ssh/known_hosts
|
||||||
|
|
||||||
|
# Include job configs and ignore other data
|
||||||
|
!config.xml
|
||||||
|
builds
|
||||||
|
lastStable
|
||||||
|
lastSuccessful
|
||||||
|
nextBuildNumber
|
||||||
|
|
||||||
|
# Ignore expanded plugins folders because we only want jpi files
|
||||||
|
plugins/*
|
||||||
|
!plugins/*.jpi
|
||||||
|
|
||||||
|
# Include user info
|
||||||
|
!userContent/*
|
||||||
|
!users/*
|
||||||
|
|
||||||
|
# Ignore Directories
|
||||||
|
workspace/
|
||||||
|
war/
|
124
jenkins/plugins.sh
Normal file
124
jenkins/plugins.sh
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
#! /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=$(egrep -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=$(egrep -i Plugin-Version "$TEMP_PLUGIN_DIR/META-INF/MANIFEST.MF"|cut -d: -f2|sed 's/ //')
|
||||||
|
echo "$PLUGIN:$VER"
|
||||||
|
done 3< <(jar tf "$JENKINS_WAR" | egrep '[^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=(${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
|
148
jenkins/publish.sh
Normal file
148
jenkins/publish.sh
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
#!/bin/bash -eu
|
||||||
|
|
||||||
|
# Publish any versions of the docker image not yet pushed to jenkinsci/jenkins
|
||||||
|
# Arguments:
|
||||||
|
# -n dry run, do not build or publish images
|
||||||
|
|
||||||
|
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="jenkinsci/jenkins:$1"
|
||||||
|
local to="jenkinsci/jenkins:$2"
|
||||||
|
local out
|
||||||
|
if out=$(docker tag -f "$from" "$to" 2>&1); then
|
||||||
|
echo "$out"
|
||||||
|
else
|
||||||
|
docker tag "$from" "$to"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
get-variant() {
|
||||||
|
local branch
|
||||||
|
branch=$(git show-ref | grep $(git rev-list -n 1 HEAD) | tail -1 | rev | cut -d/ -f 1 | rev)
|
||||||
|
if [ -z "$branch" ]; then
|
||||||
|
>&2 echo "Could not get the current branch name for commit, not in a branch?: $(git rev-list -n 1 HEAD)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
case "$branch" in
|
||||||
|
master) echo "" ;;
|
||||||
|
*) echo "-${branch}" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
login-token() {
|
||||||
|
# could use jq .token
|
||||||
|
curl -q -sSL https://auth.docker.io/token\?service\=registry.docker.io\&scope\=repository:jenkinsci/jenkins:pull | grep -o '"token":"[^"]*"' | cut -d':' -f 2 | xargs echo
|
||||||
|
}
|
||||||
|
|
||||||
|
is-published() {
|
||||||
|
get-manifest "$1" &> /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
get-manifest() {
|
||||||
|
local tag=$1
|
||||||
|
curl -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() {
|
||||||
|
#get-manifest "$1" | jq .config.digest
|
||||||
|
get-manifest "$1" | grep -A 10 -o '"config".*' | grep digest | head -1 | cut -d':' -f 2,3 | xargs echo
|
||||||
|
}
|
||||||
|
|
||||||
|
get-latest-versions() {
|
||||||
|
curl -q -fsSL https://api.github.com/repos/jenkinsci/jenkins/tags?per_page=20 | grep '"name": "jenkins-' | egrep -o '[0-9]+(\.[0-9]+)+' | sort-versions | uniq
|
||||||
|
}
|
||||||
|
|
||||||
|
publish() {
|
||||||
|
local version=$1
|
||||||
|
local variant=$2
|
||||||
|
local tag="${version}${variant}"
|
||||||
|
local sha
|
||||||
|
local build_opts="--no-cache --pull"
|
||||||
|
|
||||||
|
sha=$(curl -q -fsSL "http://repo.jenkins-ci.org/simple/releases/org/jenkins-ci/main/jenkins-war/${version}/jenkins-war-${version}.war.sha1")
|
||||||
|
|
||||||
|
docker build --build-arg "JENKINS_VERSION=$version" \
|
||||||
|
--build-arg "JENKINS_SHA=$sha" \
|
||||||
|
--tag "jenkinsci/jenkins:${tag}" ${build_opts} .
|
||||||
|
|
||||||
|
docker push "jenkinsci/jenkins:${tag}"
|
||||||
|
}
|
||||||
|
|
||||||
|
tag-and-push() {
|
||||||
|
local source=$1
|
||||||
|
local target=$2
|
||||||
|
local digest_source; digest_source=$(get-digest ${tag1})
|
||||||
|
local digest_target; digest_target=$(get-digest ${tag2})
|
||||||
|
if [ "$digest_source" == "$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}"
|
||||||
|
if [ ! "$dry_run" = true ]; then
|
||||||
|
docker-tag "jenkinsci/jenkins:${source}" "jenkinsci/jenkins:${target}"
|
||||||
|
docker push "jenkinsci/jenkins:${source}"
|
||||||
|
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}" "lts${variant}"
|
||||||
|
}
|
||||||
|
|
||||||
|
dry_run=false
|
||||||
|
if [ "-n" == "${1:-}" ]; then
|
||||||
|
dry_run=true
|
||||||
|
fi
|
||||||
|
if [ "$dry_run" = true ]; then
|
||||||
|
echo "Dry run, will not build or publish images"
|
||||||
|
fi
|
||||||
|
|
||||||
|
TOKEN=$(login-token)
|
||||||
|
|
||||||
|
variant=$(get-variant)
|
||||||
|
|
||||||
|
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"
|
||||||
|
if [ ! "$dry_run" = true ]; then
|
||||||
|
publish "$version" "$variant"
|
||||||
|
fi
|
||||||
|
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
|
31
jenkins/tests/functions.bats
Normal file
31
jenkins/tests/functions.bats
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
SUT_IMAGE=bats-jenkins
|
||||||
|
|
||||||
|
load 'test_helper/bats-support/load'
|
||||||
|
load 'test_helper/bats-assert/load'
|
||||||
|
load test_helpers
|
||||||
|
|
||||||
|
. $BATS_TEST_DIRNAME/../jenkins-support
|
||||||
|
|
||||||
|
@test "build image" {
|
||||||
|
cd $BATS_TEST_DIRNAME/..
|
||||||
|
docker_build -t $SUT_IMAGE .
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "versionLT" {
|
||||||
|
run docker run --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 1.0 1.0"
|
||||||
|
assert_failure
|
||||||
|
run docker run --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 1.0 1.1"
|
||||||
|
assert_success
|
||||||
|
run docker run --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 1.1 1.0"
|
||||||
|
assert_failure
|
||||||
|
run docker run --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 1.0-beta-1 1.0"
|
||||||
|
assert_success
|
||||||
|
run docker run --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 1.0 1.0-beta-1"
|
||||||
|
assert_failure
|
||||||
|
run docker run --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 1.0-alpha-1 1.0-beta-1"
|
||||||
|
assert_success
|
||||||
|
run docker run --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 1.0-beta-1 1.0-alpha-1"
|
||||||
|
assert_failure
|
||||||
|
}
|
118
jenkins/tests/install-plugins.bats
Normal file
118
jenkins/tests/install-plugins.bats
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
SUT_IMAGE=bats-jenkins
|
||||||
|
|
||||||
|
load 'test_helper/bats-support/load'
|
||||||
|
load 'test_helper/bats-assert/load'
|
||||||
|
load test_helpers
|
||||||
|
|
||||||
|
@test "build image" {
|
||||||
|
cd $BATS_TEST_DIRNAME/..
|
||||||
|
docker_build -t $SUT_IMAGE .
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "plugins are installed with plugins.sh" {
|
||||||
|
run docker build -t $SUT_IMAGE-plugins $BATS_TEST_DIRNAME/plugins
|
||||||
|
assert_success
|
||||||
|
# replace DOS line endings \r\n
|
||||||
|
run bash -c "docker run --rm $SUT_IMAGE-plugins ls --color=never -1 /var/jenkins_home/plugins | tr -d '\r'"
|
||||||
|
assert_success
|
||||||
|
assert_line 'maven-plugin.jpi'
|
||||||
|
assert_line 'maven-plugin.jpi.pinned'
|
||||||
|
assert_line 'ant.jpi'
|
||||||
|
assert_line 'ant.jpi.pinned'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "plugins are installed with install-plugins.sh" {
|
||||||
|
run docker build -t $SUT_IMAGE-install-plugins $BATS_TEST_DIRNAME/install-plugins
|
||||||
|
assert_success
|
||||||
|
refute_line --partial 'Skipping already bundled dependency'
|
||||||
|
# replace DOS line endings \r\n
|
||||||
|
run bash -c "docker run --rm $SUT_IMAGE-install-plugins ls --color=never -1 /var/jenkins_home/plugins | tr -d '\r'"
|
||||||
|
assert_success
|
||||||
|
assert_line 'maven-plugin.jpi'
|
||||||
|
assert_line 'maven-plugin.jpi.pinned'
|
||||||
|
assert_line 'ant.jpi'
|
||||||
|
assert_line 'ant.jpi.pinned'
|
||||||
|
assert_line 'credentials.jpi'
|
||||||
|
assert_line 'credentials.jpi.pinned'
|
||||||
|
assert_line 'mesos.jpi'
|
||||||
|
assert_line 'mesos.jpi.pinned'
|
||||||
|
# optional dependencies
|
||||||
|
refute_line 'metrics.jpi'
|
||||||
|
refute_line 'metrics.jpi.pinned'
|
||||||
|
# plugins bundled but under detached-plugins, so need to be installed
|
||||||
|
assert_line 'javadoc.jpi'
|
||||||
|
assert_line 'javadoc.jpi.pinned'
|
||||||
|
assert_line 'mailer.jpi'
|
||||||
|
assert_line 'mailer.jpi.pinned'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "plugins are installed with install-plugins.sh even when already exist" {
|
||||||
|
run docker build -t $SUT_IMAGE-install-plugins-update --no-cache $BATS_TEST_DIRNAME/install-plugins/update
|
||||||
|
assert_success
|
||||||
|
assert_line "Using provided plugin: ant"
|
||||||
|
refute_line --partial 'Skipping already bundled dependency'
|
||||||
|
# replace DOS line endings \r\n
|
||||||
|
run bash -c "docker run --rm $SUT_IMAGE-install-plugins-update unzip -p /var/jenkins_home/plugins/maven-plugin.jpi META-INF/MANIFEST.MF | tr -d '\r'"
|
||||||
|
assert_success
|
||||||
|
assert_line 'Plugin-Version: 2.13'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "plugins are getting upgraded but not downgraded" {
|
||||||
|
# Initial execution
|
||||||
|
run docker build -t $SUT_IMAGE-install-plugins $BATS_TEST_DIRNAME/install-plugins
|
||||||
|
assert_success
|
||||||
|
local work; work="$BATS_TEST_DIRNAME/upgrade-plugins/work"
|
||||||
|
mkdir -p $work
|
||||||
|
# Image contains maven-plugin 2.7.1 and ant-plugin 1.3
|
||||||
|
run bash -c "docker run -u $UID -v $work:/var/jenkins_home --rm $SUT_IMAGE-install-plugins true"
|
||||||
|
assert_success
|
||||||
|
run unzip_manifest maven-plugin.jpi $work
|
||||||
|
assert_line 'Plugin-Version: 2.7.1'
|
||||||
|
run unzip_manifest ant.jpi $work
|
||||||
|
assert_line 'Plugin-Version: 1.3'
|
||||||
|
|
||||||
|
# Upgrade to new image with different plugins
|
||||||
|
run docker build -t $SUT_IMAGE-upgrade-plugins $BATS_TEST_DIRNAME/upgrade-plugins
|
||||||
|
assert_success
|
||||||
|
# Images contains maven-plugin 2.13 and ant-plugin 1.2
|
||||||
|
run bash -c "docker run -u $UID -v $work:/var/jenkins_home --rm $SUT_IMAGE-upgrade-plugins true"
|
||||||
|
assert_success
|
||||||
|
run unzip_manifest maven-plugin.jpi $work
|
||||||
|
assert_success
|
||||||
|
# Should be updated
|
||||||
|
assert_line 'Plugin-Version: 2.13'
|
||||||
|
run unzip_manifest ant.jpi $work
|
||||||
|
# 1.2 is older than the existing 1.3, so keep 1.3
|
||||||
|
assert_line 'Plugin-Version: 1.3'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "clean work directory" {
|
||||||
|
run bash -c "rm -rf $BATS_TEST_DIRNAME/upgrade-plugins/work"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "do not upgrade if plugin has been manually updated" {
|
||||||
|
run docker build -t $SUT_IMAGE-install-plugins $BATS_TEST_DIRNAME/install-plugins
|
||||||
|
assert_success
|
||||||
|
local work; work="$BATS_TEST_DIRNAME/upgrade-plugins/work"
|
||||||
|
mkdir -p $work
|
||||||
|
# Image contains maven-plugin 2.7.1 and ant-plugin 1.3
|
||||||
|
run bash -c "docker run -u $UID -v $work:/var/jenkins_home --rm $SUT_IMAGE-install-plugins curl --connect-timeout 20 --retry 5 --retry-delay 0 --retry-max-time 60 -s -f -L https://updates.jenkins.io/download/plugins/maven-plugin/2.12.1/maven-plugin.hpi -o /var/jenkins_home/plugins/maven-plugin.jpi"
|
||||||
|
assert_success
|
||||||
|
run unzip_manifest maven-plugin.jpi $work
|
||||||
|
assert_line 'Plugin-Version: 2.12.1'
|
||||||
|
run docker build -t $SUT_IMAGE-upgrade-plugins $BATS_TEST_DIRNAME/upgrade-plugins
|
||||||
|
assert_success
|
||||||
|
# Images contains maven-plugin 2.13 and ant-plugin 1.2
|
||||||
|
run bash -c "docker run -u $UID -v $work:/var/jenkins_home --rm $SUT_IMAGE-upgrade-plugins true"
|
||||||
|
assert_success
|
||||||
|
run unzip_manifest maven-plugin.jpi $work
|
||||||
|
assert_success
|
||||||
|
# Shouldn't be updated
|
||||||
|
refute_line 'Plugin-Version: 2.13'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "clean work directory" {
|
||||||
|
run bash -c "rm -rf $BATS_TEST_DIRNAME/upgrade-plugins/work"
|
||||||
|
}
|
3
jenkins/tests/install-plugins/Dockerfile
Normal file
3
jenkins/tests/install-plugins/Dockerfile
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
FROM bats-jenkins
|
||||||
|
|
||||||
|
RUN /usr/local/bin/install-plugins.sh maven-plugin:2.7.1 ant:1.3 mesos:0.13.0
|
3
jenkins/tests/install-plugins/update/Dockerfile
Normal file
3
jenkins/tests/install-plugins/update/Dockerfile
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
FROM bats-jenkins-install-plugins
|
||||||
|
|
||||||
|
RUN /usr/local/bin/install-plugins.sh maven-plugin:2.13 ant:1.3
|
4
jenkins/tests/plugins/Dockerfile
Normal file
4
jenkins/tests/plugins/Dockerfile
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
FROM bats-jenkins
|
||||||
|
|
||||||
|
COPY plugins.txt /usr/share/jenkins/ref/
|
||||||
|
RUN /usr/local/bin/plugins.sh /usr/share/jenkins/ref/plugins.txt
|
2
jenkins/tests/plugins/plugins.txt
Normal file
2
jenkins/tests/plugins/plugins.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
maven-plugin:2.7.1
|
||||||
|
ant:1.3
|
56
jenkins/tests/runtime.bats
Normal file
56
jenkins/tests/runtime.bats
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
SUT_IMAGE=bats-jenkins
|
||||||
|
SUT_CONTAINER=bats-jenkins
|
||||||
|
|
||||||
|
load 'test_helper/bats-support/load'
|
||||||
|
load 'test_helper/bats-assert/load'
|
||||||
|
load test_helpers
|
||||||
|
|
||||||
|
@test "build image" {
|
||||||
|
cd $BATS_TEST_DIRNAME/..
|
||||||
|
docker_build -t $SUT_IMAGE .
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "clean test containers" {
|
||||||
|
cleanup $SUT_CONTAINER
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "test multiple JENKINS_OPTS" {
|
||||||
|
# running --help --version should return the version, not the help
|
||||||
|
local version=$(grep 'ENV JENKINS_VERSION' Dockerfile | sed -e 's/.*:-\(.*\)}/\1/')
|
||||||
|
# need the last line of output
|
||||||
|
assert "${version}" docker run --rm -e JENKINS_OPTS="--help --version" --name $SUT_CONTAINER -P $SUT_IMAGE | tail -n 1
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "test jenkins arguments" {
|
||||||
|
# running --help --version should return the version, not the help
|
||||||
|
local version=$(grep 'ENV JENKINS_VERSION' Dockerfile | sed -e 's/.*:-\(.*\)}/\1/')
|
||||||
|
# need the last line of output
|
||||||
|
assert "${version}" docker run --rm --name $SUT_CONTAINER -P $SUT_IMAGE --help --version | tail -n 1
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "create test container" {
|
||||||
|
docker run -d -e JAVA_OPTS="-Duser.timezone=Europe/Madrid -Dhudson.model.DirectoryBrowserSupport.CSP=\"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';\"" --name $SUT_CONTAINER -P $SUT_IMAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "test container is running" {
|
||||||
|
sleep 1 # give time to eventually fail to initialize
|
||||||
|
retry 3 1 assert "true" docker inspect -f {{.State.Running}} $SUT_CONTAINER
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Jenkins is initialized" {
|
||||||
|
retry 30 5 test_url /api/json
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "JAVA_OPTS are set" {
|
||||||
|
local sed_expr='s/<wbr>//g;s/<td class="pane">.*<\/td><td class.*normal">//g;s/<t.>//g;s/<\/t.>//g'
|
||||||
|
assert 'default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';' \
|
||||||
|
bash -c "curl -fsSL --user \"admin:$(get_jenkins_password)\" $(get_jenkins_url)/systemInfo | sed 's/<\/tr>/<\/tr>\'$'\n/g' | grep '<td class=\"pane\">hudson.model.DirectoryBrowserSupport.CSP</td>' | sed -e '${sed_expr}'"
|
||||||
|
assert 'Europe/Madrid' \
|
||||||
|
bash -c "curl -fsSL --user \"admin:$(get_jenkins_password)\" $(get_jenkins_url)/systemInfo | sed 's/<\/tr>/<\/tr>\'$'\n/g' | grep '<td class=\"pane\">user.timezone</td>' | sed -e '${sed_expr}'"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "clean test containers" {
|
||||||
|
cleanup $SUT_CONTAINER
|
||||||
|
}
|
84
jenkins/tests/test_helpers.bash
Normal file
84
jenkins/tests/test_helpers.bash
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# check dependencies
|
||||||
|
(
|
||||||
|
type docker &>/dev/null || ( echo "docker is not available"; exit 1 )
|
||||||
|
type curl &>/dev/null || ( echo "curl is not available"; exit 1 )
|
||||||
|
)>&2
|
||||||
|
|
||||||
|
# Assert that $1 is the outputof a command $2
|
||||||
|
function assert {
|
||||||
|
local expected_output=$1
|
||||||
|
shift
|
||||||
|
local actual_output
|
||||||
|
actual_output=$("$@")
|
||||||
|
actual_output="${actual_output//[$'\t\r\n']}" # remove newlines
|
||||||
|
if ! [ "$actual_output" = "$expected_output" ]; then
|
||||||
|
echo "expected: \"$expected_output\""
|
||||||
|
echo "actual: \"$actual_output\""
|
||||||
|
false
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Retry a command $1 times until it succeeds. Wait $2 seconds between retries.
|
||||||
|
function retry {
|
||||||
|
local attempts=$1
|
||||||
|
shift
|
||||||
|
local delay=$1
|
||||||
|
shift
|
||||||
|
local i
|
||||||
|
|
||||||
|
for ((i=0; i < attempts; i++)); do
|
||||||
|
run "$@"
|
||||||
|
if [ "$status" -eq 0 ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep $delay
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Command \"$*\" failed $attempts times. Status: $status. Output: $output" >&2
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
function docker_build {
|
||||||
|
if [ -n "$JENKINS_VERSION" ]; then
|
||||||
|
docker build --build-arg JENKINS_VERSION=$JENKINS_VERSION --build-arg JENKINS_SHA=$JENKINS_SHA "$@"
|
||||||
|
else
|
||||||
|
docker build "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_jenkins_url {
|
||||||
|
if [ -z "${DOCKER_HOST}" ]; then
|
||||||
|
DOCKER_IP=localhost
|
||||||
|
else
|
||||||
|
DOCKER_IP=$(echo "$DOCKER_HOST" | sed -e 's|tcp://\(.*\):[0-9]*|\1|')
|
||||||
|
fi
|
||||||
|
echo "http://$DOCKER_IP:$(docker port "$SUT_CONTAINER" 8080 | cut -d: -f2)"
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_jenkins_password {
|
||||||
|
docker logs "$SUT_CONTAINER" 2>&1 | grep -A 2 "Please use the following password to proceed to installation" | tail -n 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_url {
|
||||||
|
run curl --user "admin:$(get_jenkins_password)" --output /dev/null --silent --head --fail --connect-timeout 30 --max-time 60 "$(get_jenkins_url)$1"
|
||||||
|
if [ "$status" -eq 0 ]; then
|
||||||
|
true
|
||||||
|
else
|
||||||
|
echo "URL $(get_jenkins_url)$1 failed" >&2
|
||||||
|
echo "output: $output" >&2
|
||||||
|
false
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanup {
|
||||||
|
docker kill "$1" &>/dev/null ||:
|
||||||
|
docker rm -fv "$1" &>/dev/null ||:
|
||||||
|
}
|
||||||
|
|
||||||
|
function unzip_manifest {
|
||||||
|
local plugin=$1
|
||||||
|
local work=$2
|
||||||
|
bash -c "docker run --rm -v $work:/var/jenkins_home --entrypoint unzip $SUT_IMAGE -p /var/jenkins_home/plugins/$plugin META-INF/MANIFEST.MF | tr -d '\r'"
|
||||||
|
}
|
3
jenkins/tests/upgrade-plugins/Dockerfile
Normal file
3
jenkins/tests/upgrade-plugins/Dockerfile
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
FROM bats-jenkins
|
||||||
|
|
||||||
|
RUN /usr/local/bin/install-plugins.sh maven-plugin:2.13 ant:1.2
|
36
jenkins/update-official-library.sh
Normal file
36
jenkins/update-official-library.sh
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#!/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
|
Loading…
Reference in New Issue
Block a user