JJB Verify
----------
-Runs `jenkins-jobs test` to validate JJB syntax
+Runs `jenkins-jobs test` to validate JJB syntax. Optionally validates
+build-node labels used in templates and job definitions.
:Template Names:
- {project-name}-jjb-verify
:build-concurrent: Whether or not to allow this job to run multiple jobs
simultaneously. (default: true)
:build-days-to-keep: Days to keep build logs in Jenkins. (default: 7)
+ :build-node-label-check: Whether to check build-node labels in jobs
+ against node names in cloud config files (default: false)
+ :build-node-label-list: Space-separated list of external build-node
+ labels not present in cloud config files (default: "")
:build-timeout: Timeout in minutes before aborting build. (default: 10)
:git-url: URL clone project from. (default: $GIT_URL/$PROJECT)
:jjb-cache: JJB cache location. (default: $HOME/.cache/jenkins_jobs)
######################
build-concurrent: true
- check-build-node-labels: false
- external-build-node-labels: ""
+ build-node-label-check: false
+ build-node-label-list: ""
gerrit_verify_triggers:
- patchset-created-event:
- ../shell/jjb-verify-job.sh
- conditional-step:
condition-kind: boolean-expression
- condition-expression: "{check-build-node-labels}"
+ condition-expression: "{build-node-label-check}"
on-evaluation-failure: dont-run
steps:
- inject:
- properties-content: EXTERNAL_LABELS="{external-build-node-labels}"
+ properties-content: EXTERNAL_LABELS="{build-node-label-list}"
- shell: !include-raw-escape:
- ../shell/jjb-verify-build-nodes.sh
- lf-infra-gpg-verify-git-signature
# Prereqs:
# - Bash version 3+
# - Python tool yq is installed; e.g., by python-tools-install.sh
-# - Working directory is a ci-management repo with subdirs
-# jenkins-config/clouds/openstack and jjb
+# - Working directory is a ci-management repo with subdirs named below
# Environment variable:
# - EXTERNAL_LABELS - a space-separated list of build-node labels
# for nodes not managed in the jenkins-config area (optional)
set -eu -o pipefail
+# expected suffix on build-node config files
+suffix=".cfg"
+# subdir with cloud config files
+configdir="jenkins-config/clouds/openstack"
+# subdir with JJB yaml files
+jjbdir="jjb"
+
+# function to test if the argument is empty,
+# is two double quotes, or has unwanted suffix
+isBadLabel () {
+ local label="$1"
+ [[ -z "$label" ]] || [[ $label = "\"\"" ]] || [[ $label = *"$suffix" ]]
+}
+
# function to search an array for a value
# $1 is value
# $2 is array, passed via ${array[@]}
return 1
}
-# discover build node labels
+# check prereqs
+if [ ! -d "$configdir" ] || [ ! -d "$jjbdir" ]; then
+ echo "ERROR: failed to find subdirs $configdir and $jjbdir"
+ exit 1
+fi
+
+# find cloud config node names by recursive descent
declare -a labels=()
-suffix=".cfg"
while IFS= read -r ; do
file="$REPLY"
# valid files contain IMAGE_NAME; skip the cloud config file
if grep -q "IMAGE_NAME" "$file" && ! grep -q "CLOUD_CREDENTIAL_ID" "$file"; then
- # file name is a valid label, without path prefix and suffix
+ # file name without prefix or suffix is a valid label
name=$(basename -s "$suffix" "$file")
+ echo "INFO: add label $name for $file"
labels+=("$name")
- # a file can define custom labels
+ # add custom labels from file
if custom=$(grep "LABELS=" "$file" | cut -d= -f2); then
# TODO: confirm separator for multiple labels
read -r -a customarray <<< "$custom"
- for c in "${customarray[@]}"; do
- if ! isValueInArray "$c" "${labels[@]}"; then
- labels+=("$c")
+ for l in "${customarray[@]}"; do
+ if isBadLabel "$l"; then
+ echo "WARN: skip custom label $l from $file"
+ elif isValueInArray "$l" "${labels[@]}"; then
+ echo "INFO: skip repeat custom label $l from $file"
+ else
+ echo "INFO: add custom label $l from $file"
+ labels+=("$l")
fi
done
fi
fi
-done < <(find "jenkins-config/clouds/openstack" -name \*$suffix)
-echo "Found ${#labels[@]} configured label(s):"
-echo "${labels[@]}"
-declare -a externals=()
+done < <(find "$configdir" -name \*$suffix)
+
+# add external build-node labels
if [[ -n ${EXTERNAL_LABELS:-} ]]; then
read -r -a externals <<< "$EXTERNAL_LABELS"
- echo "Received ${#externals[@]} external label(s):"
- echo "${externals[@]}"
- labels=("${externals[@]}" "${labels[@]}")
+ # defend against empty, quotes-only and repeated values
+ for l in "${externals[@]}"; do
+ if isBadLabel "$l"; then
+ echo "WARN: skip external label $l"
+ elif isValueInArray "$l" "${labels[@]}"; then
+ echo "INFO: skip repeat label $l from environment"
+ else
+ echo "INFO: add label $l from environment"
+ labels+=("$l")
+ fi
+ done
fi
-# check build node label uses
+echo "INFO: label list has ${#labels[@]} entries:"
+echo "INFO:" "${labels[@]}"
+
+# check build-node label uses
+count=0
errs=0
while IFS= read -r ; do
file="$REPLY"
- echo "Checking $file"
- # this includes job-templates which may be annoying
+ echo "INFO: checking $file"
+ # includes job-template AND project entries
nodes=$(yq 'recurse | ."build-node"? | values' "$file" | sort -u | tr -d '"')
+ # nodes may be a yaml list; e.g., '[ foo, bar, baz ]'
for node in $nodes; do
- # may be a yaml list; e.g., '[ foo, bar, baz ]'
node="${node//[\[\],]/}"
if [[ -n $node ]] && ! isValueInArray "$node" "${labels[@]}"; then
- echo "ERROR: file $file uses unknown build-node $node"
+ echo "ERROR: unknown build-node $node in $file"
errs=$((errs+1))
+ else
+ count=$((count+1))
fi
done
-done < <(find "jjb" -name '*.yaml')
+done < <(find "$jjbdir" -name '*.yaml')
+echo "INFO: $count valid label(s), $errs invalid label(s)"
echo "---> jjb-verify-build-nodes.sh ends"
exit $errs