Skip to content

e2e-tests: Retry invalid TOTP codes during Google login #4745

e2e-tests: Retry invalid TOTP codes during Google login

e2e-tests: Retry invalid TOTP codes during Google login #4745

Workflow file for this run

name: authd QA & sanity checks
on:
push:
branches:
- main
paths:
- '**'
- '!.github/**'
- '.github/workflows/qa.yaml'
- '!.gitignore'
- '!.gitmodules'
- '!AGENTS.md'
- '!CODE_OF_CONDUCT.md'
- '!CONTRIBUTING.md'
- '!COPYING'
- '!COPYING.LESSER'
- '!README.md'
- '!SECURITY.md'
- '!authd-oidc-brokers/**'
- '!docs/**'
- '!examplebroker/**'
- '!gotestcov'
- '!scripts/**'
- '!snap/**'
tags:
- "*"
pull_request:
paths:
- '**'
- '!.github/**'
- '.github/workflows/qa.yaml'
- '!.gitignore'
- '!.gitmodules'
- '!AGENTS.md'
- '!CODE_OF_CONDUCT.md'
- '!CONTRIBUTING.md'
- '!COPYING'
- '!COPYING.LESSER'
- '!README.md'
- '!SECURITY.md'
- '!authd-oidc-brokers/**'
- '!docs/**'
- '!examplebroker/**'
- '!gotestcov'
- '!scripts/**'
- '!snap/**'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
DEBIAN_FRONTEND: noninteractive
GO_TESTS_TIMEOUT: 20m
AUTHD_SSHD_STDERR_LOG_ALL_PAM_MESSAGES: true
c_build_dependencies: >-
clang-tools
clang
libglib2.0-dev
libpam-dev
go_build_dependencies: >-
libglib2.0-dev
libpam-dev
libpwquality-dev
go_test_dependencies: >-
bubblewrap
cracklib-runtime
git-delta
openssh-client
openssh-server
jobs:
go-sanity:
name: "Go: Code sanity"
permissions: {}
runs-on: ubuntu-24.04 # ubuntu-latest-runner
steps:
- uses: canonical/desktop-engineering/gh-actions/common/dpkg-install-speedup@main
- name: Install dependencies
run: |
# Install dependencies
set -eu
sudo apt-get update
sudo apt-get install -y ${{ env.go_build_dependencies }}
- uses: actions/checkout@v6
- name: Go code sanity check
uses: canonical/desktop-engineering/gh-actions/go/code-sanity@v2
with:
golangci-lint-configfile: ".golangci.yaml"
tools-directory: "tools"
token: ${{ secrets.GITHUB_TOKEN }}
- name: Build cmd/authd with withexamplebroker tag
run: |
set -eu
go build -tags withexamplebroker ./cmd/authd
- name: Run PAM client for interactive testing purposes
run: |
set -eu
go run -tags withpamrunner ./pam/tools/pam-runner login --exec-debug
- name: Generate PAM module
run: |
set -eu
find pam -name '*.so' -print -delete
go generate -C pam -x
test -e pam/pam_authd.so
test -e pam/go-exec/pam_authd_exec.so
- name: Generate PAM module with pam_debug tag
run: |
set -eu
find pam -name '*.so' -print -delete
go generate -C pam -x -tags pam_debug
test -e pam/pam_authd.so
test -e pam/go-exec/pam_authd_exec.so
shell-sanity:
name: "Shell: Code sanity"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@master
with:
ignore_paths: "./authd-oidc-brokers"
rust-sanity:
name: "Rust: Code sanity"
permissions: {}
runs-on: ubuntu-24.04 # ubuntu-latest-runner
steps:
- uses: canonical/desktop-engineering/gh-actions/common/dpkg-install-speedup@main
- name: Install dependencies
run: |
# Install dependencies
set -eu
sudo apt-get update
# In Rust the grpc stubs are generated at build time
# so we always need to install the protobuf compilers
# when building the NSS crate.
sudo apt-get install -y protobuf-compiler
- uses: actions/checkout@v6
- name: Rust code sanity check
uses: canonical/desktop-engineering/gh-actions/rust/code-sanity@main
with:
token: ${{ secrets.GITHUB_TOKEN }}
c-sanity:
name: "C Code sanity"
runs-on: ubuntu-24.04 # ubuntu-latest-runner
env:
CFLAGS: "-Werror"
steps:
- uses: canonical/desktop-engineering/gh-actions/common/dpkg-install-speedup@main
- name: Install dependencies
run: |
# Install dependencies
set -eu
sudo apt-get update
sudo apt-get install -y ${{ env.c_build_dependencies }}
- name: Prepare report dir
run: |
set -eu
scan_build_dir=$(mktemp -d --tmpdir scan-build-dir-XXXXXX)
echo SCAN_BUILD_REPORTS_PATH="${scan_build_dir}" >> $GITHUB_ENV
- uses: actions/checkout@v6
- name: Run scan build on GDM extensions
run: |
set -eu
scan-build -v -o "${SCAN_BUILD_REPORTS_PATH}" clang ${CFLAGS} \
-Wno-gnu-variable-sized-type-not-at-end \
pam/internal/gdm/extension.h
- name: Run scan build on go-exec module
run: |
set -eu
scan-build -v -o "${SCAN_BUILD_REPORTS_PATH}" clang ${CFLAGS} \
-DAUTHD_TEST_MODULE=1 \
$(pkg-config --cflags --libs gio-unix-2.0 gio-2.0) \
-lpam -shared -fPIC \
pam/go-exec/module.c
- name: Upload scan build reports
uses: actions/upload-artifact@v7
with:
name: authd-${{ github.job }}-artifacts-${{ github.run_attempt }}
path: ${{ env.SCAN_BUILD_REPORTS_PATH }}
go-tests-coverage:
name: "Go Tests with Coverage Collection"
runs-on: ubuntu-24.04 # ubuntu-latest-runner
env:
RAW_COVERAGE_DIR: ${{ github.workspace }}/raw-coverage
COVERAGE_DIR: ${{ github.workspace }}/coverage
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-go-tests
- uses: ./.github/actions/setup-go-coverage-tests
# Installation of debug symbols takes a long time (and fails currently),
# so we skip it for now. Enable on demand.
# - uses: ./.github/actions/install-debug-symbols
# continue-on-error: true
- name: Run tests with coverage collection
env:
G_DEBUG: "fatal-criticals"
run: |
set -euo pipefail
# The coverage is not written if the output directory does not exist, so we need to create it.
mkdir -p "${RAW_COVERAGE_DIR}"
# Print executed commands to ease debugging
set -x
# Work around https://github.com/golang/go/issues/75031
go env -w GOTOOLCHAIN="$(go version | awk '{ print $3 }')+auto"
# Overriding the default coverage directory is not an exported flag of go test (yet), so
# we need to override it using the test.gocoverdir flag instead.
#TODO: Update when https://go-review.googlesource.com/c/go/+/456595 is merged.
go test -json -timeout ${GO_TESTS_TIMEOUT} -cover -covermode=set ./... -coverpkg=./... \
-shuffle=on -args -test.gocoverdir="${RAW_COVERAGE_DIR}" | \
gotestfmt --logfile "${AUTHD_TESTS_ARTIFACTS_PATH}/gotestfmt.cover.log"
# Upload the test output for the go-tests-coverage-retry job which retries
# the failed tests.
- name: Upload JSON test output on failure
if: failure()
uses: actions/upload-artifact@v7
with:
name: coverage-test-output
path: ${{ env.AUTHD_TESTS_ARTIFACTS_PATH }}/gotestfmt.cover.stdout
# Upload the raw coverage data so that the go-tests-coverage-retry job has
# all the data to generate the coverage report (if the tests succeed on
# retry).
- name: Upload raw coverage on failure
if: failure()
uses: actions/upload-artifact@v7
with:
name: raw-coverage-data
path: ${{ env.RAW_COVERAGE_DIR }}
- uses: ./.github/actions/generate-coverage-report
with:
codecov-token: ${{ secrets.CODECOV_TOKEN }}
- uses: ./.github/actions/upload-test-artifacts
if: always()
go-tests-coverage-retry:
name: "Retry Go Tests with Coverage Collection"
needs: go-tests-coverage
if: always() && needs.go-tests-coverage.result == 'failure'
runs-on: ubuntu-24.04
env:
RAW_COVERAGE_DIR: ${{ github.workspace }}/raw-coverage
COVERAGE_DIR: ${{ github.workspace }}/coverage
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-go-tests
- uses: ./.github/actions/setup-go-coverage-tests
# Installation of debug symbols takes a long time (and fails currently),
# so we skip it for now. Enable on demand.
# - uses: ./.github/actions/install-debug-symbols
# continue-on-error: true
- name: Download JSON output of failed tests
uses: actions/download-artifact@v8
with:
name: coverage-test-output
path: /tmp/coverage-test-output
- name: Download raw coverage data
uses: actions/download-artifact@v8
with:
name: raw-coverage-data
path: ${{ env.RAW_COVERAGE_DIR }}
- name: Install gotest-rerun-failed
run: go install github.com/adombeck/gotest-rerun-failed@latest
- name: Retry failed tests with coverage collection
run: |
set -euo pipefail
# Print executed commands to ease debugging
set -x
test_output="/tmp/coverage-test-output/gotestfmt.cover.stdout"
for i in $(seq 1 3); do
echo "Retrying failed tests (attempt ${i})"
gotest-rerun-failed -json -timeout ${GO_TESTS_TIMEOUT} -cover -covermode=set -- -coverpkg=./... \
-shuffle=on -args -test.gocoverdir="${RAW_COVERAGE_DIR}" \
< "${test_output}" \
| gotestfmt --logfile "${AUTHD_TESTS_ARTIFACTS_PATH}/gotestfmt.cover.retry-$i.log" \
&& exit_code=0 || exit_code=$?
if [ "${exit_code}" -eq 0 ]; then
break
fi
if [ "${i}" -eq 3 ]; then
echo "Tests failed 3 times, giving up"
exit ${exit_code}
fi
test_output="${AUTHD_TESTS_ARTIFACTS_PATH}/gotestfmt.cover.retry-$i.stdout"
done
- uses: ./.github/actions/generate-coverage-report
with:
codecov-token: ${{ secrets.CODECOV_TOKEN }}
- uses: ./.github/actions/upload-test-artifacts
if: always()
go-tests-race:
name: "Go Tests with Race Detector"
runs-on: ubuntu-24.04 # ubuntu-latest-runner
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-go-tests
# Installation of debug symbols takes a long time (and fails currently),
# so we skip it for now. Enable on demand.
# - uses: ./.github/actions/install-debug-symbols
# continue-on-error: true
- name: Run tests with race detector
env:
GO_TESTS_TIMEOUT: 35m
AUTHD_TESTS_SLEEP_MULTIPLIER: 3
AUTHD_SKIP_FLAKY_TESTS: true
GORACE: log_path=${{ env.AUTHD_TESTS_ARTIFACTS_PATH }}/gorace.log
run: |
go test -json -timeout ${GO_TESTS_TIMEOUT} -race -failfast ./... | \
gotestfmt --logfile "${AUTHD_TESTS_ARTIFACTS_PATH}/gotestfmt.race.log" || exit_code=$?
if [ "${exit_code:-0}" -ne 0 ]; then
cat "${AUTHD_TESTS_ARTIFACTS_PATH}"/gorace.log* || true
exit ${exit_code}
fi
- uses: ./.github/actions/upload-test-artifacts
if: always()
go-tests-asan:
name: "Go PAM tests with Address Sanitizer"
runs-on: ubuntu-24.04 # ubuntu-latest-runner
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/setup-go-tests
# Installation of debug symbols takes a long time (and fails currently),
# so we skip it for now. Enable on demand.
# - uses: ./.github/actions/install-debug-symbols
# continue-on-error: true
- name: Run PAM tests with Address Sanitizer
env:
# Do not optimize, keep debug symbols and frame pointer for better
# stack trace information in case of ASAN errors.
CGO_CFLAGS: "-O0 -g3 -fno-omit-frame-pointer"
G_DEBUG: "fatal-criticals"
GO_TESTS_TIMEOUT: 30m
AUTHD_TESTS_SLEEP_MULTIPLIER: 1.5
AUTHD_SKIP_FLAKY_TESTS: true
# Use these flags to give ASAN a better time to unwind the stack trace
GO_GC_FLAGS: -N -l
run: |
# Print executed commands to ease debugging
set -x
echo "::group::Install llvm-symbolizer"
# For llvm-symbolizer
sudo apt-get install -y llvm
echo "::endgroup::"
go test -C ./pam/internal -json -asan -gcflags=all="${GO_GC_FLAGS}" -failfast -timeout ${GO_TESTS_TIMEOUT} ./... | \
gotestfmt --logfile "${AUTHD_TESTS_ARTIFACTS_PATH}/gotestfmt.pam-internal-asan.log" || exit_code=$?
if [ -n "${exit_code:-}" ]; then
cat "${AUTHD_TESTS_ARTIFACTS_PATH}"/asan.log* || true
exit ${exit_code}
fi
echo "Running PAM integration tests"
pushd ./pam/integration-tests
go test -asan -gcflags=all="${GO_GC_FLAGS}" -c
go tool test2json -p pam/integrations-test ./integration-tests.test \
-test.v=test2json \
-test.failfast \
-test.timeout ${GO_TESTS_TIMEOUT} | \
gotestfmt --logfile "${AUTHD_TESTS_ARTIFACTS_PATH}/gotestfmt.pam-integration-tests-asan.log" || \
exit_code=$?
popd
# We don't need the xtrace output after this point
set +x
# We're logging to a file, and this is useful for having artifacts, but we still may want to see it in logs:
for f in "${AUTHD_TESTS_ARTIFACTS_PATH}"/asan.log*; do
if ! [ -e "${f}" ]; then
continue
fi
if [ -s "${f}" ]; then
echo "::group::${f} ($(wc -l < "${f}") lines)"
cat "${f}"
echo "::endgroup::"
else
echo "${f}: empty"
fi
done
exit ${exit_code}
- uses: ./.github/actions/upload-test-artifacts
if: always()