PageRenderTime 29ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/cluster/mesos/docker/util.sh

https://gitlab.com/shiphitchcock3/kubernetes
Shell | 418 lines | 278 code | 66 blank | 74 comment | 29 complexity | 353cb0a32fda85a122718f79e0291caf MD5 | raw file
  1. #!/bin/bash
  2. # Copyright 2015 The Kubernetes Authors 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. # Example:
  16. # export KUBERNETES_PROVIDER=mesos/docker
  17. # go run hack/e2e.go -v -up -check_cluster_size=false
  18. # go run hack/e2e.go -v -test -check_version_skew=false
  19. # go run hack/e2e.go -v -down
  20. set -o errexit
  21. set -o nounset
  22. set -o pipefail
  23. set -o errtrace
  24. KUBE_ROOT=$(cd "$(dirname "${BASH_SOURCE}")/../../.." && pwd)
  25. provider_root="${KUBE_ROOT}/cluster/${KUBERNETES_PROVIDER}"
  26. source "${provider_root}/${KUBE_CONFIG_FILE-"config-default.sh"}"
  27. source "${KUBE_ROOT}/cluster/common.sh"
  28. # Execute a docker-compose command with the default environment and compose file.
  29. function cluster::mesos::docker::docker_compose {
  30. local params="$@"
  31. # All vars required to be set
  32. declare -a env_vars=(
  33. "KUBE_KEYGEN_TIMEOUT"
  34. "MESOS_DOCKER_ETCD_TIMEOUT"
  35. "MESOS_DOCKER_MESOS_TIMEOUT"
  36. "MESOS_DOCKER_API_TIMEOUT"
  37. "MESOS_DOCKER_ADDON_TIMEOUT"
  38. "MESOS_DOCKER_WORK_DIR"
  39. "DOCKER_DAEMON_ARGS"
  40. )
  41. (
  42. for var_name in "${env_vars[@]}"; do
  43. export ${var_name}="${!var_name}"
  44. done
  45. docker-compose -f "${provider_root}/docker-compose.yml" ${params}
  46. )
  47. }
  48. # Pull the images from a docker compose file, if they're not already cached.
  49. # This avoid slow remote calls from `docker-compose pull` which delegates
  50. # to `docker pull` which always hits the remote docker repo, even if the image
  51. # is already cached.
  52. function cluster::mesos::docker::docker_compose_lazy_pull {
  53. for img in $(grep '^\s*image:\s' "${provider_root}/docker-compose.yml" | sed 's/[ \t]*image:[ \t]*//'); do
  54. read repo tag <<<$(echo "${img} "| sed 's/:/ /')
  55. if [ -z "${tag}" ]; then
  56. tag="latest"
  57. fi
  58. if ! docker images "${repo}" | awk '{print $2;}' | grep -q "${tag}"; then
  59. docker pull "${img}"
  60. fi
  61. done
  62. }
  63. # Run kubernetes scripts inside docker.
  64. # This bypasses the need to set up network routing when running docker in a VM (e.g. boot2docker).
  65. # Trap signals and kills the docker container for better signal handing
  66. function cluster::mesos::docker::run_in_docker_test {
  67. local entrypoint="$1"
  68. if [[ "${entrypoint}" = "./"* ]]; then
  69. # relative to project root
  70. entrypoint="/go/src/github.com/GoogleCloudPlatform/kubernetes/${entrypoint}"
  71. fi
  72. shift
  73. local args="$@"
  74. # only mount KUBECONFIG if it exists, otherwise the directory will be created/owned by root
  75. kube_config_mount=""
  76. if [ -n "${KUBECONFIG:-}" ] && [ -e "${KUBECONFIG}" ]; then
  77. kube_config_mount="-v \"$(dirname ${KUBECONFIG}):/root/.kube\""
  78. fi
  79. docker run \
  80. --rm \
  81. -t $(tty &>/dev/null && echo "-i") \
  82. -e "KUBERNETES_PROVIDER=${KUBERNETES_PROVIDER}" \
  83. -v "${KUBE_ROOT}:/go/src/github.com/GoogleCloudPlatform/kubernetes" \
  84. ${kube_config_mount} \
  85. -v "/var/run/docker.sock:/var/run/docker.sock" \
  86. --link docker_mesosmaster1_1:mesosmaster1 \
  87. --link docker_apiserver_1:apiserver \
  88. --entrypoint="${entrypoint}" \
  89. mesosphere/kubernetes-mesos-test \
  90. ${args}
  91. return "$?"
  92. }
  93. # Run kube-cagen.sh inside docker.
  94. # Creating and signing in the same environment avoids a subject comparison string_mask issue.
  95. function cluster::mesos::docker::run_in_docker_cagen {
  96. local out_dir="$1"
  97. docker run \
  98. --rm \
  99. -t $(tty &>/dev/null && echo "-i") \
  100. -v "${out_dir}:/var/run/kubernetes/auth" \
  101. mesosphere/kubernetes-keygen:v1.0.0 \
  102. "cagen" \
  103. "/var/run/kubernetes/auth"
  104. return "$?"
  105. }
  106. # Run kube-keygen.sh inside docker.
  107. function cluster::mesos::docker::run_in_docker_keygen {
  108. local out_file_path="$1"
  109. local out_dir="$(dirname "${out_file_path}")"
  110. local out_file="$(basename "${out_file_path}")"
  111. docker run \
  112. --rm \
  113. -t $(tty &>/dev/null && echo "-i") \
  114. -v "${out_dir}:/var/run/kubernetes/auth" \
  115. mesosphere/kubernetes-keygen:v1.0.0 \
  116. "keygen" \
  117. "/var/run/kubernetes/auth/${out_file}"
  118. return "$?"
  119. }
  120. # Generate kubeconfig data for the created cluster.
  121. function create-kubeconfig {
  122. local -r auth_dir="${MESOS_DOCKER_WORK_DIR}/auth"
  123. local kubectl="${KUBE_ROOT}/cluster/kubectl.sh"
  124. export CONTEXT="${KUBERNETES_PROVIDER}"
  125. export KUBECONFIG=${KUBECONFIG:-$DEFAULT_KUBECONFIG}
  126. # KUBECONFIG determines the file we write to, but it may not exist yet
  127. if [[ ! -e "${KUBECONFIG}" ]]; then
  128. mkdir -p $(dirname "${KUBECONFIG}")
  129. touch "${KUBECONFIG}"
  130. fi
  131. local token="$(cut -d, -f1 ${auth_dir}/token-users)"
  132. "${kubectl}" config set-cluster "${CONTEXT}" --server="${KUBE_SERVER}" --certificate-authority="${auth_dir}/root-ca.crt"
  133. "${kubectl}" config set-context "${CONTEXT}" --cluster="${CONTEXT}" --user="cluster-admin"
  134. "${kubectl}" config set-credentials cluster-admin --token="${token}"
  135. "${kubectl}" config use-context "${CONTEXT}" --cluster="${CONTEXT}"
  136. echo "Wrote config for ${CONTEXT} to ${KUBECONFIG}" 1>&2
  137. }
  138. # Perform preparations required to run e2e tests
  139. function prepare-e2e {
  140. echo "TODO: prepare-e2e" 1>&2
  141. }
  142. # Execute prior to running tests to build a release if required for env
  143. function test-build-release {
  144. # Make a release
  145. export KUBERNETES_CONTRIB=mesos
  146. export KUBE_RELEASE_RUN_TESTS=N
  147. "${KUBE_ROOT}/build/release.sh"
  148. }
  149. # Must ensure that the following ENV vars are set
  150. function detect-master {
  151. # echo "KUBE_MASTER: $KUBE_MASTER" 1>&2
  152. local docker_id=$(docker ps --filter="name=docker_apiserver" --quiet)
  153. if [[ "${docker_id}" == *'\n'* ]]; then
  154. echo "ERROR: Multiple API Servers running" 1>&2
  155. return 1
  156. fi
  157. master_ip=$(docker inspect --format="{{.NetworkSettings.IPAddress}}" "${docker_id}")
  158. master_port=6443
  159. KUBE_MASTER_IP="${master_ip}:${master_port}"
  160. KUBE_SERVER="https://${KUBE_MASTER_IP}"
  161. echo "KUBE_MASTER_IP: $KUBE_MASTER_IP" 1>&2
  162. }
  163. # Get minion IP addresses and store in KUBE_NODE_IP_ADDRESSES[]
  164. # These Mesos slaves MAY host Kublets,
  165. # but might not have a Kublet running unless a kubernetes task has been scheduled on them.
  166. function detect-nodes {
  167. local docker_ids=$(docker ps --filter="name=docker_mesosslave" --quiet)
  168. if [ -z "${docker_ids}" ]; then
  169. echo "ERROR: Mesos slave(s) not running" 1>&2
  170. return 1
  171. fi
  172. while read -r docker_id; do
  173. local minion_ip=$(docker inspect --format="{{.NetworkSettings.IPAddress}}" "${docker_id}")
  174. KUBE_NODE_IP_ADDRESSES+=("${minion_ip}")
  175. done <<< "$docker_ids"
  176. echo "KUBE_NODE_IP_ADDRESSES: [${KUBE_NODE_IP_ADDRESSES[*]}]" 1>&2
  177. }
  178. # Verify prereqs on host machine
  179. function verify-prereqs {
  180. echo "Verifying required commands" 1>&2
  181. hash docker 2>/dev/null || { echo "Missing required command: docker" 1>&2; exit 1; }
  182. hash docker 2>/dev/null || { echo "Missing required command: docker-compose" 1>&2; exit 1; }
  183. }
  184. # Initialize
  185. function cluster::mesos::docker::init_auth {
  186. local -r auth_dir="${MESOS_DOCKER_WORK_DIR}/auth"
  187. #TODO(karlkfi): reuse existing credentials/certs/keys
  188. # Nuke old auth
  189. echo "Creating Auth Dir: ${auth_dir}" 1>&2
  190. mkdir -p "${auth_dir}"
  191. rm -rf "${auth_dir}"/*
  192. echo "Creating Certificate Authority" 1>&2
  193. cluster::mesos::docker::buffer_output cluster::mesos::docker::run_in_docker_cagen "${auth_dir}"
  194. echo "Certificate Authority Key: ${auth_dir}/root-ca.key" 1>&2
  195. echo "Certificate Authority Cert: ${auth_dir}/root-ca.crt" 1>&2
  196. echo "Creating Service Account RSA Key" 1>&2
  197. cluster::mesos::docker::buffer_output cluster::mesos::docker::run_in_docker_keygen "${auth_dir}/service-accounts.key"
  198. echo "Service Account Key: ${auth_dir}/service-accounts.key" 1>&2
  199. echo "Creating User Accounts" 1>&2
  200. cluster::mesos::docker::create_token_user "cluster-admin" > "${auth_dir}/token-users"
  201. echo "Token Users: ${auth_dir}/token-users" 1>&2
  202. cluster::mesos::docker::create_basic_user "admin" "admin" > "${auth_dir}/basic-users"
  203. echo "Basic-Auth Users: ${auth_dir}/basic-users" 1>&2
  204. }
  205. # Instantiate a kubernetes cluster.
  206. function kube-up {
  207. # Nuke old mesos-slave workspaces
  208. local work_dir="${MESOS_DOCKER_WORK_DIR}/mesosslave"
  209. echo "Creating Mesos Work Dir: ${work_dir}" 1>&2
  210. mkdir -p "${work_dir}"
  211. rm -rf "${work_dir}"/*
  212. # Nuke old logs
  213. local -r log_dir="${MESOS_DOCKER_WORK_DIR}/log"
  214. mkdir -p "${log_dir}"
  215. rm -rf "${log_dir}"/*
  216. # Pull before `docker-compose up` to avoid timeouts caused by slow pulls during deployment.
  217. echo "Pulling Docker images" 1>&2
  218. cluster::mesos::docker::docker_compose_lazy_pull
  219. if [ "${MESOS_DOCKER_SKIP_BUILD}" != "true" ]; then
  220. echo "Building Docker images" 1>&2
  221. # TODO: version images (k8s version, git sha, and dirty state) to avoid re-building them every time.
  222. "${provider_root}/km/build.sh"
  223. "${provider_root}/test/build.sh"
  224. fi
  225. cluster::mesos::docker::init_auth
  226. # Dump logs on premature exit (errexit triggers exit).
  227. # Trap EXIT instead of ERR, because ERR can trigger multiple times with errtrace enabled.
  228. trap "cluster::mesos::docker::dump_logs '${log_dir}'" EXIT
  229. echo "Starting ${KUBERNETES_PROVIDER} cluster" 1>&2
  230. cluster::mesos::docker::docker_compose up -d
  231. echo "Scaling ${KUBERNETES_PROVIDER} cluster to ${NUM_NODES} slaves"
  232. cluster::mesos::docker::docker_compose scale mesosslave=${NUM_NODES}
  233. # await-health-check requires GNU timeout
  234. # apiserver hostname resolved by docker
  235. cluster::mesos::docker::run_in_docker_test await-health-check "-t=${MESOS_DOCKER_API_TIMEOUT}" http://apiserver:8888/healthz
  236. detect-master
  237. detect-nodes
  238. create-kubeconfig
  239. echo "Deploying Addons" 1>&2
  240. KUBE_SERVER=${KUBE_SERVER} "${provider_root}/deploy-addons.sh"
  241. # Wait for addons to deploy
  242. cluster::mesos::docker::await_ready "kube-dns" "${MESOS_DOCKER_ADDON_TIMEOUT}"
  243. cluster::mesos::docker::await_ready "kubernetes-dashboard" "${MESOS_DOCKER_ADDON_TIMEOUT}"
  244. trap - EXIT
  245. }
  246. function validate-cluster {
  247. echo "Validating ${KUBERNETES_PROVIDER} cluster" 1>&2
  248. # Do not validate cluster size. There will be zero k8s minions until a pod is created.
  249. # TODO(karlkfi): use componentstatuses or equivalent when it supports non-localhost core components
  250. # Validate immediate cluster reachability and responsiveness
  251. echo "KubeDNS: $(cluster::mesos::docker::addon_status 'kube-dns')"
  252. echo "Kubernetes Dashboard: $(cluster::mesos::docker::addon_status 'kubernetes-dashboard')"
  253. }
  254. # Delete a kubernetes cluster
  255. function kube-down {
  256. if [ "${MESOS_DOCKER_DUMP_LOGS}" == "true" ]; then
  257. cluster::mesos::docker::dump_logs "${MESOS_DOCKER_WORK_DIR}/log"
  258. fi
  259. echo "Stopping ${KUBERNETES_PROVIDER} cluster" 1>&2
  260. # Since restoring a stopped cluster is not yet supported, use the nuclear option
  261. cluster::mesos::docker::docker_compose kill
  262. cluster::mesos::docker::docker_compose rm -f
  263. }
  264. function test-setup {
  265. echo "test-setup" 1>&2
  266. "${KUBE_ROOT}/cluster/kube-up.sh"
  267. }
  268. # Execute after running tests to perform any required clean-up
  269. function test-teardown {
  270. echo "test-teardown" 1>&2
  271. kube-down
  272. }
  273. ## Below functions used by hack/e2e-suite/services.sh
  274. # SSH to a node by name or IP ($1) and run a command ($2).
  275. function ssh-to-node {
  276. echo "TODO: ssh-to-node" 1>&2
  277. }
  278. # Restart the kube-proxy on a node ($1)
  279. function restart-kube-proxy {
  280. echo "TODO: restart-kube-proxy" 1>&2
  281. }
  282. # Restart the apiserver
  283. function restart-apiserver {
  284. echo "TODO: restart-apiserver" 1>&2
  285. }
  286. # Waits for a kube-system pod (of the provided name) to have the phase/status "Running".
  287. function cluster::mesos::docker::await_ready {
  288. local pod_name="$1"
  289. local max_attempts="$2"
  290. local phase="Unknown"
  291. echo -n "${pod_name}: "
  292. local n=0
  293. until [ ${n} -ge ${max_attempts} ]; do
  294. phase=$(cluster::mesos::docker::addon_status "${pod_name}")
  295. if [ "${phase}" == "Running" ]; then
  296. break
  297. fi
  298. echo -n "."
  299. n=$[$n+1]
  300. sleep 1
  301. done
  302. echo "${phase}"
  303. return $([ "${phase}" == "Running" ]; echo $?)
  304. }
  305. # Prints the status of the kube-system pod specified
  306. function cluster::mesos::docker::addon_status {
  307. local pod_name="$1"
  308. local kubectl="${KUBE_ROOT}/cluster/kubectl.sh"
  309. local phase=$("${kubectl}" get pods --namespace=kube-system -l k8s-app=${pod_name} -o template --template="{{(index .items 0).status.phase}}" 2>/dev/null)
  310. phase="${phase:-Unknown}"
  311. echo "${phase}"
  312. }
  313. function cluster::mesos::docker::dump_logs {
  314. local out_dir="$1"
  315. echo "Dumping logs to '${out_dir}'" 1>&2
  316. mkdir -p "${out_dir}"
  317. while read name; do
  318. docker logs "${name}" &> "${out_dir}/${name}.log"
  319. done < <(cluster::mesos::docker::docker_compose ps -q | xargs docker inspect --format '{{.Name}}')
  320. }
  321. # Creates a k8s token auth user file.
  322. # See /docs/admin/authentication.md
  323. function cluster::mesos::docker::create_token_user {
  324. local user_name="$1"
  325. echo "$(openssl rand -hex 32),${user_name},${user_name}"
  326. }
  327. # Creates a k8s basic auth user file.
  328. # See /docs/admin/authentication.md
  329. function cluster::mesos::docker::create_basic_user {
  330. local user_name="$1"
  331. local password="$2"
  332. echo "${password},${user_name},${user_name}"
  333. }
  334. # Buffers command output to file, prints output on failure.
  335. function cluster::mesos::docker::buffer_output {
  336. local cmd="$@"
  337. local tempfile="$(mktemp "${TMPDIR:-/tmp}/buffer.XXXXXX")"
  338. trap "kill -TERM \${PID}; rm '${tempfile}'" TERM INT
  339. set +e
  340. ${cmd} &> "${tempfile}" &
  341. PID=$!
  342. wait ${PID}
  343. trap - TERM INT
  344. wait ${PID}
  345. local exit_status="$?"
  346. set -e
  347. if [ "${exit_status}" != 0 ]; then
  348. cat "${tempfile}" 1>&2
  349. fi
  350. rm "${tempfile}"
  351. return "${exit_status}"
  352. }