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  }