k8c.io/api/v3@v3.0.0-20230904060738-b0a93889c0b6/hack/lib.sh (about) 1 #!/usr/bin/env bash 2 3 # Copyright 2023 The Kubermatic Kubernetes Platform contributors. 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # you may not use this file except in compliance with the License. 7 # You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 17 # Required for signal propagation to work so 18 # the cleanup trap gets executed when a script 19 # receives a SIGINT 20 set -o monitor 21 22 retry() { 23 # Works only with bash but doesn't fail on other shells 24 start_time=$(date +%s) 25 set +e 26 actual_retry $@ 27 rc=$? 28 set -e 29 elapsed_time=$(($(date +%s) - $start_time)) 30 write_junit "$rc" "$elapsed_time" 31 return $rc 32 } 33 34 # We use an extra wrapping to write junit and have a timer 35 actual_retry() { 36 retries=$1 37 shift 38 39 count=0 40 delay=1 41 until "$@"; do 42 rc=$? 43 count=$((count + 1)) 44 if [ $count -lt "$retries" ]; then 45 echo "Retry $count/$retries exited $rc, retrying in $delay seconds..." > /dev/stderr 46 sleep $delay 47 else 48 echo "Retry $count/$retries exited $rc, no more retries left." > /dev/stderr 49 return $rc 50 fi 51 delay=$((delay * 2)) 52 done 53 return 0 54 } 55 56 echodate() { 57 # do not use -Is to keep this compatible with macOS 58 echo "[$(date +%Y-%m-%dT%H:%M:%S%:z)]" "$@" 59 } 60 61 write_junit() { 62 # Doesn't make any sense if we don't know a testname 63 if [ -z "${TEST_NAME:-}" ]; then return; fi 64 # Only run in CI 65 if [ -z "${ARTIFACTS:-}" ]; then return; fi 66 67 rc=$1 68 duration=${2:-0} 69 errors=0 70 failure="" 71 if [ "$rc" -ne 0 ]; then 72 errors=1 73 failure='<failure type="Failure">Step failed</failure>' 74 fi 75 TEST_CLASS="${TEST_CLASS:-Kubermatic}" 76 cat << EOF > ${ARTIFACTS}/junit.$(echo $TEST_NAME | sed 's/ /_/g' | tr '[:upper:]' '[:lower:]').xml 77 <?xml version="1.0" ?> 78 <testsuites> 79 <testsuite errors="$errors" failures="$errors" name="$TEST_CLASS" tests="1"> 80 <testcase classname="$TEST_CLASS" name="$TEST_NAME" time="$duration"> 81 $failure 82 </testcase> 83 </testsuite> 84 </testsuites> 85 EOF 86 } 87 88 is_containerized() { 89 # we're inside a Kubernetes pod/container or inside a container launched by containerize() 90 [ -n "${KUBERNETES_SERVICE_HOST:-}" ] || [ -n "${CONTAINERIZED:-}" ] 91 } 92 93 containerize() { 94 local cmd="$1" 95 local image="${CONTAINERIZE_IMAGE:-quay.io/kubermatic/util:2.3.0}" 96 local gocache="${CONTAINERIZE_GOCACHE:-/tmp/.gocache}" 97 local gomodcache="${CONTAINERIZE_GOMODCACHE:-/tmp/.gomodcache}" 98 local skip="${NO_CONTAINERIZE:-}" 99 100 # short-circuit containerize when in some cases it needs to be avoided 101 [ -n "$skip" ] && return 102 103 if ! is_containerized; then 104 echodate "Running $cmd in a Docker container using $image..." 105 mkdir -p "$gocache" 106 mkdir -p "$gomodcache" 107 108 exec docker run \ 109 -v "$PWD":/go/src/k8c.io/kubermatic \ 110 -v "$gocache":"$gocache" \ 111 -v "$gomodcache":"$gomodcache" \ 112 -w /go/src/k8c.io/kubermatic \ 113 -e "GOCACHE=$gocache" \ 114 -e "GOMODCACHE=$gomodcache" \ 115 -e "CONTAINERIZED=true" \ 116 -u "$(id -u):$(id -g)" \ 117 --entrypoint="$cmd" \ 118 --rm \ 119 -it \ 120 $image $@ 121 122 exit $? 123 fi 124 } 125 126 ensure_github_host_pubkey() { 127 # check whether we already have a known_hosts entry for Github 128 if ssh-keygen -F github.com > /dev/null 2>&1; then 129 echo " [*] Github's SSH host key already present" > /dev/stderr 130 else 131 local github_rsa_key 132 # https://help.github.com/en/github/authenticating-to-github/githubs-ssh-key-fingerprints 133 github_rsa_key="github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=" 134 135 echo " [*] Adding Github's SSH host key to known hosts" > /dev/stderr 136 mkdir -p "$HOME/.ssh" 137 chmod 700 "$HOME/.ssh" 138 echo "$github_rsa_key" >> "$HOME/.ssh/known_hosts" 139 chmod 600 "$HOME/.ssh/known_hosts" 140 fi 141 } 142 143 # append_trap appends to existing traps, if any. It is needed because Bash replaces existing handlers 144 # rather than appending: https://stackoverflow.com/questions/3338030/multiple-bash-traps-for-the-same-signal 145 # Needing this func is a strong indicator that Bash is not the right language anymore. Also, this 146 # basically needs unit tests. 147 append_trap() { 148 command="$1" 149 signal="$2" 150 151 # Have existing traps, must append 152 if [[ "$(trap -p | grep $signal)" ]]; then 153 existingHandlerName="$(trap -p | grep $signal | awk '{print $3}' | tr -d "'")" 154 155 newHandlerName="${command}_$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 13)" 156 # Need eval to get a random func name 157 eval "$newHandlerName() { $command; $existingHandlerName; }" 158 echodate "Appending $command as trap for $signal, existing command $existingHandlerName" 159 trap $newHandlerName $signal 160 # First trap 161 else 162 echodate "Using $command as trap for $signal" 163 trap $command $signal 164 fi 165 } 166 167 start_docker_daemon_ci() { 168 # DOCKER_REGISTRY_MIRROR_ADDR is injected via Prow preset; 169 # start-docker.sh is part of the build image. 170 DOCKER_REGISTRY_MIRROR="${DOCKER_REGISTRY_MIRROR_ADDR:-}" DOCKER_MTU=1400 start-docker.sh 171 172 # enable the modern buildx plugin 173 echodate "Enabling dockerx plugin" 174 docker buildx install 175 } 176 177 start_docker_daemon() { 178 if docker stats --no-stream > /dev/null 2>&1; then 179 echodate "Not starting Docker again, it's already running." 180 return 181 fi 182 183 # Start Docker daemon 184 echodate "Starting Docker" 185 dockerd > /tmp/docker.log 2>&1 & 186 187 echodate "Started Docker successfully" 188 append_trap docker_logs EXIT 189 190 # Wait for Docker to start 191 echodate "Waiting for Docker" 192 retry 5 docker stats --no-stream 193 echodate "Docker became ready" 194 } 195 196 repeat() { 197 local end=$1 198 local str="${2:-=}" 199 200 for i in $(seq 1 $end); do 201 echo -n "${str}" 202 done 203 } 204 205 heading() { 206 local title="$@" 207 echo "$title" 208 repeat ${#title} "=" 209 echo 210 } 211 212 # go_test wraps running `go test` commands. The first argument needs to be file name 213 # for a junit result file that will be generated if go-junit-report is present and 214 # $ARTIFACTS is set. The remaining arguments are passed to `go test`. 215 go_test() { 216 local junit_name="${1:-}" 217 shift 218 219 # only run go-junit-report if binary is present and we're in CI / the ARTIFACTS environment is set 220 if [ -x "$(command -v go-junit-report)" ] && [ ! -z "${ARTIFACTS:-}" ]; then 221 go test "$@" 2>&1 | go-junit-report -set-exit-code -iocopy -out ${ARTIFACTS}/junit.${junit_name}.xml 222 else 223 go test "$@" 224 fi 225 } 226 227 # go_test wraps running `go test` commands. The first argument needs to be file name 228 # for a junit result file that will be generated if go-junit-report is present and 229 # $ARTIFACTS is set. The remaining arguments are passed to `go test`. 230 go_test() { 231 local junit_name="${1:-}" 232 shift 233 234 # only run go-junit-report if binary is present and we're in CI / the ARTIFACTS environment is set 235 if [ -x "$(command -v go-junit-report)" ] && [ ! -z "${ARTIFACTS:-}" ]; then 236 go test "$@" 2>&1 | go-junit-report -set-exit-code -iocopy -out ${ARTIFACTS}/junit.${junit_name}.xml 237 else 238 go test "$@" 239 fi 240 }