|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +set -euo pipefail |
| 4 | + |
| 5 | +STEP_BREADCRUMB='~~~~~~~~' |
| 6 | +SECONDS=0 |
| 7 | +TIMEFORMAT="${STEP_BREADCRUMB} took %R seconds" |
| 8 | + |
| 9 | +function maybe_build_push_docker { |
| 10 | + # Build and push docker image, tagged with SHA tag, if it doesn't exist already. |
| 11 | + NAME=$1 |
| 12 | + TARGET=$NAME-docker |
| 13 | + SUFFIX=feast-$NAME |
| 14 | + |
| 15 | + if ! aws ecr describe-images --repository-name "feast-ci/feast/$SUFFIX" "--image-ids=imageTag=${GIT_TAG}" >/dev/null ; then |
| 16 | + make "build-$TARGET" "push-$TARGET" REGISTRY="${DOCKER_REPOSITORY}" VERSION="${GIT_TAG}" |
| 17 | + else |
| 18 | + echo "Image ${DOCKER_REPOSITORY}/$SUFFIX:$GIT_TAG already exists, skipping docker build" |
| 19 | + fi |
| 20 | +} |
| 21 | + |
| 22 | +source infra/scripts/k8s-common-functions.sh |
| 23 | + |
| 24 | +GIT_TAG=${CODEBUILD_RESOLVED_SOURCE_VERSION} |
| 25 | + |
| 26 | +echo "########## Starting stage $STAGE for ${CODEBUILD_SOURCE_REPO_URL} ${GIT_TAG} ###########" |
| 27 | + |
| 28 | +# This seems to make builds a bit faster. |
| 29 | +export DOCKER_BUILDKIT=1 |
| 30 | + |
| 31 | +# Workaround for COPY command in core docker image that pulls local maven repo into the image |
| 32 | +# itself. |
| 33 | +mkdir .m2 2>/dev/null || true |
| 34 | +mkdir deps/feast/.m2 2>/dev/null || true |
| 35 | + |
| 36 | +# Log into k8s. |
| 37 | +echo "${STEP_BREADCRUMB} Updating kubeconfig" |
| 38 | +aws eks update-kubeconfig --name "$EKS_CLUSTER_NAME" |
| 39 | + |
| 40 | +# chmod kubeconfig so it doesn't complain all the time |
| 41 | +chmod 755 ~/.kube/config |
| 42 | + |
| 43 | +# Sanity check that kubectl is working. |
| 44 | +echo "${STEP_BREADCRUMB} k8s sanity check" |
| 45 | +kubectl get pods |
| 46 | + |
| 47 | +case $STAGE in |
| 48 | + core-docker) |
| 49 | + maybe_build_push_docker core |
| 50 | + ;; |
| 51 | + serving-docker) |
| 52 | + maybe_build_push_docker serving |
| 53 | + ;; |
| 54 | + jupyter-docker) |
| 55 | + maybe_build_push_docker jupyter |
| 56 | + ;; |
| 57 | + jobservice-docker) |
| 58 | + maybe_build_push_docker jobservice |
| 59 | + ;; |
| 60 | + ci-docker) |
| 61 | + maybe_build_push_docker ci |
| 62 | + ;; |
| 63 | + e2e-test-emr) |
| 64 | + # EMR test - runs in default namespace. |
| 65 | + |
| 66 | + # Copy cluster config template generated for us by terraform. |
| 67 | + aws s3 cp "${EMR_TEMPLATE_YML}" emr_cluster.yaml |
| 68 | + |
| 69 | + # Delete old helm release and PVCs |
| 70 | + k8s_cleanup cicd default |
| 71 | + |
| 72 | + # Create cluster OR get existing EMR cluster id. In the latter case, clean up any steps |
| 73 | + # already running there from previous test runs. |
| 74 | + echo "${STEP_BREADCRUMB} Creating EMR cluster, this can take up 10 minutes." |
| 75 | + CLUSTER_ID=$(time emr_cluster.py --template emr_cluster.yaml ensure --cleanup) |
| 76 | + |
| 77 | + # Get (any) node IP. EMR will use this to connect to Kafka and Redis. We make them |
| 78 | + # available to the EMR job by exposing them as NodePort services. |
| 79 | + NODE_IP=$(kubectl get nodes -o custom-columns=Name:.metadata.name | tail -n1) |
| 80 | + |
| 81 | + # Helm install everything. |
| 82 | + # |
| 83 | + # This may occasionally run into "provided port is already allocated" error due to |
| 84 | + # https://github.com/kubernetes/kubernetes/issues/85894 |
| 85 | + helm_install cicd "$DOCKER_REPOSITORY" "$GIT_TAG" default \ |
| 86 | + --set "redis.master.service.type=NodePort" \ |
| 87 | + --set "redis.master.service.nodePort=32379" \ |
| 88 | + --set "kafka.externalAccess.service.type=NodePort" \ |
| 89 | + --set "kafka.externalAccess.enabled=true" \ |
| 90 | + --set "kafka.externalAccess.service.nodePorts[0]=30092" \ |
| 91 | + --set "kafka.externalAccess.service.domain=${NODE_IP}" \ |
| 92 | + --set "kafka.service.externalPort=30094" |
| 93 | + |
| 94 | + # Run the test suite as a one-off pod. We could also run it here, in the codebuild container |
| 95 | + # itself, but that'd require more networking setup to make feast services available |
| 96 | + # outside k8s cluster. |
| 97 | + kubectl delete pod ci-test-runner 2>/dev/null || true |
| 98 | + |
| 99 | + echo "${STEP_BREADCRUMB} Running the test suite" |
| 100 | + time kubectl run --rm -i ci-test-runner \ |
| 101 | + --restart=Never \ |
| 102 | + --image="${DOCKER_REPOSITORY}/feast-ci:${GIT_TAG}" \ |
| 103 | + --env="CLUSTER_ID=$CLUSTER_ID" \ |
| 104 | + --env="STAGING_PATH=$STAGING_PATH" \ |
| 105 | + --env="NODE_IP=$NODE_IP" \ |
| 106 | + -- \ |
| 107 | + bash -c "mkdir src && cd src && git clone $CODEBUILD_SOURCE_REPO_URL && cd feast* && git config remote.origin.fetch '+refs/pull/*:refs/remotes/origin/pull/*' && git fetch -q && git checkout $CODEBUILD_RESOLVED_SOURCE_VERSION && ./infra/scripts/setup-e2e-env-aws.sh && ./infra/scripts/test-end-to-end-aws.sh" |
| 108 | + |
| 109 | + ;; |
| 110 | + e2e-test-sparkop) |
| 111 | + # spark k8s test - runs in sparkop namespace (so it doesn't interfere with a concurrently |
| 112 | + # running EMR test). |
| 113 | + NAMESPACE=sparkop |
| 114 | + RELEASE=sparkop |
| 115 | + |
| 116 | + # Clean up old release |
| 117 | + k8s_cleanup "$RELEASE" "$NAMESPACE" |
| 118 | + |
| 119 | + # Helm install everything in a namespace |
| 120 | + helm_install "$RELEASE" "${DOCKER_REPOSITORY}" "${GIT_TAG}" "$NAMESPACE" |
| 121 | + |
| 122 | + # Delete old test running pod if it exists |
| 123 | + kubectl delete pod -n "$NAMESPACE" ci-test-runner 2>/dev/null || true |
| 124 | + |
| 125 | + # Delete all sparkapplication resources that may be left over from the previous test runs. |
| 126 | + kubectl delete sparkapplication --all -n "$NAMESPACE" || true |
| 127 | + |
| 128 | + # Make sure the test pod has permissions to create sparkapplication resources |
| 129 | + setup_sparkop_role |
| 130 | + |
| 131 | + # Run the test suite as a one-off pod. |
| 132 | + echo "${STEP_BREADCRUMB} Running the test suite" |
| 133 | + if ! time kubectl run --rm -n "$NAMESPACE" -i ci-test-runner \ |
| 134 | + --restart=Never \ |
| 135 | + --image="${DOCKER_REPOSITORY}/feast-ci:${GIT_TAG}" \ |
| 136 | + --env="STAGING_PATH=$STAGING_PATH" \ |
| 137 | + -- \ |
| 138 | + bash -c "mkdir src && cd src && git clone $CODEBUILD_SOURCE_REPO_URL && cd feast* && git config remote.origin.fetch '+refs/pull/*:refs/remotes/origin/pull/*' && git fetch -q && git checkout $CODEBUILD_RESOLVED_SOURCE_VERSION && ./infra/scripts/setup-e2e-env-sparkop.sh && ./infra/scripts/test-end-to-end-sparkop.sh" ; then |
| 139 | + |
| 140 | + readarray -t CRASHED_PODS < <(kubectl get pods --no-headers=true --namespace sparkop | grep Error | awk '{ print $1 }') |
| 141 | + |
| 142 | + for POD in "${CRASHED_PODS[@]}"; do |
| 143 | + echo "Logs from crashed pod $POD:" |
| 144 | + kubectl logs --namespace sparkop "$POD" |
| 145 | + done |
| 146 | + fi |
| 147 | + |
| 148 | + ;; |
| 149 | + cleanup) |
| 150 | + emr_cluster.py --template emr_cluster.yaml destroy |
| 151 | + ;; |
| 152 | + *) |
| 153 | + echo "Unknown stage $STAGE" |
| 154 | + ;; |
| 155 | +esac |
| 156 | + |
| 157 | +echo "########## Stage $STAGE took $SECONDS seconds ###########" |
0 commit comments