k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/hack/lib/test.sh (about) 1 #!/usr/bin/env bash 2 3 # Copyright 2015 The Kubernetes Authors. 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # you may not use this file except in compliance with the License. 7 # You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 17 # shellcheck disable=SC2034 # Variables sourced in other scripts. 18 19 # A set of helpers for tests 20 21 reset=$(tput sgr0) 22 bold=$(tput bold) 23 black=$(tput setaf 0) 24 red=$(tput setaf 1) 25 green=$(tput setaf 2) 26 readonly reset bold black red green 27 28 kube::test::clear_all() { 29 if kube::test::if_supports_resource "rc" ; then 30 # shellcheck disable=SC2154 31 # Disabling because "kube_flags" is set in a parent script 32 kubectl delete "${kube_flags[@]}" rc --all --grace-period=0 --force 33 fi 34 if kube::test::if_supports_resource "pods" ; then 35 kubectl delete "${kube_flags[@]}" pods --all --grace-period=0 --force 36 fi 37 } 38 39 # Prints the calling file and line number $1 levels deep 40 # Defaults to 2 levels so you can call this to find your own caller 41 kube::test::get_caller() { 42 local levels=${1:-2} 43 local caller_file="${BASH_SOURCE[${levels}]}" 44 local caller_line="${BASH_LINENO[${levels}-1]}" 45 echo "$(basename "${caller_file}"):${caller_line}" 46 } 47 48 # Force exact match of a returned result for a object query. Wrap this with || to support multiple 49 # valid return types. 50 # This runs `kubectl get` once and asserts that the result is as expected. 51 # $1: Object on which get should be run 52 # $2: The go-template to run on the result 53 # $3: The expected output 54 # $4: Additional args to be passed to kubectl 55 kube::test::get_object_assert() { 56 kube::test::object_assert 1 "$@" 57 } 58 59 # Asserts that the output of a given get query is as expected. 60 # Runs the query multiple times before failing it. 61 # $1: Object on which get should be run 62 # $2: The go-template to run on the result 63 # $3: The expected output 64 # $4: Additional args to be passed to kubectl 65 kube::test::wait_object_assert() { 66 kube::test::object_assert 10 "$@" 67 } 68 69 # Asserts that the output of a given get query is as expected. 70 # Can run the query multiple times before failing it. 71 # $1: Number of times the query should be run before failing it. 72 # $2: Object on which get should be run 73 # $3: The go-template to run on the result 74 # $4: The expected output 75 # $5: Additional args to be passed to kubectl 76 kube::test::object_assert() { 77 local tries=$1 78 local object=$2 79 local request=$3 80 local expected=$4 81 local args=${5:-} 82 83 for j in $(seq 1 "${tries}"); do 84 # shellcheck disable=SC2086 85 # Disabling because to allow for expansion here 86 res=$(kubectl get "${kube_flags[@]}" ${args} ${object} -o go-template="${request}") 87 if [[ "${res}" =~ ^$expected$ ]]; then 88 echo -n "${green}" 89 echo "$(kube::test::get_caller 3): Successful get ${object} ${request}: ${res}" 90 echo -n "${reset}" 91 return 0 92 fi 93 echo "Waiting for Get ${object} ${request} ${args}: expected: ${expected}, got: ${res}" 94 sleep $((j-1)) 95 done 96 97 echo "${bold}${red}" 98 echo "$(kube::test::get_caller 3): FAIL!" 99 echo "Get ${object} ${request}" 100 echo " Expected: ${expected}" 101 echo " Got: ${res}" 102 echo "${reset}${red}" 103 caller 104 echo "${reset}" 105 return 1 106 } 107 108 kube::test::get_object_jsonpath_assert() { 109 local object=$1 110 local request=$2 111 local expected=$3 112 113 # shellcheck disable=SC2086 114 # Disabling to allow for expansion here 115 res=$(kubectl get "${kube_flags[@]}" ${object} -o jsonpath=${request}) 116 117 if [[ "${res}" =~ ^$expected$ ]]; then 118 echo -n "${green}" 119 echo "$(kube::test::get_caller): Successful get ${object} ${request}: ${res}" 120 echo -n "${reset}" 121 return 0 122 else 123 echo "${bold}${red}" 124 echo "$(kube::test::get_caller): FAIL!" 125 echo "Get ${object} ${request}" 126 echo " Expected: ${expected}" 127 echo " Got: ${res}" 128 echo "${reset}${red}" 129 caller 130 echo "${reset}" 131 return 1 132 fi 133 } 134 135 kube::test::describe_object_assert() { 136 local resource=$1 137 local object=$2 138 local matches=( "${@:3}" ) 139 140 # shellcheck disable=SC2086 141 # Disabling to allow for expansion here 142 result=$(kubectl describe "${kube_flags[@]}" ${resource} ${object}) 143 144 for match in "${matches[@]}"; do 145 if grep -q "${match}" <<< "${result}"; then 146 echo "matched ${match}" 147 else 148 echo "${bold}${red}" 149 echo "$(kube::test::get_caller): FAIL!" 150 echo "Describe ${resource} ${object}" 151 echo " Expected Match: ${match}" 152 echo " Not found in:" 153 echo "${result}" 154 echo "${reset}${red}" 155 caller 156 echo "${reset}" 157 return 1 158 fi 159 done 160 161 echo -n "${green}" 162 echo "$(kube::test::get_caller): Successful describe ${resource} ${object}:" 163 echo "${result}" 164 echo -n "${reset}" 165 return 0 166 } 167 168 kube::test::describe_object_events_assert() { 169 local resource=$1 170 local object=$2 171 local showevents=${3:-"true"} 172 173 # shellcheck disable=SC2086 174 # Disabling to allow for expansion here 175 if [[ -z "${3:-}" ]]; then 176 result=$(kubectl describe "${kube_flags[@]}" ${resource} ${object}) 177 else 178 result=$(kubectl describe "${kube_flags[@]}" "--show-events=${showevents}" ${resource} ${object}) 179 fi 180 181 if grep -q "No events.\|Events:" <<< "${result}"; then 182 local has_events="true" 183 else 184 local has_events="false" 185 fi 186 if [[ "${showevents}" == "${has_events}" ]]; then 187 echo -n "${green}" 188 echo "$(kube::test::get_caller): Successful describe" 189 echo "${result}" 190 echo "${reset}" 191 return 0 192 else 193 echo "${bold}${red}" 194 echo "$(kube::test::get_caller): FAIL" 195 if [[ "${showevents}" == "false" ]]; then 196 echo " Events information should not be described in:" 197 else 198 echo " Events information not found in:" 199 fi 200 echo "${result}" 201 echo "${reset}${red}" 202 caller 203 echo "${reset}" 204 return 1 205 fi 206 } 207 208 kube::test::describe_resource_assert() { 209 local resource=$1 210 local matches=( "${@:2}" ) 211 212 # shellcheck disable=SC2086 213 # Disabling to allow for expansion here 214 result=$(kubectl describe "${kube_flags[@]}" ${resource}) 215 216 for match in "${matches[@]}"; do 217 if grep -q "${match}" <<< "${result}"; then 218 echo "matched ${match}" 219 else 220 echo "${bold}${red}" 221 echo "FAIL!" 222 echo "Describe ${resource}" 223 echo " Expected Match: ${match}" 224 echo " Not found in:" 225 echo "${result}" 226 echo "${reset}${red}" 227 caller 228 echo "${reset}" 229 return 1 230 fi 231 done 232 233 echo -n "${green}" 234 echo "Successful describe ${resource}:" 235 echo "${result}" 236 echo -n "${reset}" 237 return 0 238 } 239 240 kube::test::describe_resource_events_assert() { 241 local resource=$1 242 local showevents=${2:-"true"} 243 244 # shellcheck disable=SC2086 245 # Disabling to allow for expansion here 246 result=$(kubectl describe "${kube_flags[@]}" "--show-events=${showevents}" ${resource}) 247 248 if grep -q "No events.\|Events:" <<< "${result}"; then 249 local has_events="true" 250 else 251 local has_events="false" 252 fi 253 if [[ "${showevents}" == "${has_events}" ]]; then 254 echo -n "${green}" 255 echo "Successful describe" 256 echo "${result}" 257 echo -n "${reset}" 258 return 0 259 else 260 echo "${bold}${red}" 261 echo "FAIL" 262 if [[ "${showevents}" == "false" ]]; then 263 echo " Events information should not be described in:" 264 else 265 echo " Events information not found in:" 266 fi 267 echo "${result}" 268 caller 269 echo "${reset}" 270 return 1 271 fi 272 } 273 274 kube::test::describe_resource_chunk_size_assert() { 275 # $1: the target resource 276 local resource=$1 277 # $2: comma-separated list of additional resources that will be listed 278 local additionalResources=${2:-} 279 # Remaining args are flags to pass to kubectl 280 local args=${3:-} 281 282 # Expect list requests for the target resource and the additional resources 283 local expectLists 284 IFS="," read -r -a expectLists <<< "${resource},${additionalResources}" 285 286 # shellcheck disable=SC2086 287 # Disabling to allow for expansion here 288 defaultResult=$(kubectl describe ${resource} --show-events=true -v=6 ${args} "${kube_flags[@]}" 2>&1 >/dev/null) 289 for r in "${expectLists[@]}"; do 290 if grep -q "${r}?.*limit=500" <<< "${defaultResult}"; then 291 echo "query for ${r} had limit param" 292 else 293 echo "${bold}${red}" 294 echo "FAIL!" 295 echo "Describe ${resource}" 296 echo " Expected limit param on request for: ${r}" 297 echo " Not found in:" 298 echo "${defaultResult}" 299 echo "${reset}${red}" 300 caller 301 echo "${reset}" 302 return 1 303 fi 304 done 305 306 # shellcheck disable=SC2086 307 # Disabling to allow for expansion here 308 # Try a non-default chunk size 309 customResult=$(kubectl describe ${resource} --show-events=false --chunk-size=10 -v=6 ${args} "${kube_flags[@]}" 2>&1 >/dev/null) 310 if grep -q "${resource}?limit=10" <<< "${customResult}"; then 311 echo "query for ${resource} had user-specified limit param" 312 else 313 echo "${bold}${red}" 314 echo "FAIL!" 315 echo "Describe ${resource}" 316 echo " Expected limit param on request for: ${r}" 317 echo " Not found in:" 318 echo "${customResult}" 319 echo "${reset}${red}" 320 caller 321 echo "${reset}" 322 return 1 323 fi 324 325 echo -n "${green}" 326 echo "Successful describe ${resource} verbose logs:" 327 echo "${defaultResult}" 328 echo -n "${reset}" 329 330 return 0 331 } 332 333 # Compare sort-by resource name output (first column, skipping first line) with expected order specify in the last parameter 334 kube::test::if_sort_by_has_correct_order() { 335 local var 336 var="$(echo "$1" | awk '{if(NR!=1) print $1}' | tr '\n' ':')" 337 kube::test::if_has_string "${var}" "${@:$#}" 338 } 339 340 kube::test::if_has_string() { 341 local message=$1 342 local match=$2 343 344 if grep -q "${match}" <<< "${message}"; then 345 echo -n "${green}" 346 echo "Successful" 347 echo -n "${reset}" 348 echo "message:${message}" 349 echo "has:${match}" 350 return 0 351 else 352 echo -n "${bold}${red}" 353 echo "FAIL!" 354 echo -n "${reset}" 355 echo "message:${message}" 356 echo "has not:${match}" 357 caller 358 return 1 359 fi 360 } 361 362 kube::test::if_has_not_string() { 363 local message=$1 364 local match=$2 365 366 if grep -q "${match}" <<< "${message}"; then 367 echo -n "${bold}${red}" 368 echo "FAIL!" 369 echo -n "${reset}" 370 echo "message:${message}" 371 echo "has:${match}" 372 caller 373 return 1 374 else 375 echo -n "${green}" 376 echo "Successful" 377 echo -n "${reset}" 378 echo "message:${message}" 379 echo "has not:${match}" 380 return 0 381 fi 382 } 383 384 kube::test::if_empty_string() { 385 local match=$1 386 if [ -n "${match}" ]; then 387 echo -n "${bold}${red}" 388 echo "FAIL!" 389 echo "${match} is not empty" 390 echo -n "${reset}" 391 caller 392 return 1 393 else 394 echo -n "${green}" 395 echo "Successful" 396 echo -n "${reset}" 397 return 0 398 fi 399 } 400 401 # Returns true if the required resource is part of supported resources. 402 # Expects env vars: 403 # SUPPORTED_RESOURCES: Array of all resources supported by the apiserver. "*" 404 # means it supports all resources. For ex: ("*") or ("rc" "*") both mean that 405 # all resources are supported. 406 # $1: Name of the resource to be tested. 407 kube::test::if_supports_resource() { 408 SUPPORTED_RESOURCES=${SUPPORTED_RESOURCES:-""} 409 REQUIRED_RESOURCE=${1:-""} 410 411 for r in "${SUPPORTED_RESOURCES[@]}"; do 412 if [[ "${r}" == "*" || "${r}" == "${REQUIRED_RESOURCE}" ]]; then 413 return 0 414 fi 415 done 416 return 1 417 } 418 419 kube::test::version::object_to_file() { 420 name=$1 421 flags=${2:-""} 422 file=$3 423 # shellcheck disable=SC2086 424 # Disabling because "flags" needs to allow for expansion here 425 kubectl version ${flags} | grep "${name} Version:" | sed -e s/"${name} Version: "/""/g > "${file}" 426 } 427 428 kube::test::version::json_object_to_file() { 429 flags=$1 430 file=$2 431 # shellcheck disable=SC2086 432 # Disabling because "flags" needs to allow for expansion here 433 kubectl version ${flags} --output json | sed -e s/' '/''/g -e s/'\"'/''/g -e s/'}'/''/g -e s/'{'/''/g -e s/'clientVersion:'/'clientVersion:,'/ -e s/'serverVersion:'/'serverVersion:,'/ | tr , '\n' > "${file}" 434 } 435 436 kube::test::version::json_client_server_object_to_file() { 437 flags=$1 438 name=$2 439 file=$3 440 # shellcheck disable=SC2086 441 # Disabling because "flags" needs to allow for expansion here 442 kubectl version ${flags} --output json | jq -r ".${name}" | sed -e s/'\"'/''/g -e s/'}'/''/g -e s/'{'/''/g -e /^$/d -e s/','/''/g -e s/':'/'='/g > "${file}" 443 } 444 445 kube::test::version::yaml_object_to_file() { 446 flags=$1 447 file=$2 448 # shellcheck disable=SC2086 449 # Disabling because "flags" needs to allow for expansion here 450 kubectl version ${flags} --output yaml | sed -e s/' '/''/g -e s/'\"'/''/g -e /^$/d > "${file}" 451 } 452 453 kube::test::version::diff_assert() { 454 local original=$1 455 local comparator=${2:-"eq"} 456 local latest=$3 457 local diff_msg=${4:-""} 458 local res="" 459 460 if [ ! -f "${original}" ]; then 461 echo "${bold}${red}" 462 echo "FAIL! ${diff_msg}" 463 echo "the file '${original}' does not exit" 464 echo "${reset}${red}" 465 caller 466 echo "${reset}" 467 return 1 468 fi 469 470 if [ ! -f "${latest}" ]; then 471 echo "${bold}${red}" 472 echo "FAIL! ${diff_msg}" 473 echo "the file '${latest}' does not exit" 474 echo "${reset}${red}" 475 caller 476 echo "${reset}" 477 return 1 478 fi 479 480 if [ "${comparator}" == "exact" ]; then 481 # Skip sorting of file content for exact comparison. 482 cp "${original}" "${original}.sorted" 483 cp "${latest}" "${latest}.sorted" 484 else 485 sort "${original}" > "${original}.sorted" 486 sort "${latest}" > "${latest}.sorted" 487 fi 488 489 if [ "${comparator}" == "eq" ] || [ "${comparator}" == "exact" ]; then 490 if [ "$(diff -iwB "${original}".sorted "${latest}".sorted)" == "" ] ; then 491 echo -n "${green}" 492 echo "Successful: ${diff_msg}" 493 echo -n "${reset}" 494 return 0 495 else 496 echo "${bold}${red}" 497 echo "FAIL! ${diff_msg}" 498 echo " Expected: " 499 cat "${original}" 500 echo " Got: " 501 cat "${latest}" 502 echo "${reset}${red}" 503 caller 504 echo "${reset}" 505 return 1 506 fi 507 else 508 if [ -n "$(diff -iwB "${original}".sorted "${latest}".sorted)" ] ; then 509 echo -n "${green}" 510 echo "Successful: ${diff_msg}" 511 echo -n "${reset}" 512 return 0 513 else 514 echo "${bold}${red}" 515 echo "FAIL! ${diff_msg}" 516 echo " Expected: " 517 cat "${original}" 518 echo " Got: " 519 cat "${latest}" 520 echo "${reset}${red}" 521 caller 522 echo "${reset}" 523 return 1 524 fi 525 fi 526 } 527 528 # Force exact match of kubectl stdout, stderr, and return code. 529 # $1: file with actual stdout 530 # $2: file with actual stderr 531 # $3: the actual return code 532 # $4: file with expected stdout 533 # $5: file with expected stderr 534 # $6: expected return code 535 # $7: additional message describing the invocation 536 kube::test::results::diff() { 537 local actualstdout=$1 538 local actualstderr=$2 539 local actualcode=$3 540 local expectedstdout=$4 541 local expectedstderr=$5 542 local expectedcode=$6 543 local message=$7 544 local result=0 545 546 if ! kube::test::version::diff_assert "${expectedstdout}" "exact" "${actualstdout}" "stdout for ${message}"; then 547 result=1 548 fi 549 if ! kube::test::version::diff_assert "${expectedstderr}" "exact" "${actualstderr}" "stderr for ${message}"; then 550 result=1 551 fi 552 if [ "${actualcode}" -ne "${expectedcode}" ]; then 553 echo "${bold}${red}" 554 echo "$(kube::test::get_caller): FAIL!" 555 echo "Return code for ${message}" 556 echo " Expected: ${expectedcode}" 557 echo " Got: ${actualcode}" 558 echo "${reset}${red}" 559 caller 560 echo "${reset}" 561 result=1 562 fi 563 564 if [ "${result}" -eq 0 ]; then 565 echo -n "${green}" 566 echo "$(kube::test::get_caller): Successful: ${message}" 567 echo -n "${reset}" 568 fi 569 570 return "$result" 571 }