|
| 1 | +#! /bin/bash -eu |
| 2 | +# |
| 3 | +# Originally copied from https://github.com/jenkinsci/docker |
| 4 | +# You can set JENKINS_UC to change the default URL to Jenkins update center |
| 5 | +# |
| 6 | +# Usage: |
| 7 | +# |
| 8 | +# FROM openshift/jenkins-2-centos7 |
| 9 | +# COPY plugins.txt /plugins.txt |
| 10 | +# RUN /usr/local/bin/install-plugins.sh /plugins.txt |
| 11 | +# |
| 12 | +# The format of 'plugins.txt. is: |
| 13 | +# |
| 14 | +# pluginId:pluginVersion |
| 15 | + |
| 16 | +set -o pipefail |
| 17 | + |
| 18 | +# BEGIN - From https://raw.githubusercontent.com/jenkinsci/docker/master/jenkins-support |
| 19 | +# compare if version1 < version2 |
| 20 | +versionLT() { |
| 21 | + local v1; v1=$(echo "$1" | cut -d '-' -f 1 ) |
| 22 | + local q1; q1=$(echo "$1" | cut -s -d '-' -f 2- ) |
| 23 | + local v2; v2=$(echo "$2" | cut -d '-' -f 1 ) |
| 24 | + local q2; q2=$(echo "$2" | cut -s -d '-' -f 2- ) |
| 25 | + if [ "$v1" = "$v2" ]; then |
| 26 | + if [ "$q1" = "$q2" ]; then |
| 27 | + return 1 |
| 28 | + else |
| 29 | + if [ -z "$q1" ]; then |
| 30 | + return 1 |
| 31 | + else |
| 32 | + if [ -z "$q2" ]; then |
| 33 | + return 0 |
| 34 | + else |
| 35 | + [ "$q1" = "$(echo -e "$q1\n$q2" | sort -V | head -n1)" ] |
| 36 | + fi |
| 37 | + fi |
| 38 | + fi |
| 39 | + else |
| 40 | + [ "$v1" = "$(echo -e "$v1\n$v2" | sort -V | head -n1)" ] |
| 41 | + fi |
| 42 | +} |
| 43 | + |
| 44 | +# returns a plugin version from a plugin archive |
| 45 | +get_plugin_version() { |
| 46 | + local archive; archive=$1 |
| 47 | + local version; version=$(unzip -p "$archive" META-INF/MANIFEST.MF | grep "^Plugin-Version: " | sed -e 's#^Plugin-Version: ##') |
| 48 | + version=${version%%[[:space:]]} |
| 49 | + echo "$version" |
| 50 | +} |
| 51 | + |
| 52 | +# Copy files from /usr/share/jenkins/ref into $JENKINS_HOME |
| 53 | +# So the initial JENKINS-HOME is set with expected content. |
| 54 | +# Don't override, as this is just a reference setup, and use from UI |
| 55 | +# can then change this, upgrade plugins, etc. |
| 56 | +copy_reference_file() { |
| 57 | + f="${1%/}" |
| 58 | + b="${f%.override}" |
| 59 | + rel="${b:23}" |
| 60 | + version_marker="${rel}.version_from_image" |
| 61 | + dir=$(dirname "${b}") |
| 62 | + local action; |
| 63 | + local reason; |
| 64 | + local container_version; |
| 65 | + local image_version; |
| 66 | + local marker_version; |
| 67 | + local log; log=false |
| 68 | + if [[ ${rel} == plugins/*.jpi ]]; then |
| 69 | + container_version=$(get_plugin_version "$JENKINS_HOME/${rel}") |
| 70 | + image_version=$(get_plugin_version "${f}") |
| 71 | + if [[ -e $JENKINS_HOME/${version_marker} ]]; then |
| 72 | + marker_version=$(cat "$JENKINS_HOME/${version_marker}") |
| 73 | + if versionLT "$marker_version" "$container_version"; then |
| 74 | + action="SKIPPED" |
| 75 | + reason="Installed version ($container_version) has been manually upgraded from initial version ($marker_version)" |
| 76 | + log=true |
| 77 | + else |
| 78 | + if [[ "$image_version" == "$container_version" ]]; then |
| 79 | + action="SKIPPED" |
| 80 | + reason="Version from image is the same as the installed version $image_version" |
| 81 | + else |
| 82 | + if versionLT "$image_version" "$container_version"; then |
| 83 | + action="SKIPPED" |
| 84 | + log=true |
| 85 | + reason="Image version ($image_version) is older than installed version ($container_version)" |
| 86 | + else |
| 87 | + action="UPGRADED" |
| 88 | + log=true |
| 89 | + reason="Image version ($image_version) is newer than installed version ($container_version)" |
| 90 | + fi |
| 91 | + fi |
| 92 | + fi |
| 93 | + else |
| 94 | + if [[ -n "$TRY_UPGRADE_IF_NO_MARKER" ]]; then |
| 95 | + if [[ "$image_version" == "$container_version" ]]; then |
| 96 | + action="SKIPPED" |
| 97 | + reason="Version from image is the same as the installed version $image_version (no marker found)" |
| 98 | + # Add marker for next time |
| 99 | + echo "$image_version" > "$JENKINS_HOME/${version_marker}" |
| 100 | + else |
| 101 | + if versionLT "$image_version" "$container_version"; then |
| 102 | + action="SKIPPED" |
| 103 | + log=true |
| 104 | + reason="Image version ($image_version) is older than installed version ($container_version) (no marker found)" |
| 105 | + else |
| 106 | + action="UPGRADED" |
| 107 | + log=true |
| 108 | + reason="Image version ($image_version) is newer than installed version ($container_version) (no marker found)" |
| 109 | + fi |
| 110 | + fi |
| 111 | + fi |
| 112 | + fi |
| 113 | + if [[ ! -e $JENKINS_HOME/${rel} || "$action" == "UPGRADED" || $f = *.override ]]; then |
| 114 | + action=${action:-"INSTALLED"} |
| 115 | + log=true |
| 116 | + mkdir -p "$JENKINS_HOME/${dir:23}" |
| 117 | + cp -r "${f}" "$JENKINS_HOME/${rel}"; |
| 118 | + # pin plugins on initial copy |
| 119 | + touch "$JENKINS_HOME/${rel}.pinned" |
| 120 | + echo "$image_version" > "$JENKINS_HOME/${version_marker}" |
| 121 | + reason=${reason:-$image_version} |
| 122 | + else |
| 123 | + action=${action:-"SKIPPED"} |
| 124 | + fi |
| 125 | + else |
| 126 | + if [[ ! -e $JENKINS_HOME/${rel} || $f = *.override ]] |
| 127 | + then |
| 128 | + action="INSTALLED" |
| 129 | + log=true |
| 130 | + mkdir -p "$JENKINS_HOME/${dir:23}" |
| 131 | + cp -r "${f}" "$JENKINS_HOME/${rel}"; |
| 132 | + else |
| 133 | + action="SKIPPED" |
| 134 | + fi |
| 135 | + fi |
| 136 | + if [[ -n "$VERBOSE" || "$log" == "true" ]]; then |
| 137 | + if [ -z "$reason" ]; then |
| 138 | + echo "$action $rel" >> "$COPY_REFERENCE_FILE_LOG" |
| 139 | + else |
| 140 | + echo "$action $rel : $reason" >> "$COPY_REFERENCE_FILE_LOG" |
| 141 | + fi |
| 142 | + fi |
| 143 | +} |
| 144 | +# END - From https://raw.githubusercontent.com/jenkinsci/docker/master/jenkins-support |
| 145 | + |
| 146 | +REF_DIR=${REF:-/opt/openshift/plugins} |
| 147 | +FAILED="$REF_DIR/failed-plugins.txt" |
| 148 | + |
| 149 | +function getLockFile() { |
| 150 | + echo -n "$REF_DIR/${1}.lock" |
| 151 | +} |
| 152 | + |
| 153 | +function getArchiveFilename() { |
| 154 | + echo -n "$REF_DIR/${1}.jpi" |
| 155 | +} |
| 156 | + |
| 157 | +function download() { |
| 158 | + local plugin originalPlugin version lock ignoreLockFile |
| 159 | + plugin="$1" |
| 160 | + version="${2:-latest}" |
| 161 | + ignoreLockFile="${3:-}" |
| 162 | + lock="$(getLockFile "$plugin")" |
| 163 | + |
| 164 | + if [[ $ignoreLockFile ]] || mkdir "$lock" &>/dev/null; then |
| 165 | + if ! doDownload "$plugin" "$version"; then |
| 166 | + # some plugin don't follow the rules about artifact ID |
| 167 | + # typically: docker-plugin |
| 168 | + originalPlugin="$plugin" |
| 169 | + plugin="${plugin}-plugin" |
| 170 | + if ! doDownload "$plugin" "$version"; then |
| 171 | + echo "Failed to download plugin: $originalPlugin or $plugin" >&2 |
| 172 | + echo "Not downloaded: ${originalPlugin}" >> "$FAILED" |
| 173 | + return 1 |
| 174 | + fi |
| 175 | + fi |
| 176 | + |
| 177 | + if ! checkIntegrity "$plugin"; then |
| 178 | + echo "Downloaded file is not a valid ZIP: $(getArchiveFilename "$plugin")" >&2 |
| 179 | + echo "Download integrity: ${plugin}" >> "$FAILED" |
| 180 | + return 1 |
| 181 | + fi |
| 182 | + |
| 183 | + resolveDependencies "$plugin" |
| 184 | + fi |
| 185 | +} |
| 186 | + |
| 187 | +function doDownload() { |
| 188 | + local plugin version url jpi |
| 189 | + plugin="$1" |
| 190 | + version="$2" |
| 191 | + jpi="$(getArchiveFilename "$plugin")" |
| 192 | + |
| 193 | + # If plugin already exists and is the same version do not download |
| 194 | + if test -f "$jpi" && unzip -p "$jpi" META-INF/MANIFEST.MF | tr -d '\r' | grep "^Plugin-Version: ${version}$" > /dev/null; then |
| 195 | + echo "Using provided plugin: $plugin" |
| 196 | + return 0 |
| 197 | + fi |
| 198 | + |
| 199 | + JENKINS_UC_DOWNLOAD=${JENKINS_UC_DOWNLOAD:-"$JENKINS_UC/download"} |
| 200 | + |
| 201 | + url="$JENKINS_UC_DOWNLOAD/plugins/$plugin/$version/${plugin}.hpi" |
| 202 | + |
| 203 | + echo "Downloading plugin: $plugin from $url" |
| 204 | + curl --connect-timeout 5 --retry 5 --retry-delay 0 --retry-max-time 60 -s -f -L "$url" -o "$jpi" |
| 205 | + return $? |
| 206 | +} |
| 207 | + |
| 208 | +function checkIntegrity() { |
| 209 | + local plugin jpi |
| 210 | + plugin="$1" |
| 211 | + jpi="$(getArchiveFilename "$plugin")" |
| 212 | + |
| 213 | + zip -T "$jpi" >/dev/null |
| 214 | + return $? |
| 215 | +} |
| 216 | + |
| 217 | +function resolveDependencies() { |
| 218 | + local plugin jpi dependencies |
| 219 | + plugin="$1" |
| 220 | + jpi="$(getArchiveFilename "$plugin")" |
| 221 | + |
| 222 | + set +o pipefail |
| 223 | + 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: ##')" |
| 224 | + set -o pipefail |
| 225 | + |
| 226 | + if [[ ! $dependencies ]]; then |
| 227 | + echo " > $plugin has no dependencies" |
| 228 | + return |
| 229 | + fi |
| 230 | + |
| 231 | + echo " > $plugin depends on $dependencies" |
| 232 | + |
| 233 | + IFS=',' read -a array <<< "$dependencies" |
| 234 | + |
| 235 | + for d in "${array[@]}" |
| 236 | + do |
| 237 | + plugin="$(cut -d':' -f1 - <<< "$d")" |
| 238 | + # |
| 239 | + # Note, matrix-auth plugin notes cloudbees-folder as optional in the archive, but then failed to |
| 240 | + # load, citing a dependency that is too old, during testing ... so we will download optional dependencies |
| 241 | + # |
| 242 | + local versionFromPluginParam |
| 243 | + if [[ $d == *"resolution:=optional"* ]]; then |
| 244 | + echo "Examining optional dependency $plugin" |
| 245 | + optional_jpi="$(getArchiveFilename "$plugin")" |
| 246 | + if [ ! -f "${optional_jpi}" ]; then |
| 247 | + echo "Optional dependency $plugin not installed already, skipping" |
| 248 | + continue |
| 249 | + fi |
| 250 | + echo "Optional dependency $plugin already installed, need to determine if it is at a sufficient version" |
| 251 | + versionFromPluginParam="$(cut -d';' -f1 - <<< "$d")" |
| 252 | + else |
| 253 | + versionFromPluginParam=$d |
| 254 | + fi |
| 255 | + local pluginInstalled |
| 256 | + local minVersion; minVersion=$(versionFromPlugin "${versionFromPluginParam}") |
| 257 | + |
| 258 | + set +o pipefail |
| 259 | + local filename; filename=$(getArchiveFilename "$plugin") |
| 260 | + local previouslyDownloadedVersion; previouslyDownloadedVersion=$(get_plugin_version $filename) |
| 261 | + set -o pipefail |
| 262 | + |
| 263 | + # if the dependency plugin has yet to be downloaded (hence the var is not set) download |
| 264 | + if [[ -z "${previouslyDownloadedVersion:-}" ]]; then |
| 265 | + echo "Downloading dependency plugin $plugin version $minVersion that has yet to be installed" |
| 266 | + download "$plugin" "$minVersion" |
| 267 | + else |
| 268 | + # get the version of the dependency plugin already downloaded; if not recent enough, download |
| 269 | + # the minimum version required; the "true" parameter is need for "download" to overwrite the existing |
| 270 | + # version of the plugin |
| 271 | + if versionLT "${previouslyDownloadedVersion}" "${minVersion}"; then |
| 272 | + echo "Upgrading previously downloaded plugin $plugin at $previouslyDownloadedVersion to $minVersion" |
| 273 | + download "$plugin" "$minVersion" "true" |
| 274 | + fi |
| 275 | + fi |
| 276 | + done |
| 277 | + wait |
| 278 | +} |
| 279 | + |
| 280 | +function versionFromPlugin() { |
| 281 | + local plugin=$1 |
| 282 | + if [[ $plugin =~ .*:.* ]]; then |
| 283 | + echo "${plugin##*:}" |
| 284 | + else |
| 285 | + echo "latest" |
| 286 | + fi |
| 287 | + |
| 288 | +} |
| 289 | + |
| 290 | +function installedPlugins() { |
| 291 | + for f in "$REF_DIR"/*.jpi; do |
| 292 | + echo "$(basename "$f" | sed -e 's/\.jpi//'):$(get_plugin_version "$f")" |
| 293 | + done |
| 294 | +} |
| 295 | + |
| 296 | +main() { |
| 297 | + local plugin version |
| 298 | + |
| 299 | + mkdir -p "$REF_DIR" || exit 1 |
| 300 | + |
| 301 | + resolveDependencies "openshift-sync" |
| 302 | + |
| 303 | + echo |
| 304 | + echo "Installed plugins:" |
| 305 | + installedPlugins |
| 306 | + |
| 307 | +} |
| 308 | + |
| 309 | +main "$@" |
0 commit comments