test.sh 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  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. set -o errexit
  16. set -o nounset
  17. set -o pipefail
  18. KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../..
  19. source "${KUBE_ROOT}/hack/lib/init.sh"
  20. kube::golang::setup_env
  21. # start the cache mutation detector by default so that cache mutators will be found
  22. KUBE_CACHE_MUTATION_DETECTOR="${KUBE_CACHE_MUTATION_DETECTOR:-true}"
  23. export KUBE_CACHE_MUTATION_DETECTOR
  24. # panic the server on watch decode errors since they are considered coder mistakes
  25. KUBE_PANIC_WATCH_DECODE_ERROR="${KUBE_PANIC_WATCH_DECODE_ERROR:-true}"
  26. export KUBE_PANIC_WATCH_DECODE_ERROR
  27. # Handle case where OS has sha#sum commands, instead of shasum.
  28. if which shasum >/dev/null 2>&1; then
  29. SHA1SUM="shasum -a1"
  30. elif which sha1sum >/dev/null 2>&1; then
  31. SHA1SUM="sha1sum"
  32. else
  33. echo "Failed to find shasum or sha1sum utility." >&2
  34. exit 1
  35. fi
  36. kube::test::find_dirs() {
  37. (
  38. cd ${KUBE_ROOT}
  39. find -L . -not \( \
  40. \( \
  41. -path './_artifacts/*' \
  42. -o -path './bazel-*/*' \
  43. -o -path './_output/*' \
  44. -o -path './_gopath/*' \
  45. -o -path './cmd/kubeadm/test/*' \
  46. -o -path './contrib/podex/*' \
  47. -o -path './output/*' \
  48. -o -path './release/*' \
  49. -o -path './target/*' \
  50. -o -path './test/e2e/*' \
  51. -o -path './test/e2e_node/*' \
  52. -o -path './test/e2e_kubeadm/*' \
  53. -o -path './test/integration/*' \
  54. -o -path './third_party/*' \
  55. -o -path './staging/*' \
  56. -o -path './vendor/*' \
  57. \) -prune \
  58. \) -name '*_test.go' -print0 | xargs -0n1 dirname | sed "s|^\./|${KUBE_GO_PACKAGE}/|" | LC_ALL=C sort -u
  59. find -L . \
  60. -path './_output' -prune \
  61. -o -path './vendor/k8s.io/client-go/*' \
  62. -o -path './vendor/k8s.io/apiserver/*' \
  63. -name '*_test.go' -print0 | xargs -0n1 dirname | sed "s|^\./|${KUBE_GO_PACKAGE}/|" | LC_ALL=C sort -u
  64. # run tests for client-go
  65. find ./staging/src/k8s.io/client-go -name '*_test.go' \
  66. -name '*_test.go' -print0 | xargs -0n1 dirname | sed 's|^\./staging/src/|./vendor/|' | LC_ALL=C sort -u
  67. # run tests for apiserver
  68. find ./staging/src/k8s.io/apiserver -name '*_test.go' \
  69. -name '*_test.go' -print0 | xargs -0n1 dirname | sed 's|^\./staging/src/|./vendor/|' | LC_ALL=C sort -u
  70. # run tests for apimachinery
  71. find ./staging/src/k8s.io/apimachinery -name '*_test.go' \
  72. -name '*_test.go' -print0 | xargs -0n1 dirname | sed 's|^\./staging/src/|./vendor/|' | LC_ALL=C sort -u
  73. find ./staging/src/k8s.io/kube-aggregator -name '*_test.go' \
  74. -name '*_test.go' -print0 | xargs -0n1 dirname | sed 's|^\./staging/src/|./vendor/|' | LC_ALL=C sort -u
  75. find ./staging/src/k8s.io/apiextensions-apiserver -not \( \
  76. \( \
  77. -path '*/test/integration/*' \
  78. \) -prune \
  79. \) -name '*_test.go' \
  80. -name '*_test.go' -print0 | xargs -0n1 dirname | sed 's|^\./staging/src/|./vendor/|' | LC_ALL=C sort -u
  81. find ./staging/src/k8s.io/sample-apiserver -name '*_test.go' \
  82. -name '*_test.go' -print0 | xargs -0n1 dirname | sed 's|^\./staging/src/|./vendor/|' | LC_ALL=C sort -u
  83. find ./staging/src/k8s.io/cli-runtime -name '*_test.go' \
  84. -name '*_test.go' -print0 | xargs -0n1 dirname | sed 's|^\./staging/src/|./vendor/|' | LC_ALL=C sort -u
  85. # add legacy cloud providers tests
  86. find ./staging/src/k8s.io/legacy-cloud-providers -name '*_test.go' \
  87. -name '*_test.go' -print0 | xargs -0n1 dirname | sed 's|^\./staging/src/|./vendor/|' | LC_ALL=C sort -u
  88. )
  89. }
  90. KUBE_TIMEOUT=${KUBE_TIMEOUT:--timeout 120s}
  91. KUBE_COVER=${KUBE_COVER:-n} # set to 'y' to enable coverage collection
  92. KUBE_COVERMODE=${KUBE_COVERMODE:-atomic}
  93. # How many 'go test' instances to run simultaneously when running tests in
  94. # coverage mode.
  95. KUBE_COVERPROCS=${KUBE_COVERPROCS:-4}
  96. KUBE_RACE=${KUBE_RACE:-} # use KUBE_RACE="-race" to enable race testing
  97. # Set to the goveralls binary path to report coverage results to Coveralls.io.
  98. KUBE_GOVERALLS_BIN=${KUBE_GOVERALLS_BIN:-}
  99. # Lists of API Versions of each groups that should be tested, groups are
  100. # separated by comma, lists are separated by semicolon. e.g.,
  101. # "v1,compute/v1alpha1,experimental/v1alpha2;v1,compute/v2,experimental/v1alpha3"
  102. # FIXME: due to current implementation of a test client (see: pkg/api/testapi/testapi.go)
  103. # ONLY the last version is tested in each group.
  104. ALL_VERSIONS_CSV=$(IFS=',';echo "${KUBE_AVAILABLE_GROUP_VERSIONS[*]// /,}";IFS=$)
  105. KUBE_TEST_API_VERSIONS="${KUBE_TEST_API_VERSIONS:-${ALL_VERSIONS_CSV}}"
  106. # once we have multiple group supports
  107. # Create a junit-style XML test report in this directory if set.
  108. KUBE_JUNIT_REPORT_DIR=${KUBE_JUNIT_REPORT_DIR:-}
  109. # If KUBE_JUNIT_REPORT_DIR is unset, and ARTIFACTS is set, then have them match.
  110. if [[ -z "${KUBE_JUNIT_REPORT_DIR:-}" && -n "${ARTIFACTS:-}" ]]; then
  111. export KUBE_JUNIT_REPORT_DIR="${ARTIFACTS}"
  112. fi
  113. # Set to 'y' to keep the verbose stdout from tests when KUBE_JUNIT_REPORT_DIR is
  114. # set.
  115. KUBE_KEEP_VERBOSE_TEST_OUTPUT=${KUBE_KEEP_VERBOSE_TEST_OUTPUT:-n}
  116. kube::test::usage() {
  117. kube::log::usage_from_stdin <<EOF
  118. usage: $0 [OPTIONS] [TARGETS]
  119. OPTIONS:
  120. -p <number> : number of parallel workers, must be >= 1
  121. EOF
  122. }
  123. isnum() {
  124. [[ "$1" =~ ^[0-9]+$ ]]
  125. }
  126. PARALLEL="${PARALLEL:-1}"
  127. while getopts "hp:i:" opt ; do
  128. case ${opt} in
  129. h)
  130. kube::test::usage
  131. exit 0
  132. ;;
  133. p)
  134. PARALLEL="${OPTARG}"
  135. if ! isnum "${PARALLEL}" || [[ "${PARALLEL}" -le 0 ]]; then
  136. kube::log::usage "'$0': argument to -p must be numeric and greater than 0"
  137. kube::test::usage
  138. exit 1
  139. fi
  140. ;;
  141. i)
  142. kube::log::usage "'$0': use GOFLAGS='-count <num-iterations>'"
  143. kube::test::usage
  144. exit 1
  145. ;;
  146. ?)
  147. kube::test::usage
  148. exit 1
  149. ;;
  150. :)
  151. kube::log::usage "Option -${OPTARG} <value>"
  152. kube::test::usage
  153. exit 1
  154. ;;
  155. esac
  156. done
  157. shift $((OPTIND - 1))
  158. # Use eval to preserve embedded quoted strings.
  159. eval "testargs=(${KUBE_TEST_ARGS:-})"
  160. # Used to filter verbose test output.
  161. go_test_grep_pattern=".*"
  162. # The go-junit-report tool needs full test case information to produce a
  163. # meaningful report.
  164. if [[ -n "${KUBE_JUNIT_REPORT_DIR}" ]] ; then
  165. goflags+=(-v)
  166. # Show only summary lines by matching lines like "status package/test"
  167. go_test_grep_pattern="^[^[:space:]]\+[[:space:]]\+[^[:space:]]\+/[^[[:space:]]\+"
  168. fi
  169. if [[ -n "${FULL_LOG:-}" ]] ; then
  170. go_test_grep_pattern=".*"
  171. fi
  172. # Filter out arguments that start with "-" and move them to goflags.
  173. testcases=()
  174. for arg; do
  175. if [[ "${arg}" == -* ]]; then
  176. goflags+=("${arg}")
  177. else
  178. testcases+=("${arg}")
  179. fi
  180. done
  181. if [[ ${#testcases[@]} -eq 0 ]]; then
  182. testcases=($(kube::test::find_dirs))
  183. fi
  184. set -- "${testcases[@]+${testcases[@]}}"
  185. junitFilenamePrefix() {
  186. if [[ -z "${KUBE_JUNIT_REPORT_DIR}" ]]; then
  187. echo ""
  188. return
  189. fi
  190. mkdir -p "${KUBE_JUNIT_REPORT_DIR}"
  191. # This filename isn't parsed by anything, and we must avoid
  192. # exceeding 255 character filename limit. KUBE_TEST_API
  193. # barely fits there and in coverage mode test names are
  194. # appended to generated file names, easily exceeding
  195. # 255 chars in length. So let's just use a sha1 hash of it.
  196. local KUBE_TEST_API_HASH="$(echo -n "${KUBE_TEST_API//\//-}"| ${SHA1SUM} |awk '{print $1}')"
  197. echo "${KUBE_JUNIT_REPORT_DIR}/junit_${KUBE_TEST_API_HASH}_$(kube::util::sortable_date)"
  198. }
  199. verifyAndSuggestPackagePath() {
  200. local specified_package_path="$1"
  201. local alternative_package_path="$2"
  202. local original_package_path="$3"
  203. local suggestion_package_path="$4"
  204. if [[ "${specified_package_path}" =~ '/...'$ ]]; then
  205. specified_package_path=${specified_package_path::-4}
  206. fi
  207. if ! [ -d "${specified_package_path}" ]; then
  208. # Because k8s sets a localized $GOPATH for testing, seeing the actual
  209. # directory can be confusing. Instead, just show $GOPATH if it exists in the
  210. # $specified_package_path.
  211. local printable_package_path=$(echo "${specified_package_path}" | sed "s|${GOPATH}|\${GOPATH}|")
  212. kube::log::error "specified test path '${printable_package_path}' does not exist"
  213. if [ -d "${alternative_package_path}" ]; then
  214. kube::log::info "try changing \"${original_package_path}\" to \"${suggestion_package_path}\""
  215. fi
  216. exit 1
  217. fi
  218. }
  219. verifyPathsToPackagesUnderTest() {
  220. local packages_under_test=($@)
  221. for package_path in "${packages_under_test[@]}"; do
  222. local local_package_path="${package_path}"
  223. local go_package_path="${GOPATH}/src/${package_path}"
  224. if [[ "${package_path:0:2}" == "./" ]] ; then
  225. verifyAndSuggestPackagePath "${local_package_path}" "${go_package_path}" "${package_path}" "${package_path:2}"
  226. else
  227. verifyAndSuggestPackagePath "${go_package_path}" "${local_package_path}" "${package_path}" "./${package_path}"
  228. fi
  229. done
  230. }
  231. produceJUnitXMLReport() {
  232. local -r junit_filename_prefix=$1
  233. if [[ -z "${junit_filename_prefix}" ]]; then
  234. return
  235. fi
  236. local test_stdout_filenames
  237. local junit_xml_filename
  238. test_stdout_filenames=$(ls ${junit_filename_prefix}*.stdout)
  239. junit_xml_filename="${junit_filename_prefix}.xml"
  240. if ! command -v go-junit-report >/dev/null 2>&1; then
  241. kube::log::error "go-junit-report not found; please install with " \
  242. "go get -u github.com/jstemmer/go-junit-report"
  243. return
  244. fi
  245. cat ${test_stdout_filenames} | go-junit-report > "${junit_xml_filename}"
  246. if [[ ! ${KUBE_KEEP_VERBOSE_TEST_OUTPUT} =~ ^[yY]$ ]]; then
  247. rm ${test_stdout_filenames}
  248. fi
  249. kube::log::status "Saved JUnit XML test report to ${junit_xml_filename}"
  250. }
  251. runTests() {
  252. local junit_filename_prefix
  253. junit_filename_prefix=$(junitFilenamePrefix)
  254. verifyPathsToPackagesUnderTest "$@"
  255. # If we're not collecting coverage, run all requested tests with one 'go test'
  256. # command, which is much faster.
  257. if [[ ! ${KUBE_COVER} =~ ^[yY]$ ]]; then
  258. kube::log::status "Running tests without code coverage"
  259. go test "${goflags[@]:+${goflags[@]}}" \
  260. ${KUBE_RACE} ${KUBE_TIMEOUT} "${@}" \
  261. "${testargs[@]:+${testargs[@]}}" \
  262. | tee ${junit_filename_prefix:+"${junit_filename_prefix}.stdout"} \
  263. | grep --binary-files=text "${go_test_grep_pattern}" && rc=$? || rc=$?
  264. produceJUnitXMLReport "${junit_filename_prefix}"
  265. return ${rc}
  266. fi
  267. # Create coverage report directories.
  268. KUBE_TEST_API_HASH="$(echo -n "${KUBE_TEST_API//\//-}"| ${SHA1SUM} |awk '{print $1}')"
  269. cover_report_dir="/tmp/k8s_coverage/${KUBE_TEST_API_HASH}/$(kube::util::sortable_date)"
  270. cover_profile="coverage.out" # Name for each individual coverage profile
  271. kube::log::status "Saving coverage output in '${cover_report_dir}'"
  272. mkdir -p "${@+${@/#/${cover_report_dir}/}}"
  273. # Run all specified tests, collecting coverage results. Go currently doesn't
  274. # support collecting coverage across multiple packages at once, so we must issue
  275. # separate 'go test' commands for each package and then combine at the end.
  276. # To speed things up considerably, we can at least use xargs -P to run multiple
  277. # 'go test' commands at once.
  278. # To properly parse the test results if generating a JUnit test report, we
  279. # must make sure the output from PARALLEL runs is not mixed. To achieve this,
  280. # we spawn a subshell for each PARALLEL process, redirecting the output to
  281. # separate files.
  282. # ignore paths:
  283. # vendor/k8s.io/code-generator/cmd/generator: is fragile when run under coverage, so ignore it for now.
  284. # https://github.com/kubernetes/kubernetes/issues/24967
  285. # vendor/k8s.io/client-go/1.4/rest: causes cover internal errors
  286. # https://github.com/golang/go/issues/16540
  287. cover_ignore_dirs="vendor/k8s.io/code-generator/cmd/generator|vendor/k8s.io/client-go/1.4/rest"
  288. for path in $(echo ${cover_ignore_dirs} | sed 's/|/ /g'); do
  289. echo -e "skipped\tk8s.io/kubernetes/${path}"
  290. done
  291. printf "%s\n" "${@}" \
  292. | grep -Ev ${cover_ignore_dirs} \
  293. | xargs -I{} -n 1 -P ${KUBE_COVERPROCS} \
  294. bash -c "set -o pipefail; _pkg=\"\$0\"; _pkg_out=\${_pkg//\//_}; \
  295. go test ${goflags[@]:+${goflags[@]}} \
  296. ${KUBE_RACE} \
  297. ${KUBE_TIMEOUT} \
  298. -cover -covermode=\"${KUBE_COVERMODE}\" \
  299. -coverprofile=\"${cover_report_dir}/\${_pkg}/${cover_profile}\" \
  300. \"\${_pkg}\" \
  301. ${testargs[@]:+${testargs[@]}} \
  302. | tee ${junit_filename_prefix:+\"${junit_filename_prefix}-\$_pkg_out.stdout\"} \
  303. | grep \"${go_test_grep_pattern}\"" \
  304. {} \
  305. && test_result=$? || test_result=$?
  306. produceJUnitXMLReport "${junit_filename_prefix}"
  307. COMBINED_COVER_PROFILE="${cover_report_dir}/combined-coverage.out"
  308. {
  309. # The combined coverage profile needs to start with a line indicating which
  310. # coverage mode was used (set, count, or atomic). This line is included in
  311. # each of the coverage profiles generated when running 'go test -cover', but
  312. # we strip these lines out when combining so that there's only one.
  313. echo "mode: ${KUBE_COVERMODE}"
  314. # Include all coverage reach data in the combined profile, but exclude the
  315. # 'mode' lines, as there should be only one.
  316. for x in `find "${cover_report_dir}" -name "${cover_profile}"`; do
  317. cat ${x} | grep -h -v "^mode:" || true
  318. done
  319. } >"${COMBINED_COVER_PROFILE}"
  320. coverage_html_file="${cover_report_dir}/combined-coverage.html"
  321. go tool cover -html="${COMBINED_COVER_PROFILE}" -o="${coverage_html_file}"
  322. kube::log::status "Combined coverage report: ${coverage_html_file}"
  323. return ${test_result}
  324. }
  325. reportCoverageToCoveralls() {
  326. if [[ ${KUBE_COVER} =~ ^[yY]$ ]] && [[ -x "${KUBE_GOVERALLS_BIN}" ]]; then
  327. kube::log::status "Reporting coverage results to Coveralls for service ${CI_NAME:-}"
  328. ${KUBE_GOVERALLS_BIN} -coverprofile="${COMBINED_COVER_PROFILE}" \
  329. ${CI_NAME:+"-service=${CI_NAME}"} \
  330. ${COVERALLS_REPO_TOKEN:+"-repotoken=${COVERALLS_REPO_TOKEN}"} \
  331. || true
  332. fi
  333. }
  334. checkFDs() {
  335. # several unittests panic when httptest cannot open more sockets
  336. # due to the low default files limit on OS X. Warn about low limit.
  337. local fileslimit="$(ulimit -n)"
  338. if [[ ${fileslimit} -lt 1000 ]]; then
  339. echo "WARNING: ulimit -n (files) should be at least 1000, is ${fileslimit}, may cause test failure";
  340. fi
  341. }
  342. checkFDs
  343. # Convert the CSVs to arrays.
  344. IFS=';' read -a apiVersions <<< "${KUBE_TEST_API_VERSIONS}"
  345. apiVersionsCount=${#apiVersions[@]}
  346. for (( i=0; i<${apiVersionsCount}; i++ )); do
  347. apiVersion=${apiVersions[i]}
  348. echo "Running tests for APIVersion: ${apiVersion}"
  349. # KUBE_TEST_API sets the version of each group to be tested.
  350. KUBE_TEST_API="${apiVersion}" runTests "$@"
  351. done
  352. # We might run the tests for multiple versions, but we want to report only
  353. # one of them to coveralls. Here we report coverage from the last run.
  354. reportCoverageToCoveralls