[Fuego] [PATCH] docker: load plugins using hardcoded versions

Tim.Bird at sony.com Tim.Bird at sony.com
Tue Apr 10 00:34:59 UTC 2018


Use install-plugins.sh (copied from github jenkinsci/docker project)
to load the plugins for Jenkins using hardcoded versions.

This resolves issues with using the update center with the latest
versions of packages, which yields lots of dependency errors and
makes the description-setter plugin not work (among others).

For this release, use the same Jenkins war version and plugin
versions as the 1.2.1 release (from November, 2017).

Signed-off-by: Tim Bird <tim.bird at sony.com>
---
 Dockerfile                          |  46 +++++--
 frontend-install/install-plugins.sh | 266 ++++++++++++++++++++++++++++++++++++
 frontend-install/jenkins-support    | 134 ++++++++++++++++++
 3 files changed, 436 insertions(+), 10 deletions(-)
 create mode 100755 frontend-install/install-plugins.sh
 create mode 100755 frontend-install/jenkins-support

diff --git a/Dockerfile b/Dockerfile
index 2c0b17e..cc995d6 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -95,15 +95,41 @@ RUN source /etc/default/jenkins && \
 
 COPY frontend-install/plugins/flot-plotter-plugin/flot.hpi /tmp
 
+COPY frontend-install/install-plugins.sh frontend-install/jenkins-support /usr/local/bin/
+
+
+# install flot.hpi manually from local file
 RUN service jenkins start && \
 	sleep 30 && \
-	sudo -u jenkins java -jar /var/cache/jenkins/war/WEB-INF/jenkins-cli.jar -s http://localhost:8080/fuego install-plugin description-setter && \
-	sudo -u jenkins java -jar /var/cache/jenkins/war/WEB-INF/jenkins-cli.jar -s http://localhost:8080/fuego install-plugin pegdown-formatter && \
     sudo -u jenkins java -jar /var/cache/jenkins/war/WEB-INF/jenkins-cli.jar -s http://localhost:8080/fuego install-plugin /tmp/flot.hpi && \
-    sleep 10
-
-# Let Jenkins install flot before making the symlink
-RUN service jenkins restart && sleep 30 && \
+    sleep 10 && \
+    service jenkins stop
+
+# install other plugins from Jenkins update center
+# NOTE: not sure all of these are needed, but keep list
+# compatible with 1.2.1 release for now
+RUN /usr/local/bin/install-plugins.sh ant:1.7 \
+    bouncycastle-api:2.16.2 \
+    description-setter:1.10 \
+    display-url-api:2.1.0 \
+    external-monitor-job:1.7 \
+    greenballs:1.15 \
+    icon-shim:2.0.3 \
+    javadoc:1.4 \
+    junit:1.21 \
+    ldap:1.17 \
+    mailer:1.20 \
+    matrix-auth:1.7 \
+    matrix-project:1.12 \
+    antisamy-markup-formatter:1.5 \
+    pam-auth:1.3 \
+    pegdown-formatter:1.3 \
+    script-security:1.35 \
+    structs:1.10 \
+    windows-slaves:1.3.1
+
+# make the mod.js symlink well after flot is installed
+RUN service jenkins start && sleep 30 && \
     rm $JENKINS_HOME/plugins/flot/flot/mod.js && \
     ln -s /fuego-core/engine/scripts/mod.js $JENKINS_HOME/plugins/flot/flot/mod.js
 
@@ -127,14 +153,14 @@ RUN ln -s /fuego-ro/scripts/fuego-lava-target-teardown /usr/local/bin
 #RUN echo "fuego-create-node --board raspberrypi3" >> /root/firststart.sh
 #RUN echo "fuego-create-jobs --board raspberrypi3 --testplan testplan_docker --distrib nosyslogd.dist" >> /root/firststart.sh
 
-RUN echo "deb http://emdebian.org/tools/debian/ jessie main" > /etc/apt/sources.list.d/crosstools.list
-RUN dpkg --add-architecture armhf
-RUN curl http://emdebian.org/tools/debian/emdebian-toolchain-archive.key | sudo apt-key add -
-RUN DEBIAN_FRONTEND=noninteractive apt-get update
 # TRB-2018-03-19 - don't automatically install emdebian armhf toolchains
 # These are old, and have conflicts with recent Debian package releases.
 # Also, users should be encouraged to install the correct toolchain for
 # their board.
+#RUN echo "deb http://emdebian.org/tools/debian/ jessie main" > /etc/apt/sources.list.d/crosstools.list
+#RUN dpkg --add-architecture armhf
+#RUN curl http://emdebian.org/tools/debian/emdebian-toolchain-archive.key | sudo apt-key add -
+#RUN DEBIAN_FRONTEND=noninteractive apt-get update
 #RUN DEBIAN_FRONTEND=noninteractive apt-get -yV install crossbuild-essential-armhf cpp-arm-linux-gnueabihf gcc-arm-linux-gnueabihf binutils-arm-linux-gnueabihf
 
 # ==============================================================================
diff --git a/frontend-install/install-plugins.sh b/frontend-install/install-plugins.sh
new file mode 100755
index 0000000..2b5d78c
--- /dev/null
+++ b/frontend-install/install-plugins.sh
@@ -0,0 +1,266 @@
+#!/bin/bash -eu
+# SPDX-License-Identifier: MIT
+#
+# Copyright (c) 2014-, Michael Neale, Nicolas de Loof, Carlos Sanchez, and a number of other contributors
+#
+# Copied from: https://github.com/jenkinsci/docker
+#
+# Download plugins given on the command line
+#
+# FROM jenkins
+# COPY install-plugins.sh /usr/local/bin/
+# RUN install-plugins.sh greenballs description-setter:1.10
+
+JENKINS_UC=https://updates.jenkins.io
+
+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
+
+        # TRB - don't resolve deps - only load exactly what is specified
+        #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"
+    chown jenkins.jenkins "$jpi"
+    cp -p "$jpi" /var/lib/jenkins/plugins
+    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 -e "${bundledPlugins}\n${installedPlugins}" | 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 installed 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 || [ "$line" != "" ]; do
+            # Remove leading/trailing spaces, comments, and empty lines
+            plugin=$(echo "${line}" | tr -d '\r' | sed -e 's/^[ \t]*//g' -e 's/[ \t]*$//g' -e 's/[ \t]*#.*$//g' -e '/^[ \t]*$/d')
+
+            # Avoid adding empty plugin into array
+            if [ ${#plugin} -ne 0 ]; then
+                plugins+=("${plugin}")
+            fi
+        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)"
+
+    echo "Registering preinstalled plugins..."
+    installedPlugins="$(installedPlugins)"
+
+    # 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 "$@"
diff --git a/frontend-install/jenkins-support b/frontend-install/jenkins-support
new file mode 100755
index 0000000..9ce7937
--- /dev/null
+++ b/frontend-install/jenkins-support
@@ -0,0 +1,134 @@
+#!/bin/bash -eu
+# SPDX-License-Identifier: MIT
+#
+# Copyright (c) 2014-, Michael Neale, Nicolas de Loof, Carlose Sanchez, and a number of other contributors
+#
+# Copied from: https://github.com/jenkinsci/docker
+#
+# utility functions used by install-plugins.sh
+#
+# 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 -pr "${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 -pr "${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
+}
-- 
2.1.4



More information about the Fuego mailing list