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