k8s.io/test-infra@v0.0.0-20240520184403-27c6b4c223d8/experiment/kind-detect-local-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
   147    cat <<EOF > "${ARTIFACTS}/kind-config.yaml"
   148  # config for 1 control plane node and 2 workers (necessary for conformance)
   149  kind: Cluster
   150  apiVersion: kind.x-k8s.io/v1alpha4
   151  networking:
   152    ipFamily: ${IP_FAMILY:-ipv4}
   153    kubeProxyMode: ${KUBE_PROXY_MODE:-iptables}
   154  nodes:
   155  - role: control-plane
   156  - role: worker
   157  - role: worker
   158  featureGates: ${feature_gates}
   159  runtimeConfig: ${runtime_config}
   160  kubeadmConfigPatches:
   161  - |
   162    kind: ClusterConfiguration
   163    metadata:
   164      name: config
   165    apiServer:
   166      extraArgs:
   167  ${apiServer_extra_args}
   168    controllerManager:
   169      extraArgs:
   170  ${controllerManager_extra_args}
   171    scheduler:
   172      extraArgs:
   173  ${scheduler_extra_args}
   174    ---
   175    kind: InitConfiguration
   176    nodeRegistration:
   177      kubeletExtraArgs:
   178  ${kubelet_extra_args}
   179    ---
   180    kind: JoinConfiguration
   181    nodeRegistration:
   182      kubeletExtraArgs:
   183  ${kubelet_extra_args}
   184    ---
   185    kind: KubeProxyConfiguration
   186    detectLocalMode: ${KUBE_PROXY_DETECT_LOCAL_MODE:-ClusterCIDR}
   187    detectLocal:
   188      interfaceNamePrefix: veth # used only with detectLocalMode "InterfaceNamePrefix"
   189  EOF
   190    # NOTE: must match the number of workers above
   191    NUM_NODES=2
   192    # actually create the cluster
   193    # TODO(BenTheElder): settle on verbosity for this script
   194    KIND_CREATE_ATTEMPTED=true
   195    kind create cluster \
   196      --image=kindest/node:latest \
   197      --retain \
   198      --wait=1m \
   199      -v=3 \
   200      "--config=${ARTIFACTS}/kind-config.yaml"
   201  
   202    # Patch kube-proxy to set the verbosity level
   203    kubectl patch -n kube-system daemonset/kube-proxy \
   204      --type='json' -p='[{"op": "add", "path": "/spec/template/spec/containers/0/command/-", "value": "--v='"${KIND_CLUSTER_LOG_LEVEL}"'" }]'
   205  }
   206  
   207  # run e2es with ginkgo-e2e.sh
   208  run_tests() {
   209    # IPv6 clusters need some CoreDNS changes in order to work in k8s CI:
   210    # 1. k8s CI doesn´t offer IPv6 connectivity, so CoreDNS should be configured
   211    # to work in an offline environment:
   212    # https://github.com/coredns/coredns/issues/2494#issuecomment-457215452
   213    # 2. k8s CI adds following domains to resolv.conf search field:
   214    # c.k8s-prow-builds.internal google.internal.
   215    # CoreDNS should handle those domains and answer with NXDOMAIN instead of SERVFAIL
   216    # otherwise pods stops trying to resolve the domain.
   217    if [ "${IP_FAMILY:-ipv4}" = "ipv6" ]; then
   218      # Get the current config
   219      original_coredns=$(kubectl get -oyaml -n=kube-system configmap/coredns)
   220      echo "Original CoreDNS config:"
   221      echo "${original_coredns}"
   222      # Patch it
   223      fixed_coredns=$(
   224        printf '%s' "${original_coredns}" | sed \
   225          -e 's/^.*kubernetes cluster\.local/& internal/' \
   226          -e '/^.*upstream$/d' \
   227          -e '/^.*fallthrough.*$/d' \
   228          -e '/^.*forward . \/etc\/resolv.conf$/d' \
   229          -e '/^.*loop$/d' \
   230      )
   231      echo "Patched CoreDNS config:"
   232      echo "${fixed_coredns}"
   233      printf '%s' "${fixed_coredns}" | kubectl apply -f -
   234    fi
   235  
   236    # ginkgo regexes
   237    SKIP="${SKIP:-}"
   238    FOCUS="${FOCUS:-"\\[Conformance\\]"}"
   239    # if we set PARALLEL=true, skip serial tests set --ginkgo-parallel
   240    if [ "${PARALLEL:-false}" = "true" ]; then
   241      export GINKGO_PARALLEL=y
   242      if [ -z "${SKIP}" ]; then
   243        SKIP="\\[Serial\\]"
   244      else
   245        SKIP="\\[Serial\\]|${SKIP}"
   246      fi
   247    fi
   248  
   249    # setting this env prevents ginkgo e2e from trying to run provider setup
   250    export KUBERNETES_CONFORMANCE_TEST='y'
   251    # setting these is required to make RuntimeClass tests work ... :/
   252    export KUBE_CONTAINER_RUNTIME=remote
   253    export KUBE_CONTAINER_RUNTIME_ENDPOINT=unix:///run/containerd/containerd.sock
   254    export KUBE_CONTAINER_RUNTIME_NAME=containerd
   255    # ginkgo can take forever to exit, so we run it in the background and save the
   256    # PID, bash will not run traps while waiting on a process, but it will while
   257    # running a builtin like `wait`, saving the PID also allows us to forward the
   258    # interrupt
   259    ./hack/ginkgo-e2e.sh \
   260      '--provider=skeleton' "--num-nodes=${NUM_NODES}" \
   261      "--ginkgo.focus=${FOCUS}" "--ginkgo.skip=${SKIP}" \
   262      "--report-dir=${ARTIFACTS}" '--disable-log-dump=true' &
   263    GINKGO_PID=$!
   264    wait "$GINKGO_PID"
   265  }
   266  
   267  main() {
   268    # create temp dir and setup cleanup
   269    TMP_DIR=$(mktemp -d)
   270  
   271    # ensure artifacts (results) directory exists when not in CI
   272    export ARTIFACTS="${ARTIFACTS:-${PWD}/_artifacts}"
   273    mkdir -p "${ARTIFACTS}"
   274  
   275    # export the KUBECONFIG to a unique path for testing
   276    KUBECONFIG="${HOME}/.kube/kind-test-config"
   277    export KUBECONFIG
   278    echo "exported KUBECONFIG=${KUBECONFIG}"
   279  
   280    # debug kind version
   281    kind version
   282  
   283    # build kubernetes
   284    build
   285    # in CI attempt to release some memory after building
   286    if [ -n "${KUBETEST_IN_DOCKER:-}" ]; then
   287      sync || true
   288      echo 1 > /proc/sys/vm/drop_caches || true
   289    fi
   290  
   291    # create the cluster and run tests
   292    res=0
   293    create_cluster || res=$?
   294    run_tests || res=$?
   295    cleanup || res=$?
   296    exit $res
   297  }
   298  
   299  main