common.sh 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. #!/usr/bin/env bash
  2. # Copyright 2017 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. # Common utilities for kube-up/kube-down
  16. set -o errexit
  17. set -o nounset
  18. set -o pipefail
  19. KUBE_ROOT=$(cd $(dirname "${BASH_SOURCE[0]}")/.. && pwd)
  20. DEFAULT_KUBECONFIG="${HOME:-.}/.kube/config"
  21. source "${KUBE_ROOT}/hack/lib/util.sh"
  22. # KUBE_RELEASE_VERSION_REGEX matches things like "v1.2.3" or "v1.2.3-alpha.4"
  23. #
  24. # NOTE This must match the version_regex in build/common.sh
  25. # kube::release::parse_and_validate_release_version()
  26. KUBE_RELEASE_VERSION_REGEX="^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(-([a-zA-Z0-9]+)\\.(0|[1-9][0-9]*))?$"
  27. KUBE_RELEASE_VERSION_DASHED_REGEX="v(0|[1-9][0-9]*)-(0|[1-9][0-9]*)-(0|[1-9][0-9]*)(-([a-zA-Z0-9]+)-(0|[1-9][0-9]*))?"
  28. # KUBE_CI_VERSION_REGEX matches things like "v1.2.3-alpha.4.56+abcdefg" This
  29. #
  30. # NOTE This must match the version_regex in build/common.sh
  31. # kube::release::parse_and_validate_ci_version()
  32. KUBE_CI_VERSION_REGEX="^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)-([a-zA-Z0-9]+)\\.(0|[1-9][0-9]*)(\\.(0|[1-9][0-9]*)\\+[-0-9a-z]*)?$"
  33. KUBE_CI_VERSION_DASHED_REGEX="^v(0|[1-9][0-9]*)-(0|[1-9][0-9]*)-(0|[1-9][0-9]*)-([a-zA-Z0-9]+)-(0|[1-9][0-9]*)(-(0|[1-9][0-9]*)\\+[-0-9a-z]*)?"
  34. # Generate kubeconfig data for the created cluster.
  35. # Assumed vars:
  36. # KUBE_USER
  37. # KUBE_PASSWORD
  38. # KUBE_MASTER_IP
  39. # KUBECONFIG
  40. # CONTEXT
  41. #
  42. # If the apiserver supports bearer auth, also provide:
  43. # KUBE_BEARER_TOKEN
  44. #
  45. # If the kubeconfig context being created should NOT be set as the current context
  46. # SECONDARY_KUBECONFIG=true
  47. #
  48. # To explicitly name the context being created, use OVERRIDE_CONTEXT
  49. #
  50. # The following can be omitted for --insecure-skip-tls-verify
  51. # KUBE_CERT
  52. # KUBE_KEY
  53. # CA_CERT
  54. function create-kubeconfig() {
  55. KUBECONFIG=${KUBECONFIG:-$DEFAULT_KUBECONFIG}
  56. local kubectl="${KUBE_ROOT}/cluster/kubectl.sh"
  57. SECONDARY_KUBECONFIG=${SECONDARY_KUBECONFIG:-}
  58. OVERRIDE_CONTEXT=${OVERRIDE_CONTEXT:-}
  59. if [[ "$OVERRIDE_CONTEXT" != "" ]];then
  60. CONTEXT=$OVERRIDE_CONTEXT
  61. fi
  62. # KUBECONFIG determines the file we write to, but it may not exist yet
  63. OLD_IFS=$IFS
  64. IFS=':'
  65. for cfg in ${KUBECONFIG} ; do
  66. if [[ ! -e "${cfg}" ]]; then
  67. mkdir -p "$(dirname "${cfg}")"
  68. touch "${cfg}"
  69. fi
  70. done
  71. IFS=$OLD_IFS
  72. local cluster_args=(
  73. "--server=${KUBE_SERVER:-https://${KUBE_MASTER_IP}}"
  74. )
  75. if [[ -z "${CA_CERT:-}" ]]; then
  76. cluster_args+=("--insecure-skip-tls-verify=true")
  77. else
  78. cluster_args+=(
  79. "--certificate-authority=${CA_CERT}"
  80. "--embed-certs=true"
  81. )
  82. fi
  83. local user_args=()
  84. if [[ ! -z "${KUBE_BEARER_TOKEN:-}" ]]; then
  85. user_args+=(
  86. "--token=${KUBE_BEARER_TOKEN}"
  87. )
  88. elif [[ ! -z "${KUBE_USER:-}" && ! -z "${KUBE_PASSWORD:-}" ]]; then
  89. user_args+=(
  90. "--username=${KUBE_USER}"
  91. "--password=${KUBE_PASSWORD}"
  92. )
  93. fi
  94. if [[ ! -z "${KUBE_CERT:-}" && ! -z "${KUBE_KEY:-}" ]]; then
  95. user_args+=(
  96. "--client-certificate=${KUBE_CERT}"
  97. "--client-key=${KUBE_KEY}"
  98. "--embed-certs=true"
  99. )
  100. fi
  101. KUBECONFIG="${KUBECONFIG}" "${kubectl}" config set-cluster "${CONTEXT}" "${cluster_args[@]}"
  102. if [[ -n "${user_args[@]:-}" ]]; then
  103. KUBECONFIG="${KUBECONFIG}" "${kubectl}" config set-credentials "${CONTEXT}" "${user_args[@]}"
  104. fi
  105. KUBECONFIG="${KUBECONFIG}" "${kubectl}" config set-context "${CONTEXT}" --cluster="${CONTEXT}" --user="${CONTEXT}"
  106. if [[ "${SECONDARY_KUBECONFIG}" != "true" ]];then
  107. KUBECONFIG="${KUBECONFIG}" "${kubectl}" config use-context "${CONTEXT}" --cluster="${CONTEXT}"
  108. fi
  109. # If we have a bearer token, also create a credential entry with basic auth
  110. # so that it is easy to discover the basic auth password for your cluster
  111. # to use in a web browser.
  112. if [[ ! -z "${KUBE_BEARER_TOKEN:-}" && ! -z "${KUBE_USER:-}" && ! -z "${KUBE_PASSWORD:-}" ]]; then
  113. KUBECONFIG="${KUBECONFIG}" "${kubectl}" config set-credentials "${CONTEXT}-basic-auth" "--username=${KUBE_USER}" "--password=${KUBE_PASSWORD}"
  114. fi
  115. echo "Wrote config for ${CONTEXT} to ${KUBECONFIG}"
  116. }
  117. # Clear kubeconfig data for a context
  118. # Assumed vars:
  119. # KUBECONFIG
  120. # CONTEXT
  121. #
  122. # To explicitly name the context being removed, use OVERRIDE_CONTEXT
  123. function clear-kubeconfig() {
  124. export KUBECONFIG=${KUBECONFIG:-$DEFAULT_KUBECONFIG}
  125. OVERRIDE_CONTEXT=${OVERRIDE_CONTEXT:-}
  126. if [[ "$OVERRIDE_CONTEXT" != "" ]];then
  127. CONTEXT=$OVERRIDE_CONTEXT
  128. fi
  129. local kubectl="${KUBE_ROOT}/cluster/kubectl.sh"
  130. # Unset the current-context before we delete it, as otherwise kubectl errors.
  131. local cc=$("${kubectl}" config view -o jsonpath='{.current-context}')
  132. if [[ "${cc}" == "${CONTEXT}" ]]; then
  133. "${kubectl}" config unset current-context
  134. fi
  135. "${kubectl}" config unset "clusters.${CONTEXT}"
  136. "${kubectl}" config unset "users.${CONTEXT}"
  137. "${kubectl}" config unset "users.${CONTEXT}-basic-auth"
  138. "${kubectl}" config unset "contexts.${CONTEXT}"
  139. echo "Cleared config for ${CONTEXT} from ${KUBECONFIG}"
  140. }
  141. # Gets username, password for the current-context in kubeconfig, if they exist.
  142. # Assumed vars:
  143. # KUBECONFIG # if unset, defaults to global
  144. # KUBE_CONTEXT # if unset, defaults to current-context
  145. #
  146. # Vars set:
  147. # KUBE_USER
  148. # KUBE_PASSWORD
  149. #
  150. # KUBE_USER,KUBE_PASSWORD will be empty if no current-context is set, or
  151. # the current-context user does not exist or contain basicauth entries.
  152. function get-kubeconfig-basicauth() {
  153. export KUBECONFIG=${KUBECONFIG:-$DEFAULT_KUBECONFIG}
  154. local cc=$("${KUBE_ROOT}/cluster/kubectl.sh" config view -o jsonpath="{.current-context}")
  155. if [[ ! -z "${KUBE_CONTEXT:-}" ]]; then
  156. cc="${KUBE_CONTEXT}"
  157. fi
  158. local user=$("${KUBE_ROOT}/cluster/kubectl.sh" config view -o jsonpath="{.contexts[?(@.name == \"${cc}\")].context.user}")
  159. get-kubeconfig-user-basicauth "${user}"
  160. if [[ -z "${KUBE_USER:-}" || -z "${KUBE_PASSWORD:-}" ]]; then
  161. # kube-up stores username/password in a an additional kubeconfig section
  162. # suffixed with "-basic-auth". Cloudproviders like GKE store in directly
  163. # in the top level section along with the other credential information.
  164. # TODO: Handle this uniformly, either get rid of "basic-auth" or
  165. # consolidate its usage into a function across scripts in cluster/
  166. get-kubeconfig-user-basicauth "${user}-basic-auth"
  167. fi
  168. }
  169. # Sets KUBE_USER and KUBE_PASSWORD to the username and password specified in
  170. # the kubeconfig section corresponding to $1.
  171. #
  172. # Args:
  173. # $1 kubeconfig section to look for basic auth (eg: user or user-basic-auth).
  174. # Assumed vars:
  175. # KUBE_ROOT
  176. # Vars set:
  177. # KUBE_USER
  178. # KUBE_PASSWORD
  179. function get-kubeconfig-user-basicauth() {
  180. KUBE_USER=$("${KUBE_ROOT}/cluster/kubectl.sh" config view -o jsonpath="{.users[?(@.name == \"$1\")].user.username}")
  181. KUBE_PASSWORD=$("${KUBE_ROOT}/cluster/kubectl.sh" config view -o jsonpath="{.users[?(@.name == \"$1\")].user.password}")
  182. }
  183. # Generate basic auth user and password.
  184. # Vars set:
  185. # KUBE_USER
  186. # KUBE_PASSWORD
  187. function gen-kube-basicauth() {
  188. KUBE_USER=admin
  189. KUBE_PASSWORD=$(python -c 'import string,random; print("".join(random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(16)))')
  190. }
  191. # Get the bearer token for the current-context in kubeconfig if one exists.
  192. # Assumed vars:
  193. # KUBECONFIG # if unset, defaults to global
  194. # KUBE_CONTEXT # if unset, defaults to current-context
  195. #
  196. # Vars set:
  197. # KUBE_BEARER_TOKEN
  198. #
  199. # KUBE_BEARER_TOKEN will be empty if no current-context is set, or the
  200. # current-context user does not exist or contain a bearer token entry.
  201. function get-kubeconfig-bearertoken() {
  202. export KUBECONFIG=${KUBECONFIG:-$DEFAULT_KUBECONFIG}
  203. local cc=$("${KUBE_ROOT}/cluster/kubectl.sh" config view -o jsonpath="{.current-context}")
  204. if [[ ! -z "${KUBE_CONTEXT:-}" ]]; then
  205. cc="${KUBE_CONTEXT}"
  206. fi
  207. local user=$("${KUBE_ROOT}/cluster/kubectl.sh" config view -o jsonpath="{.contexts[?(@.name == \"${cc}\")].context.user}")
  208. KUBE_BEARER_TOKEN=$("${KUBE_ROOT}/cluster/kubectl.sh" config view -o jsonpath="{.users[?(@.name == \"${user}\")].user.token}")
  209. }
  210. # Generate bearer token.
  211. #
  212. # Vars set:
  213. # KUBE_BEARER_TOKEN
  214. function gen-kube-bearertoken() {
  215. KUBE_BEARER_TOKEN=$(dd if=/dev/urandom bs=128 count=1 2>/dev/null | base64 | tr -d "=+/" | dd bs=32 count=1 2>/dev/null)
  216. }
  217. function load-or-gen-kube-basicauth() {
  218. if [[ ! -z "${KUBE_CONTEXT:-}" ]]; then
  219. get-kubeconfig-basicauth
  220. fi
  221. if [[ -z "${KUBE_USER:-}" || -z "${KUBE_PASSWORD:-}" ]]; then
  222. gen-kube-basicauth
  223. fi
  224. # Make sure they don't contain any funny characters.
  225. if ! [[ "${KUBE_USER}" =~ ^[-._@a-zA-Z0-9]+$ ]]; then
  226. echo "Bad KUBE_USER string."
  227. exit 1
  228. fi
  229. if ! [[ "${KUBE_PASSWORD}" =~ ^[-._@#%/a-zA-Z0-9]+$ ]]; then
  230. echo "Bad KUBE_PASSWORD string."
  231. exit 1
  232. fi
  233. }
  234. # Sets KUBE_VERSION variable to the proper version number (e.g. "v1.0.6",
  235. # "v1.2.0-alpha.1.881+376438b69c7612") or a version' publication of the form
  236. # <path>/<version> (e.g. "release/stable",' "ci/latest-1").
  237. #
  238. # See the docs on getting builds for more information about version
  239. # publication.
  240. #
  241. # Args:
  242. # $1 version string from command line
  243. # Vars set:
  244. # KUBE_VERSION
  245. function set_binary_version() {
  246. if [[ "${1}" =~ "/" ]]; then
  247. IFS='/' read -a path <<< "${1}"
  248. if [[ "${path[0]}" == "release" ]]; then
  249. KUBE_VERSION=$(gsutil cat "gs://kubernetes-release/${1}.txt")
  250. else
  251. KUBE_VERSION=$(gsutil cat "gs://kubernetes-release-dev/${1}.txt")
  252. fi
  253. else
  254. KUBE_VERSION=${1}
  255. fi
  256. }
  257. # Search for the specified tarball in the various known output locations,
  258. # echoing the location if found.
  259. #
  260. # Assumed vars:
  261. # KUBE_ROOT
  262. #
  263. # Args:
  264. # $1 name of tarball to search for
  265. function find-tar() {
  266. local -r tarball=$1
  267. locations=(
  268. "${KUBE_ROOT}/node/${tarball}"
  269. "${KUBE_ROOT}/server/${tarball}"
  270. "${KUBE_ROOT}/_output/release-tars/${tarball}"
  271. "${KUBE_ROOT}/bazel-bin/build/release-tars/${tarball}"
  272. )
  273. location=$( (ls -t "${locations[@]}" 2>/dev/null || true) | head -1 )
  274. if [[ ! -f "${location}" ]]; then
  275. echo "!!! Cannot find ${tarball}" >&2
  276. exit 1
  277. fi
  278. echo "${location}"
  279. }
  280. # Verify and find the various tar files that we are going to use on the server.
  281. #
  282. # Assumed vars:
  283. # KUBE_ROOT
  284. # Vars set:
  285. # NODE_BINARY_TAR
  286. # SERVER_BINARY_TAR
  287. # KUBE_MANIFESTS_TAR
  288. function find-release-tars() {
  289. SERVER_BINARY_TAR=$(find-tar kubernetes-server-linux-amd64.tar.gz)
  290. if [[ "${NUM_WINDOWS_NODES}" -gt "0" ]]; then
  291. NODE_BINARY_TAR=$(find-tar kubernetes-node-windows-amd64.tar.gz)
  292. fi
  293. # This tarball is used by GCI, Ubuntu Trusty, and Container Linux.
  294. KUBE_MANIFESTS_TAR=
  295. if [[ "${MASTER_OS_DISTRIBUTION:-}" == "trusty" || "${MASTER_OS_DISTRIBUTION:-}" == "gci" || "${MASTER_OS_DISTRIBUTION:-}" == "ubuntu" ]] || \
  296. [[ "${NODE_OS_DISTRIBUTION:-}" == "trusty" || "${NODE_OS_DISTRIBUTION:-}" == "gci" || "${NODE_OS_DISTRIBUTION:-}" == "ubuntu" || "${NODE_OS_DISTRIBUTION:-}" == "custom" ]] ; then
  297. KUBE_MANIFESTS_TAR=$(find-tar kubernetes-manifests.tar.gz)
  298. fi
  299. }
  300. # Run the cfssl command to generates certificate files for etcd service, the
  301. # certificate files will save in $1 directory.
  302. #
  303. # Optional vars:
  304. # GEN_ETCD_CA_CERT (CA cert encode with base64 and ZIP compression)
  305. # GEN_ETCD_CA_KEY (CA key encode with base64)
  306. #
  307. # If GEN_ETCD_CA_CERT or GEN_ETCD_CA_KEY is not specified, it will generates certs for CA.
  308. #
  309. # Args:
  310. # $1 (the directory that certificate files to save)
  311. # $2 (the ip of etcd member)
  312. # $3 (the type of etcd certificates, must be one of client, server, peer)
  313. # $4 (the prefix of the certificate filename, default is $3)
  314. function generate-etcd-cert() {
  315. local cert_dir=${1}
  316. local member_ip=${2}
  317. local type_cert=${3}
  318. local prefix=${4:-"${type_cert}"}
  319. local GEN_ETCD_CA_CERT=${GEN_ETCD_CA_CERT:-}
  320. local GEN_ETCD_CA_KEY=${GEN_ETCD_CA_KEY:-}
  321. mkdir -p "${cert_dir}"
  322. pushd "${cert_dir}"
  323. kube::util::ensure-cfssl .
  324. if [ ! -r "ca-config.json" ]; then
  325. cat >ca-config.json <<EOF
  326. {
  327. "signing": {
  328. "default": {
  329. "expiry": "43800h"
  330. },
  331. "profiles": {
  332. "server": {
  333. "expiry": "43800h",
  334. "usages": [
  335. "signing",
  336. "key encipherment",
  337. "server auth"
  338. ]
  339. },
  340. "client": {
  341. "expiry": "43800h",
  342. "usages": [
  343. "signing",
  344. "key encipherment",
  345. "client auth"
  346. ]
  347. },
  348. "peer": {
  349. "expiry": "43800h",
  350. "usages": [
  351. "signing",
  352. "key encipherment",
  353. "server auth",
  354. "client auth"
  355. ]
  356. }
  357. }
  358. }
  359. }
  360. EOF
  361. fi
  362. if [ ! -r "ca-csr.json" ]; then
  363. cat >ca-csr.json <<EOF
  364. {
  365. "CN": "Kubernetes",
  366. "key": {
  367. "algo": "ecdsa",
  368. "size": 256
  369. },
  370. "names": [
  371. {
  372. "C": "US",
  373. "L": "CA",
  374. "O": "kubernetes.io"
  375. }
  376. ]
  377. }
  378. EOF
  379. fi
  380. if [[ -n "${GEN_ETCD_CA_CERT}" && -n "${GEN_ETCD_CA_KEY}" ]]; then
  381. echo "${ca_cert}" | base64 --decode | gunzip > ca.pem
  382. echo "${ca_key}" | base64 --decode > ca-key.pem
  383. fi
  384. if [[ ! -r "ca.pem" || ! -r "ca-key.pem" ]]; then
  385. ${CFSSL_BIN} gencert -initca ca-csr.json | ${CFSSLJSON_BIN} -bare ca -
  386. fi
  387. case "${type_cert}" in
  388. client)
  389. echo "Generate client certificates..."
  390. echo '{"CN":"client","hosts":["*"],"key":{"algo":"ecdsa","size":256}}' \
  391. | ${CFSSL_BIN} gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client - \
  392. | ${CFSSLJSON_BIN} -bare "${prefix}"
  393. ;;
  394. server)
  395. echo "Generate server certificates..."
  396. echo '{"CN":"'${member_ip}'","hosts":[""],"key":{"algo":"ecdsa","size":256}}' \
  397. | ${CFSSL_BIN} gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server -hostname="${member_ip},127.0.0.1" - \
  398. | ${CFSSLJSON_BIN} -bare "${prefix}"
  399. ;;
  400. peer)
  401. echo "Generate peer certificates..."
  402. echo '{"CN":"'${member_ip}'","hosts":[""],"key":{"algo":"ecdsa","size":256}}' \
  403. | ${CFSSL_BIN} gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=peer -hostname="${member_ip},127.0.0.1" - \
  404. | ${CFSSLJSON_BIN} -bare "${prefix}"
  405. ;;
  406. *)
  407. echo "Unknow, unsupported etcd certs type: ${type_cert}" >&2
  408. echo "Supported type: client, server, peer" >&2
  409. exit 2
  410. esac
  411. popd
  412. }
  413. # Check whether required binaries exist, prompting to download
  414. # if missing.
  415. # If KUBERNETES_SKIP_CONFIRM is set to y, we'll automatically download binaries
  416. # without prompting.
  417. function verify-kube-binaries() {
  418. if ! "${KUBE_ROOT}/cluster/kubectl.sh" version --client >&/dev/null; then
  419. echo "!!! kubectl appears to be broken or missing"
  420. download-release-binaries
  421. fi
  422. }
  423. # Check whether required release artifacts exist, prompting to download
  424. # if missing.
  425. # If KUBERNETES_SKIP_CONFIRM is set to y, we'll automatically download binaries
  426. # without prompting.
  427. function verify-release-tars() {
  428. if ! $(find-release-tars); then
  429. download-release-binaries
  430. fi
  431. }
  432. # Download release artifacts.
  433. function download-release-binaries() {
  434. get_binaries_script="${KUBE_ROOT}/cluster/get-kube-binaries.sh"
  435. local resp="y"
  436. if [[ ! "${KUBERNETES_SKIP_CONFIRM:-n}" =~ ^[yY]$ ]]; then
  437. echo "Required release artifacts appear to be missing. Do you wish to download them? [Y/n]"
  438. read resp
  439. fi
  440. if [[ "${resp}" =~ ^[nN]$ ]]; then
  441. echo "You must download release artifacts to continue. You can use "
  442. echo " ${get_binaries_script}"
  443. echo "to do this for your automatically."
  444. exit 1
  445. fi
  446. "${get_binaries_script}"
  447. }
  448. # Run pushd without stack output
  449. function pushd() {
  450. command pushd $@ > /dev/null
  451. }
  452. # Run popd without stack output
  453. function popd() {
  454. command popd $@ > /dev/null
  455. }