github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/contrib/cirrus/lib.sh (about) 1 2 3 # Library of common, shared utility functions. This file is intended 4 # to be sourced by other scripts, not called directly. 5 6 # Global details persist here 7 source /etc/environment # not always loaded under all circumstances 8 9 # Under some contexts these values are not set, make sure they are. 10 export USER="$(whoami)" 11 export HOME="$(getent passwd $USER | cut -d : -f 6)" 12 [[ -n "$UID" ]] || export UID=$(getent passwd $USER | cut -d : -f 3) 13 export GID=$(getent passwd $USER | cut -d : -f 4) 14 15 # Essential default paths, many are overridden when executing under Cirrus-CI 16 export GOPATH="${GOPATH:-/var/tmp/go}" 17 if type -P go &> /dev/null 18 then 19 # required for go 1.12+ 20 export GOCACHE="${GOCACHE:-$HOME/.cache/go-build}" 21 # called processes like `make` and other tools need these vars. 22 eval "export $(go env)" 23 24 # Ensure compiled tooling is reachable 25 export PATH="$PATH:$GOPATH/bin" 26 fi 27 CIRRUS_WORKING_DIR="${CIRRUS_WORKING_DIR:-$GOPATH/src/github.com/containers/libpod}" 28 export GOSRC="${GOSRC:-$CIRRUS_WORKING_DIR}" 29 export PATH="$HOME/bin:$GOPATH/bin:/usr/local/bin:$PATH" 30 export LD_LIBRARY_PATH="/usr/local/lib${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}" 31 # Saves typing / in case location ever moves 32 SCRIPT_BASE=${SCRIPT_BASE:-./contrib/cirrus} 33 PACKER_BASE=${PACKER_BASE:-./contrib/cirrus/packer} 34 # Important filepaths 35 SETUP_MARKER_FILEPATH="${SETUP_MARKER_FILEPATH:-/var/tmp/.setup_environment_sh_complete}" 36 AUTHOR_NICKS_FILEPATH="${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/git_authors_to_irc_nicks.csv" 37 BUILDAH_PACKAGES_FILEPATH="./contrib/cirrus/packages.sh" # in buildah repo. 38 39 # Log remote-client system test varlink output here 40 export VARLINK_LOG=/var/tmp/varlink.log 41 42 cd $GOSRC 43 if type -P git &> /dev/null && [[ -d "$GOSRC/.git" ]] 44 then 45 CIRRUS_CHANGE_IN_REPO=${CIRRUS_CHANGE_IN_REPO:-$(git show-ref --hash=8 HEAD || date +%s)} 46 else # pick something unique and obviously not from Cirrus 47 CIRRUS_CHANGE_IN_REPO=${CIRRUS_CHANGE_IN_REPO:-unknown_$(date +%s)} 48 fi 49 50 # Defaults when not running under CI 51 export CI="${CI:-false}" 52 CIRRUS_CI="${CIRRUS_CI:-false}" 53 DEST_BRANCH="${DEST_BRANCH:-v1.9}" 54 CONTINUOUS_INTEGRATION="${CONTINUOUS_INTEGRATION:-false}" 55 CIRRUS_REPO_NAME=${CIRRUS_REPO_NAME:-libpod} 56 CIRRUS_BASE_SHA=${CIRRUS_BASE_SHA:-unknown$(date +%s)} # difficult to reliably discover 57 CIRRUS_BUILD_ID=${CIRRUS_BUILD_ID:-$RANDOM$(date +%s)} # must be short and unique 58 # Vars. for image-building 59 PACKER_VER="1.4.2" 60 # CSV of cache-image names to build (see $PACKER_BASE/libpod_images.json) 61 62 # Base-images rarely change, define them here so they're out of the way. 63 export PACKER_BUILDS="${PACKER_BUILDS:-ubuntu-18,ubuntu-19,fedora-31,fedora-30}" 64 # Manually produced base-image names (see $SCRIPT_BASE/README.md) 65 export UBUNTU_BASE_IMAGE="ubuntu-1910-eoan-v20200211" 66 export PRIOR_UBUNTU_BASE_IMAGE="ubuntu-1804-bionic-v20200218" 67 # Manually produced base-image names (see $SCRIPT_BASE/README.md) 68 export FEDORA_BASE_IMAGE="fedora-cloud-base-31-1-9-1578586410" 69 export PRIOR_FEDORA_BASE_IMAGE="fedora-cloud-base-30-1-2-1578586410" 70 export BUILT_IMAGE_SUFFIX="${BUILT_IMAGE_SUFFIX:--$CIRRUS_REPO_NAME-${CIRRUS_BUILD_ID}}" 71 # IN_PODMAN container image 72 IN_PODMAN_IMAGE="quay.io/libpod/in_podman:$DEST_BRANCH" 73 # Image for uploading releases 74 UPLDREL_IMAGE="quay.io/libpod/upldrel:v1.9" 75 76 # Avoid getting stuck waiting for user input 77 export DEBIAN_FRONTEND="noninteractive" 78 SUDOAPTGET="ooe.sh sudo -E apt-get -qq --yes" 79 SUDOAPTADD="ooe.sh sudo -E add-apt-repository --yes" 80 # Regex that finds enabled periodic apt configuration items 81 PERIODIC_APT_RE='^(APT::Periodic::.+")1"\;' 82 # Short-cuts for retrying/timeout calls 83 LILTO="timeout_attempt_delay_command 120s 5 30s" 84 BIGTO="timeout_attempt_delay_command 300s 5 60s" 85 86 # Safe env. vars. to transfer from root -> $ROOTLESS_USER (go env handled separately) 87 ROOTLESS_ENV_RE='(CIRRUS_.+)|(ROOTLESS_.+)|(.+_IMAGE.*)|(.+_BASE)|(.*DIRPATH)|(.*FILEPATH)|(SOURCE.*)|(DEPEND.*)|(.+_DEPS_.+)|(OS_REL.*)|(.+_ENV_RE)|(TRAVIS)|(CI.+)|(TEST_REMOTE.*)' 88 # Unsafe env. vars for display 89 SECRET_ENV_RE='(IRCID)|(ACCOUNT)|(GC[EP]..+)|(SSH)' 90 91 SPECIALMODE="${SPECIALMODE:-none}" 92 MOD_LIBPOD_CONF="${MOD_LIBPOD_CONF:false}" 93 TEST_REMOTE_CLIENT="${TEST_REMOTE_CLIENT:-false}" 94 export CONTAINER_RUNTIME=${CONTAINER_RUNTIME:-podman} 95 96 # When running as root, this may be empty or not, as a user, it MUST be set. 97 if [[ "$USER" == "root" ]] 98 then 99 ROOTLESS_USER="${ROOTLESS_USER:-}" 100 else 101 ROOTLESS_USER="${ROOTLESS_USER:-$USER}" 102 fi 103 104 # GCE image-name compatible string representation of distribution name 105 OS_RELEASE_ID="$(source /etc/os-release; echo $ID)" 106 # GCE image-name compatible string representation of distribution _major_ version 107 OS_RELEASE_VER="$(source /etc/os-release; echo $VERSION_ID | cut -d '.' -f 1)" 108 # Combined to ease soe usage 109 OS_REL_VER="${OS_RELEASE_ID}-${OS_RELEASE_VER}" 110 # Type of filesystem used for cgroups 111 CG_FS_TYPE="$(stat -f -c %T /sys/fs/cgroup)" 112 113 # Installed into cache-images, supports overrides 114 # by user-data in case of breakage or for debugging. 115 CUSTOM_CLOUD_CONFIG_DEFAULTS="$GOSRC/$PACKER_BASE/cloud-init/$OS_RELEASE_ID/cloud.cfg.d" 116 # Pass in a list of one or more envariable names; exit non-zero with 117 # helpful error message if any value is empty 118 req_env_var() { 119 # Provide context. If invoked from function use its name; else script name 120 local caller=${FUNCNAME[1]} 121 if [[ -n "$caller" ]]; then 122 # Indicate that it's a function name 123 caller="$caller()" 124 else 125 # Not called from a function: use script name 126 caller=$(basename $0) 127 fi 128 129 # Usage check 130 [[ -n "$1" ]] || die 1 "FATAL: req_env_var: invoked without arguments" 131 132 # Each input arg is an envariable name, e.g. HOME PATH etc. Expand each. 133 # If any is empty, bail out and explain why. 134 for i; do 135 if [[ -z "${!i}" ]]; then 136 die 9 "FATAL: $caller requires \$$i to be non-empty" 137 fi 138 done 139 } 140 141 item_test() { 142 ITEM="$1" 143 shift 144 TEST_ARGS="$@" 145 req_env_var ITEM TEST_ARGS 146 147 if ERR=$(test "$@" 2>&1) 148 then 149 echo "ok $ITEM" 150 return 0 151 else 152 RET=$? 153 echo -n "not ok $ITEM: $TEST_ARGS" 154 if [[ -z "$ERR" ]] 155 then 156 echo "" 157 else # test command itself failed 158 echo -n ":" # space follows :'s in $ERR 159 echo "$ERR" | cut -d : -f 4- # omit filename, line number, and command 160 fi 161 return $RET 162 fi 163 } 164 165 show_env_vars() { 166 echo "Showing selection of environment variable definitions:" 167 _ENV_VAR_NAMES=$(awk 'BEGIN{for(v in ENVIRON) print v}' | \ 168 egrep -v "(^PATH$)|(^BASH_FUNC)|(^[[:punct:][:space:]]+)|$SECRET_ENV_RE" | \ 169 sort -u) 170 for _env_var_name in $_ENV_VAR_NAMES 171 do 172 # Supports older BASH versions 173 printf " ${_env_var_name}=%q\n" "$(printenv $_env_var_name)" 174 done 175 } 176 177 die() { 178 echo "************************************************" 179 echo ">>>>> ${2:-FATAL ERROR (but no message given!) in ${FUNCNAME[1]}()}" 180 echo "************************************************" 181 exit ${1:-1} 182 } 183 184 warn() { 185 echo ">>>>> ${1:-WARNING (but no message given!) in ${FUNCNAME[1]}()}" > /dev/stderr 186 } 187 188 bad_os_id_ver() { 189 echo "Unknown/Unsupported distro. $OS_RELEASE_ID and/or version $OS_RELEASE_VER for $(basename $0)" 190 exit 42 191 } 192 193 stub() { 194 echo "STUB: Pretending to do $1" 195 } 196 197 timeout_attempt_delay_command() { 198 TIMEOUT=$1 199 ATTEMPTS=$2 200 DELAY=$3 201 shift 3 202 STDOUTERR=$(mktemp -p '' $(basename $0)_XXXXX) 203 req_env_var ATTEMPTS DELAY 204 echo "Retrying $ATTEMPTS times with a $DELAY delay, and $TIMEOUT timeout for command: $@" 205 for (( COUNT=1 ; COUNT <= $ATTEMPTS ; COUNT++ )) 206 do 207 echo "##### (attempt #$COUNT)" &>> "$STDOUTERR" 208 if timeout --foreground $TIMEOUT "$@" &>> "$STDOUTERR" 209 then 210 echo "##### (success after #$COUNT attempts)" &>> "$STDOUTERR" 211 break 212 else 213 echo "##### (failed with exit: $?)" &>> "$STDOUTERR" 214 sleep $DELAY 215 fi 216 done 217 cat "$STDOUTERR" 218 rm -f "$STDOUTERR" 219 if (( COUNT > $ATTEMPTS )) 220 then 221 echo "##### (exceeded $ATTEMPTS attempts)" 222 exit 125 223 fi 224 } 225 226 ircmsg() { 227 req_env_var CIRRUS_TASK_ID IRCID 228 [[ -n "$*" ]] || die 9 "ircmsg() invoked without message text argument" 229 # Sometimes setup_environment.sh didn't run 230 SCRIPT="$(dirname $0)/podbot.py" 231 NICK="podbot_$CIRRUS_TASK_ID" 232 NICK="${NICK:0:15}" # Any longer will break things 233 set +e 234 $SCRIPT $NICK $@ 235 echo "Ignoring exit($?)" 236 set -e 237 } 238 239 # This covers all possible human & CI workflow parallel & serial combinations 240 # where at least one caller must definitively discover if within a commit range 241 # there is at least one release tag not having any '-' characters (return 0) 242 # or otherwise (return non-0). 243 is_release() { 244 unset RELVER 245 local ret 246 req_env_var CIRRUS_CHANGE_IN_REPO 247 if [[ -n "$CIRRUS_TAG" ]]; then 248 RELVER="$CIRRUS_TAG" 249 elif [[ ! "$CIRRUS_BASE_SHA" =~ "unknown" ]] 250 then 251 # Normally not possible for this to be empty, except when unittesting. 252 req_env_var CIRRUS_BASE_SHA 253 local range="${CIRRUS_BASE_SHA}..${CIRRUS_CHANGE_IN_REPO}" 254 if echo "${range}$CIRRUS_TAG" | grep -iq 'unknown'; then 255 die 11 "is_release() unusable range ${range} or tag $CIRRUS_TAG" 256 fi 257 258 if type -P git &> /dev/null 259 then 260 git fetch --all --tags &> /dev/null|| \ 261 die 12 "is_release() failed to fetch tags" 262 RELVER=$(git log --pretty='format:%d' $range | \ 263 grep '(tag:' | sed -r -e 's/\s+[(]tag:\s+(v[0-9].*)[)]/\1/' | \ 264 sort -uV | tail -1) 265 ret=$? 266 else 267 warn -1 "Git command not found while checking for release" 268 ret="-1" 269 fi 270 [[ "$ret" -eq "0" ]] || \ 271 die 13 "is_release() failed to parse tags" 272 else # Not testing a PR, but neither CIRRUS_BASE_SHA or CIRRUS_TAG are set 273 return 1 274 fi 275 if [[ -n "$RELVER" ]]; then 276 echo "Found \$RELVER $RELVER" 277 if echo "$RELVER" | grep -q '-'; then 278 return 2 # development tag 279 else 280 return 0 281 fi 282 else 283 return 1 # not a release 284 fi 285 } 286 287 setup_rootless() { 288 req_env_var ROOTLESS_USER GOSRC SECRET_ENV_RE ROOTLESS_ENV_RE 289 290 # Only do this once 291 if passwd --status $ROOTLESS_USER 292 then 293 echo "Updating $ROOTLESS_USER user permissions on possibly changed libpod code" 294 chown -R $ROOTLESS_USER:$ROOTLESS_USER "$GOSRC" 295 return 0 296 fi 297 298 cd $GOSRC 299 # Guarantee independence from specific values 300 ROOTLESS_UID=$[RANDOM+1000] 301 ROOTLESS_GID=$[RANDOM+1000] 302 echo "creating $ROOTLESS_UID:$ROOTLESS_GID $ROOTLESS_USER user" 303 groupadd -g $ROOTLESS_GID $ROOTLESS_USER 304 useradd -g $ROOTLESS_GID -u $ROOTLESS_UID --no-user-group --create-home $ROOTLESS_USER 305 chown -R $ROOTLESS_USER:$ROOTLESS_USER "$GOSRC" 306 307 echo "creating ssh keypair for $USER" 308 [[ -r "$HOME/.ssh/id_rsa" ]] || \ 309 ssh-keygen -P "" -f "$HOME/.ssh/id_rsa" 310 311 echo "Allowing ssh key for $ROOTLESS_USER" 312 (umask 077 && mkdir "/home/$ROOTLESS_USER/.ssh") 313 chown -R $ROOTLESS_USER:$ROOTLESS_USER "/home/$ROOTLESS_USER/.ssh" 314 install -o $ROOTLESS_USER -g $ROOTLESS_USER -m 0600 \ 315 "$HOME/.ssh/id_rsa.pub" "/home/$ROOTLESS_USER/.ssh/authorized_keys" 316 # Makes debugging easier 317 cat /root/.ssh/authorized_keys >> "/home/$ROOTLESS_USER/.ssh/authorized_keys" 318 319 echo "Configuring subuid and subgid" 320 grep -q "${ROOTLESS_USER}" /etc/subuid || \ 321 echo "${ROOTLESS_USER}:$[ROOTLESS_UID * 100]:65536" | \ 322 tee -a /etc/subuid >> /etc/subgid 323 324 # Env. vars set by Cirrus and setup_environment.sh must be explicitly 325 # transferred to the test-user. 326 echo "Configuring rootless user's environment variables:" 327 echo "# Added by $GOSRC/$SCRIPT_PATH/lib.sh setup_rootless()" 328 _ENV_VAR_NAMES=$(awk 'BEGIN{for(v in ENVIRON) print v}' | \ 329 egrep -v "(^PATH$)|(^BASH_FUNC)|(^[[:punct:][:space:]]+)|$SECRET_ENV_RE" | \ 330 egrep "$ROOTLESS_ENV_RE" | \ 331 sort -u) 332 for _env_var_name in $_ENV_VAR_NAMES 333 do 334 # Works with older versions of bash 335 printf "${_env_var_name}=%q\n" "$(printenv $_env_var_name)" >> "/home/$ROOTLESS_USER/.bashrc" 336 done 337 338 echo "Ensure the systems ssh process is up and running within 5 minutes" 339 systemctl start sshd 340 NOW=$(date +%s) 341 TIMEOUT=$(date --date '+5 minutes' +%s) 342 while [[ "$(date +%s)" -lt "$TIMEOUT" ]] 343 do 344 if timeout --foreground -k 1s 1s \ 345 ssh $ROOTLESS_USER@localhost \ 346 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o CheckHostIP=no \ 347 true 348 then 349 break 350 else 351 sleep 2s 352 fi 353 done 354 [[ "$(date +%s)" -lt "$TIMEOUT" ]] || \ 355 die 11 "Timeout exceeded waiting for localhost ssh capability" 356 } 357 358 # Helper/wrapper script to only show stderr/stdout on non-zero exit 359 install_ooe() { 360 req_env_var SCRIPT_BASE 361 echo "Installing script to mask stdout/stderr unless non-zero exit." 362 sudo install -D -m 755 "$GOSRC/$SCRIPT_BASE/ooe.sh" /usr/local/bin/ooe.sh 363 } 364 365 # Grab a newer version of git from software collections 366 # https://www.softwarecollections.org/en/ 367 # and use it with a wrapper 368 install_scl_git() { 369 echo "Installing SoftwareCollections updated 'git' version." 370 ooe.sh sudo yum -y install rh-git29 371 cat << "EOF" | sudo tee /usr/bin/git 372 #!/bin/bash 373 374 scl enable rh-git29 -- git $@ 375 EOF 376 sudo chmod 755 /usr/bin/git 377 } 378 379 install_test_configs() { 380 echo "Installing cni config, policy and registry config" 381 req_env_var GOSRC SCRIPT_BASE 382 cd $GOSRC 383 install -v -D -m 644 ./cni/87-podman-bridge.conflist /etc/cni/net.d/ 384 # This config must always sort last in the list of networks (podman picks first one 385 # as the default). This config prevents allocation of network address space used 386 # by default in google cloud. https://cloud.google.com/vpc/docs/vpc#ip-ranges 387 install -v -D -m 644 $SCRIPT_BASE/99-do-not-use-google-subnets.conflist /etc/cni/net.d/ 388 install -v -D -m 644 ./test/policy.json /etc/containers/ 389 install -v -D -m 644 ./test/registries.conf /etc/containers/ 390 } 391 392 # Remove all files (except conmon, for now) provided by the distro version of podman. 393 # Except conmon, for now as it's expected to eventually be packaged separately. 394 # All VM cache-images used for testing include the distro podman because (1) it's 395 # required for podman-in-podman testing and (2) it somewhat simplifies the task 396 # of pulling in necessary prerequisites packages as the set can change over time. 397 # For general CI testing however, calling this function makes sure the system 398 # can only run the compiled source version. 399 remove_packaged_podman_files() { 400 echo "Removing packaged podman files to prevent conflicts with source build and testing." 401 req_env_var OS_RELEASE_ID 402 403 # If any binaries are resident they could cause unexpected pollution 404 for unit in io.podman.service io.podman.socket 405 do 406 for state in enabled active 407 do 408 if systemctl --quiet is-$state $unit 409 then 410 echo "Warning: $unit found $state prior to packaged-file removal" 411 systemctl --quiet disable $unit || true 412 systemctl --quiet stop $unit || true 413 fi 414 done 415 done 416 417 if [[ "$OS_RELEASE_ID" =~ "ubuntu" ]] 418 then 419 LISTING_CMD="sudo -E dpkg-query -L podman" 420 else 421 LISTING_CMD='sudo rpm -ql podman' 422 fi 423 424 # yum/dnf/dpkg may list system directories, only remove files 425 $LISTING_CMD | while read fullpath 426 do 427 # Sub-directories may contain unrelated/valuable stuff 428 if [[ -d "$fullpath" ]]; then continue; fi 429 ooe.sh sudo rm -vf "$fullpath" 430 done 431 432 # Be super extra sure and careful vs performant and completely safe 433 sync && echo 3 > /proc/sys/vm/drop_caches 434 } 435 436 canonicalize_image_names() { 437 req_env_var IMGNAMES 438 echo "Adding all current base images to \$IMGNAMES for timestamp update" 439 export IMGNAMES="\ 440 $IMGNAMES 441 $UBUNTU_BASE_IMAGE 442 $PRIOR_UBUNTU_BASE_IMAGE 443 $FEDORA_BASE_IMAGE 444 $PRIOR_FEDORA_BASE_IMAGE 445 " 446 } 447 448 systemd_banish() { 449 $GOSRC/$PACKER_BASE/systemd_banish.sh 450 } 451 452 install_buildah_packages() { 453 git clone https://github.com/containers/buildah.git /tmp/buildah 454 if [[ -r "$BUILDAH_PACKAGES_FILEPATH" ]]; then 455 source "$BUILDAH_PACKAGES_FILEPATH" 456 req_env_var UBUNTU_BUILDAH_PACKAGES FEDORA_BUILDAH_PACKAGES OS_RELEASE_ID 457 case "$OS_RELEASE_ID" in 458 fedora) 459 $BIGTO ooe.sh sudo dnf install -y ${FEDORA_BUILDAH_PACKAGES[@]} 460 ;; 461 ubuntu) 462 $LILTO $SUDOAPTGET update 463 $BIGTO $SUDOAPTGET install ${UBUNTU_BUILDAH_PACKAGES[@]} 464 ;; 465 *) bad_os_id_ver ;; 466 esac 467 else 468 warn "Could not find $BUILDAH_PACKAGES_FILEPATH in buildah repository root." 469 fi 470 } 471 472 _finalize() { 473 set +e # Don't fail at the very end 474 if [[ -d "$CUSTOM_CLOUD_CONFIG_DEFAULTS" ]] 475 then 476 echo "Installing custom cloud-init defaults" 477 sudo cp -v "$CUSTOM_CLOUD_CONFIG_DEFAULTS"/* /etc/cloud/cloud.cfg.d/ 478 else 479 echo "Could not find any files in $CUSTOM_CLOUD_CONFIG_DEFAULTS" 480 fi 481 echo "Re-initializing so next boot does 'first-boot' setup again." 482 cd / 483 sudo rm -rf /var/lib/cloud/instanc* 484 sudo rm -rf /root/.ssh/* 485 sudo rm -rf /etc/ssh/*key* 486 sudo rm -rf /etc/ssh/moduli 487 sudo rm -rf /home/* 488 sudo rm -rf /tmp/* 489 sudo rm -rf /tmp/.??* 490 sudo sync 491 sudo fstrim -av 492 } 493 494 rh_finalize() { 495 set +e # Don't fail at the very end 496 echo "Resetting to fresh-state for usage as cloud-image." 497 PKG=$(type -P dnf || type -P yum || echo "") 498 sudo $PKG clean all 499 sudo rm -rf /var/cache/{yum,dnf} 500 sudo rm -f /etc/udev/rules.d/*-persistent-*.rules 501 sudo touch /.unconfigured # force firstboot to run 502 _finalize 503 } 504 505 ubuntu_finalize() { 506 set +e # Don't fail at the very end 507 echo "Resetting to fresh-state for usage as cloud-image." 508 $LILTO $SUDOAPTGET autoremove 509 sudo rm -rf /var/cache/apt 510 _finalize 511 }