You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

util.sh 22 kB

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