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