github.com/wmuizelaar/kpt@v0.0.0-20221018115725-bd564717b2ed/scripts/lib/util.sh (about) 1 #!/usr/bin/env bash 2 3 # Copyright 2014 The Kubernetes Authors. 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 function kube::util::sourced_variable { 18 # Call this function to tell shellcheck that a variable is supposed to 19 # be used from other calling context. This helps quiet an "unused 20 # variable" warning from shellcheck and also document your code. 21 true 22 } 23 24 kube::util::sortable_date() { 25 date "+%Y%m%d-%H%M%S" 26 } 27 28 # arguments: target, item1, item2, item3, ... 29 # returns 0 if target is in the given items, 1 otherwise. 30 kube::util::array_contains() { 31 local search="$1" 32 local element 33 shift 34 for element; do 35 if [[ "${element}" == "${search}" ]]; then 36 return 0 37 fi 38 done 39 return 1 40 } 41 42 kube::util::wait_for_url() { 43 local url=$1 44 local prefix=${2:-} 45 local wait=${3:-1} 46 local times=${4:-30} 47 local maxtime=${5:-1} 48 49 command -v curl >/dev/null || { 50 kube::log::usage "curl must be installed" 51 exit 1 52 } 53 54 local i 55 for i in $(seq 1 "${times}"); do 56 local out 57 if out=$(curl --max-time "${maxtime}" -gkfs "${url}" 2>/dev/null); then 58 kube::log::status "On try ${i}, ${prefix}: ${out}" 59 return 0 60 fi 61 sleep "${wait}" 62 done 63 kube::log::error "Timed out waiting for ${prefix} to answer at ${url}; tried ${times} waiting ${wait} between each" 64 return 1 65 } 66 67 # Example: kube::util::wait_for_success 120 5 "kubectl get nodes|grep localhost" 68 # arguments: wait time, sleep time, shell command 69 # returns 0 if the shell command get output, 1 otherwise. 70 kube::util::wait_for_success(){ 71 local wait_time="$1" 72 local sleep_time="$2" 73 local cmd="$3" 74 while [ "$wait_time" -gt 0 ]; do 75 if eval "$cmd"; then 76 return 0 77 else 78 sleep "$sleep_time" 79 wait_time=$((wait_time-sleep_time)) 80 fi 81 done 82 return 1 83 } 84 85 # Example: kube::util::trap_add 'echo "in trap DEBUG"' DEBUG 86 # See: http://stackoverflow.com/questions/3338030/multiple-bash-traps-for-the-same-signal 87 kube::util::trap_add() { 88 local trap_add_cmd 89 trap_add_cmd=$1 90 shift 91 92 for trap_add_name in "$@"; do 93 local existing_cmd 94 local new_cmd 95 96 # Grab the currently defined trap commands for this trap 97 existing_cmd=$(trap -p "${trap_add_name}" | awk -F"'" '{print $2}') 98 99 if [[ -z "${existing_cmd}" ]]; then 100 new_cmd="${trap_add_cmd}" 101 else 102 new_cmd="${trap_add_cmd};${existing_cmd}" 103 fi 104 105 # Assign the test. Disable the shellcheck warning telling that trap 106 # commands should be single quoted to avoid evaluating them at this 107 # point instead evaluating them at run time. The logic of adding new 108 # commands to a single trap requires them to be evaluated right away. 109 # shellcheck disable=SC2064 110 trap "${new_cmd}" "${trap_add_name}" 111 done 112 } 113 114 # Opposite of kube::util::ensure-temp-dir() 115 kube::util::cleanup-temp-dir() { 116 rm -rf "${KUBE_TEMP}" 117 } 118 119 # Create a temp dir that'll be deleted at the end of this bash session. 120 # 121 # Vars set: 122 # KUBE_TEMP 123 kube::util::ensure-temp-dir() { 124 if [[ -z ${KUBE_TEMP-} ]]; then 125 KUBE_TEMP=$(mktemp -d 2>/dev/null || mktemp -d -t kubernetes.XXXXXX) 126 kube::util::trap_add kube::util::cleanup-temp-dir EXIT 127 fi 128 } 129 130 kube::util::host_os() { 131 local host_os 132 case "$(uname -s)" in 133 Darwin) 134 host_os=darwin 135 ;; 136 Linux) 137 host_os=linux 138 ;; 139 *) 140 kube::log::error "Unsupported host OS. Must be Linux or Mac OS X." 141 exit 1 142 ;; 143 esac 144 echo "${host_os}" 145 } 146 147 kube::util::host_arch() { 148 local host_arch 149 case "$(uname -m)" in 150 x86_64*) 151 host_arch=amd64 152 ;; 153 i?86_64*) 154 host_arch=amd64 155 ;; 156 amd64*) 157 host_arch=amd64 158 ;; 159 aarch64*) 160 host_arch=arm64 161 ;; 162 arm64*) 163 host_arch=arm64 164 ;; 165 arm*) 166 host_arch=arm 167 ;; 168 i?86*) 169 host_arch=x86 170 ;; 171 s390x*) 172 host_arch=s390x 173 ;; 174 ppc64le*) 175 host_arch=ppc64le 176 ;; 177 *) 178 kube::log::error "Unsupported host arch. Must be x86_64, 386, arm, arm64, s390x or ppc64le." 179 exit 1 180 ;; 181 esac 182 echo "${host_arch}" 183 } 184 185 # This figures out the host platform without relying on golang. We need this as 186 # we don't want a golang install to be a prerequisite to building yet we need 187 # this info to figure out where the final binaries are placed. 188 kube::util::host_platform() { 189 echo "$(kube::util::host_os)/$(kube::util::host_arch)" 190 } 191 192 # looks for $1 in well-known output locations for the platform ($2) 193 # $KUBE_ROOT must be set 194 kube::util::find-binary-for-platform() { 195 local -r lookfor="$1" 196 local -r platform="$2" 197 local locations=( 198 "${KUBE_ROOT}/_output/bin/${lookfor}" 199 "${KUBE_ROOT}/_output/dockerized/bin/${platform}/${lookfor}" 200 "${KUBE_ROOT}/_output/local/bin/${platform}/${lookfor}" 201 "${KUBE_ROOT}/platforms/${platform}/${lookfor}" 202 ) 203 # Also search for binary in bazel build tree. 204 # The bazel go rules place some binaries in subtrees like 205 # "bazel-bin/source/path/linux_amd64_pure_stripped/binaryname", so make sure 206 # the platform name is matched in the path. 207 while IFS=$'\n' read -r location; do 208 locations+=("$location"); 209 done < <(find "${KUBE_ROOT}/bazel-bin/" -type f -executable \ 210 \( -path "*/${platform/\//_}*/${lookfor}" -o -path "*/${lookfor}" \) 2>/dev/null || true) 211 212 # List most recently-updated location. 213 local -r bin=$( (ls -t "${locations[@]}" 2>/dev/null || true) | head -1 ) 214 echo -n "${bin}" 215 } 216 217 # looks for $1 in well-known output locations for the host platform 218 # $KUBE_ROOT must be set 219 kube::util::find-binary() { 220 kube::util::find-binary-for-platform "$1" "$(kube::util::host_platform)" 221 } 222 223 # Run all known doc generators (today gendocs and genman for kubectl) 224 # $1 is the directory to put those generated documents 225 kube::util::gen-docs() { 226 local dest="$1" 227 228 # Find binary 229 gendocs=$(kube::util::find-binary "gendocs") 230 genkubedocs=$(kube::util::find-binary "genkubedocs") 231 genman=$(kube::util::find-binary "genman") 232 genyaml=$(kube::util::find-binary "genyaml") 233 genfeddocs=$(kube::util::find-binary "genfeddocs") 234 235 # TODO: If ${genfeddocs} is not used from anywhere (it isn't used at 236 # least from k/k tree), remove it completely. 237 kube::util::sourced_variable "${genfeddocs}" 238 239 mkdir -p "${dest}/docs/user-guide/kubectl/" 240 "${gendocs}" "${dest}/docs/user-guide/kubectl/" 241 mkdir -p "${dest}/docs/admin/" 242 "${genkubedocs}" "${dest}/docs/admin/" "kube-apiserver" 243 "${genkubedocs}" "${dest}/docs/admin/" "kube-controller-manager" 244 "${genkubedocs}" "${dest}/docs/admin/" "kube-proxy" 245 "${genkubedocs}" "${dest}/docs/admin/" "kube-scheduler" 246 "${genkubedocs}" "${dest}/docs/admin/" "kubelet" 247 "${genkubedocs}" "${dest}/docs/admin/" "kubeadm" 248 249 mkdir -p "${dest}/docs/man/man1/" 250 "${genman}" "${dest}/docs/man/man1/" "kube-apiserver" 251 "${genman}" "${dest}/docs/man/man1/" "kube-controller-manager" 252 "${genman}" "${dest}/docs/man/man1/" "kube-proxy" 253 "${genman}" "${dest}/docs/man/man1/" "kube-scheduler" 254 "${genman}" "${dest}/docs/man/man1/" "kubelet" 255 "${genman}" "${dest}/docs/man/man1/" "kubectl" 256 "${genman}" "${dest}/docs/man/man1/" "kubeadm" 257 258 mkdir -p "${dest}/docs/yaml/kubectl/" 259 "${genyaml}" "${dest}/docs/yaml/kubectl/" 260 261 # create the list of generated files 262 pushd "${dest}" > /dev/null || return 1 263 touch docs/.generated_docs 264 find . -type f | cut -sd / -f 2- | LC_ALL=C sort > docs/.generated_docs 265 popd > /dev/null || return 1 266 } 267 268 # Removes previously generated docs-- we don't want to check them in. $KUBE_ROOT 269 # must be set. 270 kube::util::remove-gen-docs() { 271 if [ -e "${KUBE_ROOT}/docs/.generated_docs" ]; then 272 # remove all of the old docs; we don't want to check them in. 273 while read -r file; do 274 rm "${KUBE_ROOT}/${file}" 2>/dev/null || true 275 done <"${KUBE_ROOT}/docs/.generated_docs" 276 # The docs/.generated_docs file lists itself, so we don't need to explicitly 277 # delete it. 278 fi 279 } 280 281 # Takes a group/version and returns the path to its location on disk, sans 282 # "pkg". E.g.: 283 # * default behavior: extensions/v1beta1 -> apis/extensions/v1beta1 284 # * default behavior for only a group: experimental -> apis/experimental 285 # * Special handling for empty group: v1 -> api/v1, unversioned -> api/unversioned 286 # * Special handling for groups suffixed with ".k8s.io": foo.k8s.io/v1 -> apis/foo/v1 287 # * Very special handling for when both group and version are "": / -> api 288 # 289 # $KUBE_ROOT must be set. 290 kube::util::group-version-to-pkg-path() { 291 local group_version="$1" 292 293 while IFS=$'\n' read -r api; do 294 if [[ "${api}" = "${group_version/.*k8s.io/}" ]]; then 295 echo "vendor/k8s.io/api/${group_version/.*k8s.io/}" 296 return 297 fi 298 done < <(cd "${KUBE_ROOT}/staging/src/k8s.io/api" && find . -name types.go -exec dirname {} \; | sed "s|\./||g" | sort) 299 300 # "v1" is the API GroupVersion 301 if [[ "${group_version}" == "v1" ]]; then 302 echo "vendor/k8s.io/api/core/v1" 303 return 304 fi 305 306 # Special cases first. 307 # TODO(lavalamp): Simplify this by moving pkg/api/v1 and splitting pkg/api, 308 # moving the results to pkg/apis/api. 309 case "${group_version}" in 310 # both group and version are "", this occurs when we generate deep copies for internal objects of the legacy v1 API. 311 __internal) 312 echo "pkg/apis/core" 313 ;; 314 meta/v1) 315 echo "vendor/k8s.io/apimachinery/pkg/apis/meta/v1" 316 ;; 317 meta/v1beta1) 318 echo "vendor/k8s.io/apimachinery/pkg/apis/meta/v1beta1" 319 ;; 320 *.k8s.io) 321 echo "pkg/apis/${group_version%.*k8s.io}" 322 ;; 323 *.k8s.io/*) 324 echo "pkg/apis/${group_version/.*k8s.io/}" 325 ;; 326 *) 327 echo "pkg/apis/${group_version%__internal}" 328 ;; 329 esac 330 } 331 332 # Takes a group/version and returns the swagger-spec file name. 333 # default behavior: extensions/v1beta1 -> extensions_v1beta1 334 # special case for v1: v1 -> v1 335 kube::util::gv-to-swagger-name() { 336 local group_version="$1" 337 case "${group_version}" in 338 v1) 339 echo "v1" 340 ;; 341 *) 342 echo "${group_version%/*}_${group_version#*/}" 343 ;; 344 esac 345 } 346 347 # Returns the name of the upstream remote repository name for the local git 348 # repo, e.g. "upstream" or "origin". 349 kube::util::git_upstream_remote_name() { 350 git remote -v | grep fetch |\ 351 grep -E 'github.com[/:]kubernetes/kubernetes|k8s.io/kubernetes' |\ 352 head -n 1 | awk '{print $1}' 353 } 354 355 # Exits script if working directory is dirty. If it's run interactively in the terminal 356 # the user can commit changes in a second terminal. This script will wait. 357 kube::util::ensure_clean_working_dir() { 358 while ! git diff HEAD --exit-code &>/dev/null; do 359 echo -e "\nUnexpected dirty working directory:\n" 360 if tty -s; then 361 git status -s 362 else 363 git diff -a # be more verbose in log files without tty 364 exit 1 365 fi | sed 's/^/ /' 366 echo -e "\nCommit your changes in another terminal and then continue here by pressing enter." 367 read -r 368 done 1>&2 369 } 370 371 # Find the base commit using: 372 # $PULL_BASE_SHA if set (from Prow) 373 # current ref from the remote upstream branch 374 kube::util::base_ref() { 375 local -r git_branch=$1 376 377 if [[ -n ${PULL_BASE_SHA:-} ]]; then 378 echo "${PULL_BASE_SHA}" 379 return 380 fi 381 382 full_branch="$(kube::util::git_upstream_remote_name)/${git_branch}" 383 384 # make sure the branch is valid, otherwise the check will pass erroneously. 385 if ! git describe "${full_branch}" >/dev/null; then 386 # abort! 387 exit 1 388 fi 389 390 echo "${full_branch}" 391 } 392 393 # Checks whether there are any files matching pattern $2 changed between the 394 # current branch and upstream branch named by $1. 395 # Returns 1 (false) if there are no changes 396 # 0 (true) if there are changes detected. 397 kube::util::has_changes() { 398 local -r git_branch=$1 399 local -r pattern=$2 400 local -r not_pattern=${3:-totallyimpossiblepattern} 401 402 local base_ref 403 base_ref=$(kube::util::base_ref "${git_branch}") 404 echo "Checking for '${pattern}' changes against '${base_ref}'" 405 406 # notice this uses ... to find the first shared ancestor 407 if git diff --name-only "${base_ref}...HEAD" | grep -v -E "${not_pattern}" | grep "${pattern}" > /dev/null; then 408 return 0 409 fi 410 # also check for pending changes 411 if git status --porcelain | grep -v -E "${not_pattern}" | grep "${pattern}" > /dev/null; then 412 echo "Detected '${pattern}' uncommitted changes." 413 return 0 414 fi 415 echo "No '${pattern}' changes detected." 416 return 1 417 } 418 419 kube::util::download_file() { 420 local -r url=$1 421 local -r destination_file=$2 422 423 rm "${destination_file}" 2&> /dev/null || true 424 425 for i in $(seq 5) 426 do 427 if ! curl -fsSL --retry 3 --keepalive-time 2 "${url}" -o "${destination_file}"; then 428 echo "Downloading ${url} failed. $((5-i)) retries left." 429 sleep 1 430 else 431 echo "Downloading ${url} succeed" 432 return 0 433 fi 434 done 435 return 1 436 } 437 438 # Test whether openssl is installed. 439 # Sets: 440 # OPENSSL_BIN: The path to the openssl binary to use 441 function kube::util::test_openssl_installed { 442 if ! openssl version >& /dev/null; then 443 echo "Failed to run openssl. Please ensure openssl is installed" 444 exit 1 445 fi 446 447 OPENSSL_BIN=$(command -v openssl) 448 } 449 450 # creates a client CA, args are sudo, dest-dir, ca-id, purpose 451 # purpose is dropped in after "key encipherment", you usually want 452 # '"client auth"' 453 # '"server auth"' 454 # '"client auth","server auth"' 455 function kube::util::create_signing_certkey { 456 local sudo=$1 457 local dest_dir=$2 458 local id=$3 459 local purpose=$4 460 # Create client ca 461 ${sudo} /usr/bin/env bash -e <<EOF 462 rm -f "${dest_dir}/${id}-ca.crt" "${dest_dir}/${id}-ca.key" 463 ${OPENSSL_BIN} req -x509 -sha256 -new -nodes -days 365 -newkey rsa:2048 -keyout "${dest_dir}/${id}-ca.key" -out "${dest_dir}/${id}-ca.crt" -subj "/C=xx/ST=x/L=x/O=x/OU=x/CN=ca/emailAddress=x/" 464 echo '{"signing":{"default":{"expiry":"43800h","usages":["signing","key encipherment",${purpose}]}}}' > "${dest_dir}/${id}-ca-config.json" 465 EOF 466 } 467 468 # signs a client certificate: args are sudo, dest-dir, CA, filename (roughly), username, groups... 469 function kube::util::create_client_certkey { 470 local sudo=$1 471 local dest_dir=$2 472 local ca=$3 473 local id=$4 474 local cn=${5:-$4} 475 local groups="" 476 local SEP="" 477 shift 5 478 while [ -n "${1:-}" ]; do 479 groups+="${SEP}{\"O\":\"$1\"}" 480 SEP="," 481 shift 1 482 done 483 ${sudo} /usr/bin/env bash -e <<EOF 484 cd ${dest_dir} 485 echo '{"CN":"${cn}","names":[${groups}],"hosts":[""],"key":{"algo":"rsa","size":2048}}' | ${CFSSL_BIN} gencert -ca=${ca}.crt -ca-key=${ca}.key -config=${ca}-config.json - | ${CFSSLJSON_BIN} -bare client-${id} 486 mv "client-${id}-key.pem" "client-${id}.key" 487 mv "client-${id}.pem" "client-${id}.crt" 488 rm -f "client-${id}.csr" 489 EOF 490 } 491 492 # signs a serving certificate: args are sudo, dest-dir, ca, filename (roughly), subject, hosts... 493 function kube::util::create_serving_certkey { 494 local sudo=$1 495 local dest_dir=$2 496 local ca=$3 497 local id=$4 498 local cn=${5:-$4} 499 local hosts="" 500 local SEP="" 501 shift 5 502 while [ -n "${1:-}" ]; do 503 hosts+="${SEP}\"$1\"" 504 SEP="," 505 shift 1 506 done 507 ${sudo} /usr/bin/env bash -e <<EOF 508 cd ${dest_dir} 509 echo '{"CN":"${cn}","hosts":[${hosts}],"key":{"algo":"rsa","size":2048}}' | ${CFSSL_BIN} gencert -ca=${ca}.crt -ca-key=${ca}.key -config=${ca}-config.json - | ${CFSSLJSON_BIN} -bare serving-${id} 510 mv "serving-${id}-key.pem" "serving-${id}.key" 511 mv "serving-${id}.pem" "serving-${id}.crt" 512 rm -f "serving-${id}.csr" 513 EOF 514 } 515 516 # creates a self-contained kubeconfig: args are sudo, dest-dir, ca file, host, port, client id, token(optional) 517 function kube::util::write_client_kubeconfig { 518 local sudo=$1 519 local dest_dir=$2 520 local ca_file=$3 521 local api_host=$4 522 local api_port=$5 523 local client_id=$6 524 local token=${7:-} 525 cat <<EOF | ${sudo} tee "${dest_dir}"/"${client_id}".kubeconfig > /dev/null 526 apiVersion: v1 527 kind: Config 528 clusters: 529 - cluster: 530 certificate-authority: ${ca_file} 531 server: https://${api_host}:${api_port}/ 532 name: local-up-cluster 533 users: 534 - user: 535 token: ${token} 536 client-certificate: ${dest_dir}/client-${client_id}.crt 537 client-key: ${dest_dir}/client-${client_id}.key 538 name: local-up-cluster 539 contexts: 540 - context: 541 cluster: local-up-cluster 542 user: local-up-cluster 543 name: local-up-cluster 544 current-context: local-up-cluster 545 EOF 546 547 # flatten the kubeconfig files to make them self contained 548 username=$(whoami) 549 ${sudo} /usr/bin/env bash -e <<EOF 550 $(kube::util::find-binary kubectl) --kubeconfig="${dest_dir}/${client_id}.kubeconfig" config view --minify --flatten > "/tmp/${client_id}.kubeconfig" 551 mv -f "/tmp/${client_id}.kubeconfig" "${dest_dir}/${client_id}.kubeconfig" 552 chown ${username} "${dest_dir}/${client_id}.kubeconfig" 553 EOF 554 } 555 556 # list_staging_repos outputs a sorted list of repos in staging/src/k8s.io 557 # each entry will just be the $repo portion of staging/src/k8s.io/$repo/... 558 # $KUBE_ROOT must be set. 559 function kube::util::list_staging_repos() { 560 ( 561 cd "${KUBE_ROOT}/staging/src/k8s.io" && \ 562 find . -mindepth 1 -maxdepth 1 -type d | cut -c 3- | sort 563 ) 564 } 565 566 567 # Determines if docker can be run, failures may simply require that the user be added to the docker group. 568 function kube::util::ensure_docker_daemon_connectivity { 569 IFS=" " read -ra DOCKER <<< "${DOCKER_OPTS}" 570 # Expand ${DOCKER[@]} only if it's not unset. This is to work around 571 # Bash 3 issue with unbound variable. 572 DOCKER=(docker ${DOCKER[@]:+"${DOCKER[@]}"}) 573 if ! "${DOCKER[@]}" info > /dev/null 2>&1 ; then 574 cat <<'EOF' >&2 575 Can't connect to 'docker' daemon. please fix and retry. 576 577 Possible causes: 578 - Docker Daemon not started 579 - Linux: confirm via your init system 580 - macOS w/ docker-machine: run `docker-machine ls` and `docker-machine start <name>` 581 - macOS w/ Docker for Mac: Check the menu bar and start the Docker application 582 - DOCKER_HOST hasn't been set or is set incorrectly 583 - Linux: domain socket is used, DOCKER_* should be unset. In Bash run `unset ${!DOCKER_*}` 584 - macOS w/ docker-machine: run `eval "$(docker-machine env <name>)"` 585 - macOS w/ Docker for Mac: domain socket is used, DOCKER_* should be unset. In Bash run `unset ${!DOCKER_*}` 586 - Other things to check: 587 - Linux: User isn't in 'docker' group. Add and relogin. 588 - Something like 'sudo usermod -a -G docker ${USER}' 589 - RHEL7 bug and workaround: https://bugzilla.redhat.com/show_bug.cgi?id=1119282#c8 590 EOF 591 return 1 592 fi 593 } 594 595 # Wait for background jobs to finish. Return with 596 # an error status if any of the jobs failed. 597 kube::util::wait-for-jobs() { 598 local fail=0 599 local job 600 for job in $(jobs -p); do 601 wait "${job}" || fail=$((fail + 1)) 602 done 603 return ${fail} 604 } 605 606 # kube::util::join <delim> <list...> 607 # Concatenates the list elements with the delimiter passed as first parameter 608 # 609 # Ex: kube::util::join , a b c 610 # -> a,b,c 611 function kube::util::join { 612 local IFS="$1" 613 shift 614 echo "$*" 615 } 616 617 # Downloads cfssl/cfssljson into $1 directory if they do not already exist in PATH 618 # 619 # Assumed vars: 620 # $1 (cfssl directory) (optional) 621 # 622 # Sets: 623 # CFSSL_BIN: The path of the installed cfssl binary 624 # CFSSLJSON_BIN: The path of the installed cfssljson binary 625 # 626 function kube::util::ensure-cfssl { 627 if command -v cfssl &>/dev/null && command -v cfssljson &>/dev/null; then 628 CFSSL_BIN=$(command -v cfssl) 629 CFSSLJSON_BIN=$(command -v cfssljson) 630 return 0 631 fi 632 633 host_arch=$(kube::util::host_arch) 634 635 if [[ "${host_arch}" != "amd64" ]]; then 636 echo "Cannot download cfssl on non-amd64 hosts and cfssl does not appear to be installed." 637 echo "Please install cfssl and cfssljson and verify they are in \$PATH." 638 echo "Hint: export PATH=\$PATH:\$GOPATH/bin; go get -u github.com/cloudflare/cfssl/cmd/..." 639 exit 1 640 fi 641 642 # Create a temp dir for cfssl if no directory was given 643 local cfssldir=${1:-} 644 if [[ -z "${cfssldir}" ]]; then 645 kube::util::ensure-temp-dir 646 cfssldir="${KUBE_TEMP}/cfssl" 647 fi 648 649 mkdir -p "${cfssldir}" 650 pushd "${cfssldir}" > /dev/null || return 1 651 652 echo "Unable to successfully run 'cfssl' from ${PATH}; downloading instead..." 653 kernel=$(uname -s) 654 case "${kernel}" in 655 Linux) 656 curl --retry 10 -L -o cfssl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 657 curl --retry 10 -L -o cfssljson https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 658 ;; 659 Darwin) 660 curl --retry 10 -L -o cfssl https://pkg.cfssl.org/R1.2/cfssl_darwin-amd64 661 curl --retry 10 -L -o cfssljson https://pkg.cfssl.org/R1.2/cfssljson_darwin-amd64 662 ;; 663 *) 664 echo "Unknown, unsupported platform: ${kernel}." >&2 665 echo "Supported platforms: Linux, Darwin." >&2 666 exit 2 667 esac 668 669 chmod +x cfssl || true 670 chmod +x cfssljson || true 671 672 CFSSL_BIN="${cfssldir}/cfssl" 673 CFSSLJSON_BIN="${cfssldir}/cfssljson" 674 if [[ ! -x ${CFSSL_BIN} || ! -x ${CFSSLJSON_BIN} ]]; then 675 echo "Failed to download 'cfssl'. Please install cfssl and cfssljson and verify they are in \$PATH." 676 echo "Example:" 677 echo "export PATH=\$PATH:\$GOPATH/bin" 678 echo "go install github.com/cloudflare/cfssl/cmd/cfssl@latest" 679 echo "go install github.com/cloudflare/cfssl/cmd/cfssljson@latest" 680 exit 1 681 fi 682 popd > /dev/null || return 1 683 } 684 685 # kube::util::ensure_dockerized 686 # Confirms that the script is being run inside a kube-build image 687 # 688 function kube::util::ensure_dockerized { 689 if [[ -f /kube-build-image ]]; then 690 return 0 691 else 692 echo "ERROR: This script is designed to be run inside a kube-build container" 693 exit 1 694 fi 695 } 696 697 # kube::util::ensure-gnu-sed 698 # Determines which sed binary is gnu-sed on linux/darwin 699 # 700 # Sets: 701 # SED: The name of the gnu-sed binary 702 # 703 function kube::util::ensure-gnu-sed { 704 # NOTE: the echo below is a workaround to ensure sed is executed before the grep. 705 # see: https://github.com/kubernetes/kubernetes/issues/87251 706 sed_help="$(LANG=C sed --help 2>&1 || true)" 707 if echo "${sed_help}" | grep -q "GNU\|BusyBox"; then 708 SED="sed" 709 elif command -v gsed &>/dev/null; then 710 SED="gsed" 711 else 712 kube::log::error "Failed to find GNU sed as sed or gsed. If you are on Mac: brew install gnu-sed." >&2 713 return 1 714 fi 715 kube::util::sourced_variable "${SED}" 716 } 717 718 # kube::util::check-file-in-alphabetical-order <file> 719 # Check that the file is in alphabetical order 720 # 721 function kube::util::check-file-in-alphabetical-order { 722 local failure_file="$1" 723 if ! diff -u "${failure_file}" <(LC_ALL=C sort "${failure_file}"); then 724 { 725 echo 726 echo "${failure_file} is not in alphabetical order. Please sort it:" 727 echo 728 echo " LC_ALL=C sort -o ${failure_file} ${failure_file}" 729 echo 730 } >&2 731 false 732 fi 733 } 734 735 # kube::util::require-jq 736 # Checks whether jq is installed. 737 function kube::util::require-jq { 738 if ! command -v jq &>/dev/null; then 739 echo "jq not found. Please install." 1>&2 740 return 1 741 fi 742 } 743 744 # outputs md5 hash of $1, works on macOS and Linux 745 function kube::util::md5() { 746 if which md5 >/dev/null 2>&1; then 747 md5 -q "$1" 748 else 749 md5sum "$1" | awk '{ print $1 }' 750 fi 751 } 752 753 # kube::util::read-array 754 # Reads in stdin and adds it line by line to the array provided. This can be 755 # used instead of "mapfile -t", and is bash 3 compatible. 756 # 757 # Assumed vars: 758 # $1 (name of array to create/modify) 759 # 760 # Example usage: 761 # kube::util::read-array files < <(ls -1) 762 # 763 function kube::util::read-array { 764 local i=0 765 unset -v "$1" 766 while IFS= read -r "$1[i++]"; do :; done 767 eval "[[ \${$1[--i]} ]]" || unset "$1[i]" # ensures last element isn't empty 768 } 769 770 # Some useful colors. 771 if [[ -z "${color_start-}" ]]; then 772 declare -r color_start="\033[" 773 declare -r color_red="${color_start}0;31m" 774 declare -r color_yellow="${color_start}0;33m" 775 declare -r color_green="${color_start}0;32m" 776 declare -r color_blue="${color_start}1;34m" 777 declare -r color_cyan="${color_start}1;36m" 778 declare -r color_norm="${color_start}0m" 779 780 kube::util::sourced_variable "${color_start}" 781 kube::util::sourced_variable "${color_red}" 782 kube::util::sourced_variable "${color_yellow}" 783 kube::util::sourced_variable "${color_green}" 784 kube::util::sourced_variable "${color_blue}" 785 kube::util::sourced_variable "${color_cyan}" 786 kube::util::sourced_variable "${color_norm}" 787 fi 788 789 # ex: ts=2 sw=2 et filetype=sh