util.sh 23 KB

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