name: deploy-local-acs description: Deploy local ACS for E2E testing inputs: docker_username: description: 'Docker username' required: true docker_password: description: 'Docker password' required: true quay_username: description: 'Quay username' required: true quay_password: description: 'Quay password' required: true acs_deployment_version: description: >- Git ref (tag, branch, or commit SHA) of the Alfresco/acs-deployment repository to check out. Use the special value 'latest' (the default) to resolve to the latest GA release tag at runtime. When set to 'master', pre-release_values.yaml is included in the Helm install to pick up pre-release image tags; any other ref uses the chart's standard values.yaml instead. required: false default: 'latest' helm_install_timeout: description: >- Timeout for the Helm install to wait for the chart's hooks (e.g. the search reindexing job) and, via --wait, its Deployments, StatefulSets and PVCs to become ready, in Helm's duration format (e.g. '5m', '10m'). required: false default: '10m' log_name_identifier: description: >- Suffix that makes the uploaded pod-logs artifact name unique. Callers in a matrix must pass a value unique per leg (e.g. browser + suite) to avoid artifact name collisions. required: false default: 'deploy-local-acs' runs: using: "composite" steps: - uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 with: version: "3.14.3" - name: Login to Docker Hub id: dockerhub-login uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 continue-on-error: true with: username: ${{ inputs.docker_username }} password: ${{ inputs.docker_password }} - name: Retry Docker Hub login on transient failure if: steps.dockerhub-login.outcome == 'failure' shell: bash env: DOCKER_USERNAME: ${{ inputs.docker_username }} DOCKER_PASSWORD: ${{ inputs.docker_password }} run: | for attempt in 1 2 3; do err=$(echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin 2>&1) && exit 0 # Bail out on credential errors; retrying only annoys the registry and delays the real fix. if echo "$err" | grep -qiE "401|403|unauthorized|denied|forbidden"; then echo "$err" exit 1 fi sleep_s=$((attempt * 5)) echo "docker login attempt $attempt failed: $err" echo "retrying in ${sleep_s}s..." sleep "$sleep_s" done exit 1 - name: Login to Quay.io id: quay-login uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 continue-on-error: true with: registry: quay.io username: ${{ inputs.quay_username }} password: ${{ inputs.quay_password }} - name: Retry Quay.io login on transient failure if: steps.quay-login.outcome == 'failure' shell: bash env: QUAY_USERNAME: ${{ inputs.quay_username }} QUAY_PASSWORD: ${{ inputs.quay_password }} run: | for attempt in 1 2 3; do err=$(echo "$QUAY_PASSWORD" | docker login quay.io -u "$QUAY_USERNAME" --password-stdin 2>&1) && exit 0 if echo "$err" | grep -qiE "401|403|unauthorized|denied|forbidden"; then echo "$err" exit 1 fi sleep_s=$((attempt * 5)) echo "quay login attempt $attempt failed: $err" echo "retrying in ${sleep_s}s..." sleep "$sleep_s" done exit 1 - name: Setup cluster uses: Alfresco/alfresco-build-tools/.github/actions/setup-kind@v18.26.0 with: ingress-nginx-ref: controller-v1.8.2 ingress-creation-timeout: 180s - name: Set nginx ingress config shell: bash run: >- kubectl -n ingress-nginx patch cm ingress-nginx-controller -p '{"data": {"allow-snippet-annotations":"true"}}' - name: Create registries auth secret shell: bash run: >- kubectl create secret generic regcred --from-file=.dockerconfigjson=$HOME/.docker/config.json --type=kubernetes.io/dockerconfigjson - name: Add dependency chart repos shell: bash run: | helm repo add self https://alfresco.github.io/alfresco-helm-charts/ helm repo add elastic https://helm.elastic.co/ - name: Resolve acs-deployment ref id: get-acs-version shell: bash env: ACS_VERSION_INPUT: ${{ inputs.acs_deployment_version }} GITHUB_TOKEN: ${{ github.token }} run: | if [ "$ACS_VERSION_INPUT" != "latest" ]; then echo "Using specified acs-deployment ref: $ACS_VERSION_INPUT" echo "version=$ACS_VERSION_INPUT" >> "$GITHUB_OUTPUT" exit 0 fi auth_header=() [ -n "$GITHUB_TOKEN" ] && auth_header=(-H "Authorization: Bearer $GITHUB_TOKEN") max_retries=3 retry_count=0 while [ $retry_count -lt $max_retries ]; do echo "Attempt $((retry_count + 1)) of $max_retries to fetch latest ACS deployment version..." response=$(curl -s "${auth_header[@]}" https://api.github.com/repos/Alfresco/acs-deployment/releases/latest) latest_tag=$(echo "$response" | jq -r .tag_name 2>/dev/null || echo "") if [ "$latest_tag" != "null" ] && [ -n "$latest_tag" ]; then echo "Successfully fetched latest ACS deployment version: $latest_tag" echo "version=$latest_tag" >> "$GITHUB_OUTPUT" exit 0 fi echo "Warning: Received invalid response (tag_name: $latest_tag)" retry_count=$((retry_count + 1)) if [ $retry_count -lt $max_retries ]; then sleep_time=$((2 ** retry_count)) echo "Retrying in $sleep_time seconds..." sleep $sleep_time fi done echo "Error: Failed to fetch latest ACS deployment version after $max_retries attempts" echo "Last response: $response" exit 1 - name: Checkout acs-deployment ${{ steps.get-acs-version.outputs.version }} uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: repository: Alfresco/acs-deployment ref: ${{ steps.get-acs-version.outputs.version }} path: acs-deployment - name: Helm install shell: bash env: ACS_VERSION_INPUT: ${{ inputs.acs_deployment_version }} HELM_INSTALL_TIMEOUT: ${{ inputs.helm_install_timeout }} run: | [ "$ACS_VERSION_INPUT" = "master" ] && chart_values="pre-release_values.yaml" || chart_values="values.yaml" for attempt in 1 2 3; do if helm dep build acs-deployment/helm/alfresco-content-services; then break fi if [ "$attempt" -eq 3 ]; then echo "helm dep build failed after 3 attempts" exit 1 fi sleep_s=$((attempt * 10)) echo "helm dep build attempt $attempt failed, retrying in ${sleep_s}s..." sleep "$sleep_s" done helm install acs acs-deployment/helm/alfresco-content-services \ --wait \ --timeout "$HELM_INSTALL_TIMEOUT" \ --set global.search.sharedSecret="$(openssl rand -hex 24)" \ --values "acs-deployment/helm/alfresco-content-services/${chart_values}" \ --values acs-deployment/test/enterprise-integration-test-values.yaml \ --values .github/acs-deployment-values-override.yaml - name: Dump cluster state on deploy failure if: failure() shell: bash run: | echo "::group::Helm releases" helm ls --all-namespaces --all || true echo "::endgroup::" echo "::group::Helm status (acs)" helm status acs --show-resources || true echo "::endgroup::" echo "::group::All resources (all namespaces)" kubectl get all --all-namespaces || true echo "::endgroup::" echo "::group::Node metrics" kubectl top nodes || true echo "::endgroup::" echo "::group::Pod metrics (by memory)" kubectl top pod --all-namespaces --sort-by=memory || true echo "::endgroup::" echo "::group::Recent cluster events" kubectl get events -A --sort-by=.lastTimestamp | tail -n 100 || true echo "::endgroup::" # Filter on the Ready condition, not phase: the usual culprit is a repository pod stuck phase=Running/Ready=False on a startup-probe timeout, which a phase filter would miss. pods=$(kubectl get pods -A \ -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.status.phase}{"\t"}{range .status.conditions[?(@.type=="Ready")]}{.status}{end}{"\n"}{end}' \ 2>/dev/null || true) not_ready=$(echo "$pods" | awk -F'\t' '$3 != "Succeeded" && $4 != "True" { print $1" "$2 }') if [ -z "$not_ready" ]; then echo "No not-ready pods were found." exit 0 fi while read -r ns pod; do [ -z "$ns" ] && continue echo "::group::describe ${ns}/${pod}" kubectl describe pod -n "$ns" "$pod" || true echo "::endgroup::" echo "::group::logs ${ns}/${pod} (previous)" kubectl logs -n "$ns" "$pod" --all-containers --previous --tail=200 2>/dev/null || echo "(no previous container logs)" echo "::endgroup::" done <<< "$not_ready" - name: Report deployed ACS versions if: always() shell: bash env: ACS_DEPLOYMENT_VERSION: ${{ steps.get-acs-version.outputs.version }} run: | pre_release_values="acs-deployment/helm/alfresco-content-services/pre-release_values.yaml" pod_images=$(kubectl get pods -A \ -o jsonpath='{range .items[*]}{.metadata.namespace}{"/"}{.metadata.name}{"\t"}{range .spec.containers[*]}{.name}{"="}{.image}{"\n"}{end}{end}' \ 2>/dev/null | sort -u || true) cluster_images=$(echo "${pod_images}" | awk -F'\t' 'NF > 1 { sub(/^[^=]*=/, "", $NF); print $NF }' | sort -u) acs_repository_image=$(echo "${cluster_images}" | grep -m1 'alfresco-content-repository:' || echo "(alfresco-content-repository pod not found)") acs_repository_tag="${acs_repository_image##*:}" # ACS_DEPLOYMENT_VERSION is a git ref (branch, tag, or SHA) — resolve it to the # exact commit SHA that was checked out so past runs can be reproduced even # when moving refs like `master` have advanced. acs_deployment_commit=$(git -C acs-deployment rev-parse HEAD 2>/dev/null || echo "(unknown)") { echo "### 🐳 Deployed ACS versions" echo "- **ACS repository:** \`${acs_repository_tag}\`" echo "- **acs-deployment ref:** \`${ACS_DEPLOYMENT_VERSION}\`" echo "- **acs-deployment commit:** \`${acs_deployment_commit}\`" echo "**Container images in cluster:**" echo "${cluster_images:-(no pods found)}" } >> "$GITHUB_STEP_SUMMARY" echo "::group::ACS repository image tag" echo "${acs_repository_image}" echo "::endgroup::" echo "::group::acs-deployment ref" echo "${ACS_DEPLOYMENT_VERSION}" echo "::endgroup::" echo "::group::acs-deployment commit" echo "${acs_deployment_commit}" echo "::endgroup::" values_path="acs-deployment/helm/alfresco-content-services/values.yaml" [ "$ACS_DEPLOYMENT_VERSION" = "master" ] && values_path="$pre_release_values" echo "::group::Chart values file used ($(basename "$values_path"))" if [ -f "$values_path" ]; then cat "$values_path" else echo "(file not found: $values_path)" fi echo "::endgroup::" echo "::group::Actual container images running in the cluster" echo "${pod_images:-(no pods found)}" echo "::endgroup::" - name: Upload pods logs if: always() uses: Alfresco/alfresco-build-tools/.github/actions/kubectl-keep-nslogs@d2981070adf1a68c3ddcbf0ebba193467261c9b3 # v18.26.0 with: namespace: default log_retention: 7 log_name_identifier: "logs-pods-${{ inputs.log_name_identifier }}"