Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes required for Configuration As Code (and plugin install) #39

Open
wants to merge 27 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Created by .ignore support plugin (hsz.mobi)
12 changes: 12 additions & 0 deletions jenkins/cac/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM jenkins/jenkins:lts

COPY entrypoint.sh /bin/entrypoint.sh
COPY s3get.py /usr/bin/s3get.py
COPY install-plugins-local.sh /usr/local/bin/install-plugins-local.sh
ENV CASC_JENKINS_CONFIG="/var/jenkins_home/jenkins.yaml"
RUN /usr/local/bin/install-plugins.sh jdk-tool command-launcher script-security configuration-as-code configuration-as-code-support
USER root
RUN mkdir -p /inits/ && chown jenkins:jenkins /inits/ && chmod a+x /usr/local/bin/install-plugins-local.sh
USER $USER

ENTRYPOINT ["/sbin/tini","--","/bin/entrypoint.sh"]
26 changes: 26 additions & 0 deletions jenkins/cac/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/bash -xe

if [[ -z "${SRCTAR}" ]]; then
echo "No source tar provided. Deployment is probably incomplete"
elif [[ "${SRCTAR}" =~ ^s3:.* ]]; then
echo "Downloading ${SRCTAR} from s3"
/usr/bin/s3get.py "${SRCTAR}" | tar -vx -C /
elif [[ "${SRCTAR}" =~ ^https?:.* ]]; then
echo "Downloading ${SRCTAR} from web"
curl "${SRCTAR}" | tar -vx -C /
elif [[ "${SRCTAR}" =~ ^file:.* ]]; then
echo "Extracting ${SRCTAR} from file system"
tar -vxmf `echo "${SRCTAR}" | sed 's:^file\://\?::'` -C /
else
echo "I don't know how to handle: ${SRCTAR}"
fi

if test -d /inits/; then
for e in /inits/*.sh; do
chmod +x "$e" && "$e" || /bin/true
done
fi

export JAVA_OPTS="-Djenkins.install.runSetupWizard=false ${JAVA_OPTS:-}"

exec /usr/local/bin/jenkins.sh
288 changes: 288 additions & 0 deletions jenkins/cac/install-plugins-local.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
#!/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"
}

install() {
local plugin lock
plugin="$1"
lock="$(getLockFile "$plugin")"

echo "$plugin"
echo "$lock"

if mkdir "$lock" &> /dev/null; then
if ! checkIntegrity "$plugin"; then
echo "Downloaded file is not a valid ZIP: $(getArchiveFilename "$plugin")" >&2
echo "Download integrity: ${plugin}" >> "$FAILED"
return 1
fi
fi

resolveDependencies "$plugin"
}

download() {
local plugin originalPlugin version lock ignoreLockFile
plugin="$1"
version="${2:-latest}"
ignoreLockFile="${3:-}"
lock="$(getLockFile "$plugin")"

if [[ $ignoreLockFile ]] || mkdir "$lock" &>/dev/null; then
if ! doDownload "$plugin" "$version"; then
# some plugin don't follow the rules about artifact ID
# typically: docker-plugin
originalPlugin="$plugin"
plugin="${plugin}-plugin"
if ! doDownload "$plugin" "$version"; then
echo "Failed to download plugin: $originalPlugin or $plugin" >&2
echo "Not downloaded: ${originalPlugin}" >> "$FAILED"
return 1
fi
fi

if ! checkIntegrity "$plugin"; then
echo "Downloaded file is not a valid ZIP: $(getArchiveFilename "$plugin")" >&2
echo "Download integrity: ${plugin}" >> "$FAILED"
return 1
fi

resolveDependencies "$plugin"
fi
}

doDownload() {
local plugin version url jpi
plugin="$1"
version="$2"
jpi="$(getArchiveFilename "$plugin")"

# If plugin already exists and is the same version do not download
if test -f "$jpi" && unzip -p "$jpi" META-INF/MANIFEST.MF | tr -d '\r' | grep "^Plugin-Version: ${version}$" > /dev/null; then
echo "Using provided plugin: $plugin"
return 0
fi

if [[ "$version" == "latest" && -n "$JENKINS_UC_LATEST" ]]; then
# If version-specific Update Center is available, which is the case for LTS versions,
# use it to resolve latest versions.
url="$JENKINS_UC_LATEST/latest/${plugin}.hpi"
elif [[ "$version" == "experimental" && -n "$JENKINS_UC_EXPERIMENTAL" ]]; then
# Download from the experimental update center
url="$JENKINS_UC_EXPERIMENTAL/latest/${plugin}.hpi"
elif [[ "$version" == incrementals* ]] ; then
# Download from Incrementals repo: https://jenkins.io/blog/2018/05/15/incremental-deployment/
# Example URL: https://repo.jenkins-ci.org/incrementals/org/jenkins-ci/plugins/workflow/workflow-support/2.19-rc289.d09828a05a74/workflow-support-2.19-rc289.d09828a05a74.hpi
local groupId incrementalsVersion
arrIN=(${version//;/ })
groupId=${arrIN[1]}
incrementalsVersion=${arrIN[2]}
url="${JENKINS_INCREMENTALS_REPO_MIRROR}/$(echo "${groupId}" | tr '.' '/')/${plugin}/${incrementalsVersion}/${plugin}-${incrementalsVersion}.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"
retry_command curl "${CURL_OPTIONS:--sSfL}" --connect-timeout "${CURL_CONNECTION_TIMEOUT:-20}" --retry "${CURL_RETRY:-5}" --retry-delay "${CURL_RETRY_DELAY:-0}" --retry-max-time "${CURL_RETRY_MAX_TIME:-60}" "$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 -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
rm -f "$FAILED"

# 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
local pluginBn pluginAfn
pluginBn="`basename $plugin .hpi`"
pluginAfn="$(getArchiveFilename "$pluginBn")"
cp -v "$plugin" "$pluginAfn"
plugins+=("$pluginBn")
fi
done
else
for plugin in "$@"
do
local pluginBn pluginAfn
pluginBn="`basename $plugin .hpi`"
pluginAfn="$(getArchiveFilename "$pluginBn")"
cp -v "$plugin" "$pluginAfn"
plugins+=("$pluginBn")
done
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 "Install plugins..."
for plugin in "${plugins[@]}"; do
install "$plugin" &
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 "$@"
24 changes: 24 additions & 0 deletions jenkins/cac/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
This version of the container allows a lot loaded overlay from any webserver or S3 bucket.

## HTTP
To download from HTTP simply set the environment variable:

SRCTAR

To the URL of an (uncompressed) tar file, it will be extracted over /

## S3
To download from HTTP simply set the environment variable:

SRCTAR

To the S3 URL of an (uncompressed) tar file, it will be extracted over /. The URL should look like this:
s3://<bucket>/<path>

It will remove all parameters.

It will then grab the the security credentials from:

http://169.254.169.254/latest/meta-data/iam/security-credentials/

You should ensure that the role is setup with S3 read access.
Loading