k8s.io/test-infra@v0.0.0-20240520184403-27c6b4c223d8/experiment/kind-multizone-e2e.sh (about)

     1  #!/bin/sh
     2  # Copyright 2018 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  
    16  # hack script for running a kind e2e
    17  # must be run with a kubernetes checkout in $PWD (IE from the checkout)
    18  # Usage: SKIP="ginkgo skip regex" FOCUS="ginkgo focus regex" kind-e2e.sh
    19  
    20  set -o errexit -o nounset -o xtrace
    21  
    22  # Settings:
    23  # SKIP: ginkgo skip regex
    24  # FOCUS: ginkgo focus regex
    25  # GA_ONLY: true  - limit to GA APIs/features as much as possible
    26  #          false - (default) APIs and features left at defaults
    27  # 
    28  
    29  # cleanup logic for cleanup on exit
    30  CLEANED_UP=false
    31  cleanup() {
    32    if [ "$CLEANED_UP" = "true" ]; then
    33      return
    34    fi
    35    # KIND_CREATE_ATTEMPTED is true once we: kind create
    36    if [ "${KIND_CREATE_ATTEMPTED:-}" = true ]; then
    37      kind "export" logs "${ARTIFACTS}/logs" || true
    38      kind delete cluster || true
    39    fi
    40    rm -f _output/bin/e2e.test || true
    41    # remove our tempdir, this needs to be last, or it will prevent kind delete
    42    if [ -n "${TMP_DIR:-}" ]; then
    43      rm -rf "${TMP_DIR:?}"
    44    fi
    45    CLEANED_UP=true
    46  }
    47  
    48  # setup signal handlers
    49  signal_handler() {
    50    if [ -n "${GINKGO_PID:-}" ]; then
    51      kill -TERM "$GINKGO_PID" || true
    52    fi
    53    cleanup
    54  }
    55  trap signal_handler INT TERM
    56  
    57  # build kubernetes / node image, e2e binaries and ginkgo
    58  build() {
    59    # build the node image w/ kubernetes
    60    kind build node-image -v 1
    61    # Ginkgo v1 is used by Kubernetes 1.24 and earlier and exists in the vendor directory.
    62    # Historically it has been built with the "vendor" prefix.
    63    GINKGO_TARGET="vendor/github.com/onsi/ginkgo/ginkgo"
    64    if [ ! -d "$GINKGO_TARGET" ]; then
    65        # If the directory doesn't exist, then we must be on Kubernetes >= 1.25 with Ginkgo V2.
    66        # The "vendor" prefix is no longer needed.
    67        GINKGO_TARGET="github.com/onsi/ginkgo/v2/ginkgo"
    68    fi
    69    # make sure we have e2e requirements
    70    make all WHAT="cmd/kubectl test/e2e/e2e.test ${GINKGO_TARGET}"
    71  }
    72  
    73  check_structured_log_support() {
    74  	case "${KUBE_VERSION}" in
    75  		v1.1[0-8].*)
    76  			echo "$1 is only supported on versions >= v1.19, got ${KUBE_VERSION}"
    77  			exit 1
    78  			;;
    79  	esac
    80  }
    81  
    82  # up a cluster with kind
    83  create_cluster() {
    84    # Grab the version of the cluster we're about to start
    85    KUBE_VERSION="$(docker run --rm --entrypoint=cat "kindest/node:latest" /kind/version)"
    86  
    87    # Default Log level for all components in test clusters
    88    KIND_CLUSTER_LOG_LEVEL=${KIND_CLUSTER_LOG_LEVEL:-4}
    89  
    90    # potentially enable --logging-format
    91    CLUSTER_LOG_FORMAT=${CLUSTER_LOG_FORMAT:-}
    92    scheduler_extra_args="      \"v\": \"${KIND_CLUSTER_LOG_LEVEL}\""
    93    controllerManager_extra_args="      \"v\": \"${KIND_CLUSTER_LOG_LEVEL}\""
    94    apiServer_extra_args="      \"v\": \"${KIND_CLUSTER_LOG_LEVEL}\""
    95    if [ -n "$CLUSTER_LOG_FORMAT" ]; then
    96        check_structured_log_support "CLUSTER_LOG_FORMAT"
    97        scheduler_extra_args="${scheduler_extra_args}
    98        \"logging-format\": \"${CLUSTER_LOG_FORMAT}\""
    99        controllerManager_extra_args="${controllerManager_extra_args}
   100        \"logging-format\": \"${CLUSTER_LOG_FORMAT}\""
   101        apiServer_extra_args="${apiServer_extra_args}
   102        \"logging-format\": \"${CLUSTER_LOG_FORMAT}\""
   103    fi
   104    kubelet_extra_args="      \"v\": \"${KIND_CLUSTER_LOG_LEVEL}\""
   105    KUBELET_LOG_FORMAT=${KUBELET_LOG_FORMAT:-$CLUSTER_LOG_FORMAT}
   106    if [ -n "$KUBELET_LOG_FORMAT" ]; then
   107        check_structured_log_support "KUBECTL_LOG_FORMAT"
   108        kubelet_extra_args="${kubelet_extra_args}
   109        \"logging-format\": \"${KUBELET_LOG_FORMAT}\""
   110    fi
   111  
   112    # JSON map injected into featureGates config
   113    feature_gates="{}"
   114    # --runtime-config argument value passed to the API server
   115    runtime_config="{}"
   116  
   117    case "${GA_ONLY:-false}" in
   118    false)
   119      feature_gates="{}"
   120      runtime_config="{}"
   121      ;;
   122    true)
   123      case "${KUBE_VERSION}" in
   124      v1.1[0-7].*)
   125        echo "GA_ONLY=true is only supported on versions >= v1.18, got ${KUBE_VERSION}"
   126        exit 1
   127        ;;
   128      v1.18.*)
   129        echo "Limiting to GA APIs and features (plus certificates.k8s.io/v1beta1 and RotateKubeletClientCertificate) for ${KUBE_VERSION}"
   130        feature_gates='{"AllAlpha":false,"AllBeta":false,"RotateKubeletClientCertificate":true}'
   131        runtime_config='{"api/alpha":"false", "api/beta":"false", "certificates.k8s.io/v1beta1":"true"}'
   132        ;;
   133      *)
   134        echo "Limiting to GA APIs and features for ${KUBE_VERSION}"
   135        feature_gates='{"AllAlpha":false,"AllBeta":false}'
   136        runtime_config='{"api/alpha":"false", "api/beta":"false"}'
   137        ;;
   138      esac
   139      ;;
   140    *)
   141      echo "\$GA_ONLY set to '${GA_ONLY}'; supported values are true and false (default)"
   142      exit 1
   143      ;;
   144    esac
   145  
   146    # create the config file with 2 nodes per zone
   147    cat <<EOF > "${ARTIFACTS}/kind-config.yaml"
   148  kind: Cluster
   149  apiVersion: kind.x-k8s.io/v1alpha4
   150  featureGates:
   151    TopologyAwareHints: true
   152    ServiceTrafficDistribution: true
   153  nodes:
   154  - role: control-plane
   155  - role: worker
   156    kubeadmConfigPatches:
   157    - |
   158      kind: JoinConfiguration
   159      nodeRegistration:
   160        kubeletExtraArgs:
   161          node-labels: "topology.kubernetes.io/zone=zone-a"
   162  - role: worker
   163    kubeadmConfigPatches:
   164    - |
   165      kind: JoinConfiguration
   166      nodeRegistration:
   167        kubeletExtraArgs:
   168          node-labels: "topology.kubernetes.io/zone=zone-a"
   169  - role: worker
   170    kubeadmConfigPatches:
   171    - |
   172      kind: JoinConfiguration
   173      nodeRegistration:
   174        kubeletExtraArgs:
   175          node-labels: "topology.kubernetes.io/zone=zone-b"
   176  - role: worker
   177    kubeadmConfigPatches:
   178    - |
   179      kind: JoinConfiguration
   180      nodeRegistration:
   181        kubeletExtraArgs:
   182          node-labels: "topology.kubernetes.io/zone=zone-b"
   183  - role: worker
   184    kubeadmConfigPatches:
   185    - |
   186      kind: JoinConfiguration
   187      nodeRegistration:
   188        kubeletExtraArgs:
   189          node-labels: "topology.kubernetes.io/zone=zone-c"
   190  - role: worker
   191    kubeadmConfigPatches:
   192    - |
   193      kind: JoinConfiguration
   194      nodeRegistration:
   195        kubeletExtraArgs:
   196          node-labels: "topology.kubernetes.io/zone=zone-c"
   197  EOF
   198    # NOTE: must match the number of workers above
   199    NUM_NODES=6
   200    # actually create the cluster
   201    # TODO(BenTheElder): settle on verbosity for this script
   202    KIND_CREATE_ATTEMPTED=true
   203    kind create cluster \
   204      --image=kindest/node:latest \
   205      --retain \
   206      --wait=1m \
   207      -v=3 \
   208      "--config=${ARTIFACTS}/kind-config.yaml"
   209  
   210    # Patch kube-proxy to set the verbosity level
   211    kubectl patch -n kube-system daemonset/kube-proxy \
   212      --type='json' -p='[{"op": "add", "path": "/spec/template/spec/containers/0/command/-", "value": "--v='"${KIND_CLUSTER_LOG_LEVEL}"'" }]'
   213  }
   214  
   215  # run e2es with ginkgo-e2e.sh
   216  run_tests() {
   217    # IPv6 clusters need some CoreDNS changes in order to work in k8s CI:
   218    # 1. k8s CI doesn´t offer IPv6 connectivity, so CoreDNS should be configured
   219    # to work in an offline environment:
   220    # https://github.com/coredns/coredns/issues/2494#issuecomment-457215452
   221    # 2. k8s CI adds following domains to resolv.conf search field:
   222    # c.k8s-prow-builds.internal google.internal.
   223    # CoreDNS should handle those domains and answer with NXDOMAIN instead of SERVFAIL
   224    # otherwise pods stops trying to resolve the domain.
   225    if [ "${IP_FAMILY:-ipv4}" = "ipv6" ]; then
   226      # Get the current config
   227      original_coredns=$(kubectl get -oyaml -n=kube-system configmap/coredns)
   228      echo "Original CoreDNS config:"
   229      echo "${original_coredns}"
   230      # Patch it
   231      fixed_coredns=$(
   232        printf '%s' "${original_coredns}" | sed \
   233          -e 's/^.*kubernetes cluster\.local/& internal/' \
   234          -e '/^.*upstream$/d' \
   235          -e '/^.*fallthrough.*$/d' \
   236          -e '/^.*forward . \/etc\/resolv.conf$/d' \
   237          -e '/^.*loop$/d' \
   238      )
   239      echo "Patched CoreDNS config:"
   240      echo "${fixed_coredns}"
   241      printf '%s' "${fixed_coredns}" | kubectl apply -f -
   242    fi
   243  
   244    # ginkgo regexes
   245    SKIP="${SKIP:-}"
   246    FOCUS="${FOCUS:-"\\[Conformance\\]"}"
   247    # if we set PARALLEL=true, skip serial tests set --ginkgo-parallel
   248    if [ "${PARALLEL:-false}" = "true" ]; then
   249      export GINKGO_PARALLEL=y
   250      if [ -z "${SKIP}" ]; then
   251        SKIP="\\[Serial\\]"
   252      else
   253        SKIP="\\[Serial\\]|${SKIP}"
   254      fi
   255    fi
   256  
   257    # setting this env prevents ginkgo e2e from trying to run provider setup
   258    export KUBERNETES_CONFORMANCE_TEST='y'
   259    # setting these is required to make RuntimeClass tests work ... :/
   260    export KUBE_CONTAINER_RUNTIME_ENDPOINT=unix:///run/containerd/containerd.sock
   261    export KUBE_CONTAINER_RUNTIME_NAME=containerd
   262    # ginkgo can take forever to exit, so we run it in the background and save the
   263    # PID, bash will not run traps while waiting on a process, but it will while
   264    # running a builtin like `wait`, saving the PID also allows us to forward the
   265    # interrupt
   266    ./hack/ginkgo-e2e.sh \
   267      '--provider=skeleton' "--num-nodes=${NUM_NODES}" \
   268      "--ginkgo.focus=${FOCUS}" "--ginkgo.skip=${SKIP}" \
   269      "--report-dir=${ARTIFACTS}" '--disable-log-dump=true' &
   270    GINKGO_PID=$!
   271    wait "$GINKGO_PID"
   272  }
   273  
   274  main() {
   275    # create temp dir and setup cleanup
   276    TMP_DIR=$(mktemp -d)
   277  
   278    # ensure artifacts (results) directory exists when not in CI
   279    export ARTIFACTS="${ARTIFACTS:-${PWD}/_artifacts}"
   280    mkdir -p "${ARTIFACTS}"
   281  
   282    # export the KUBECONFIG to a unique path for testing
   283    KUBECONFIG="${HOME}/.kube/kind-test-config"
   284    export KUBECONFIG
   285    echo "exported KUBECONFIG=${KUBECONFIG}"
   286  
   287    # debug kind version
   288    kind version
   289  
   290    # build kubernetes
   291    build
   292    # in CI attempt to release some memory after building
   293    if [ -n "${KUBETEST_IN_DOCKER:-}" ]; then
   294      sync || true
   295      echo 1 > /proc/sys/vm/drop_caches || true
   296    fi
   297  
   298    # create the cluster and run tests
   299    res=0
   300    create_cluster || res=$?
   301    run_tests || res=$?
   302    cleanup || res=$?
   303    exit $res
   304  }
   305  
   306  main