...
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
21reset=$(tput sgr0)
22bold=$(tput bold)
23black=$(tput setaf 0)
24red=$(tput setaf 1)
25green=$(tput setaf 2)
26readonly reset bold black red green
27
28kube::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
41kube::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
55kube::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
65kube::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
76kube::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
108kube::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
135kube::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
168kube::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
208kube::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
240kube::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
274kube::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
334kube::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
340kube::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
362kube::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
384kube::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.
407kube::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
419kube::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
428kube::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
436kube::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
445kube::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
453kube::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
536kube::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}
View as plain text