diff --git a/.github/actions/e2e-tests/set-up-ssh-keys/action.yml b/.github/actions/e2e-tests/set-up-ssh-keys/action.yml new file mode 100644 index 0000000000..830a3fce6d --- /dev/null +++ b/.github/actions/e2e-tests/set-up-ssh-keys/action.yml @@ -0,0 +1,42 @@ +name: Set up SSH keys for VM provisioning +description: "Sets up SSH keys for VM provisioning" +inputs: + E2E_VM_SSH_PRIV_KEY: + description: "Private SSH key for VM provisioning" + required: true + E2E_VM_SSH_PUB_KEY: + description: "Public SSH key for VM provisioning" + required: true +outputs: + keys-path: + description: "Path to the SSH keys" + value: "${{ steps.set-ssh-keys.outputs.keys-path }}" +runs: + using: "composite" + steps: + - name: Set GitHub Path + run: echo "$GITHUB_ACTION_PATH" >> $GITHUB_PATH + shell: bash + env: + GITHUB_ACTION_PATH: ${{ github.action_path }} + + - name: Set up SSH keys for the VM provisioning + id: set-ssh-keys + shell: bash + run: | + set -eu + + mkdir -p "${HOME}/.ssh" + chmod 700 "${HOME}/.ssh" + + echo "${{ inputs.E2E_VM_SSH_PRIV_KEY }}" > "${HOME}/.ssh/id_rsa" + chmod 600 "${HOME}/.ssh/id_rsa" + + echo "${{ inputs.E2E_VM_SSH_PUB_KEY }}" > "${HOME}/.ssh/id_rsa.pub" + chmod 644 "${HOME}/.ssh/id_rsa.pub" + + eval "$(ssh-agent -s)" + + ssh-add "${HOME}/.ssh/id_rsa" + + echo "keys-path=${HOME}/.ssh" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/auto-updates.yaml b/.github/workflows/auto-updates.yaml deleted file mode 100644 index 9884b71e71..0000000000 --- a/.github/workflows/auto-updates.yaml +++ /dev/null @@ -1,115 +0,0 @@ -name: Update Rust packaging related files in main -on: - push: - branches: - - main - paths: - - 'Cargo.lock' - - 'debian/control' -concurrency: auto-update - -permissions: - pull-requests: write - contents: write - -# Jobs in this action must not run concurrently, as they modify the repository. -# When adding more jobs, make sure to use the "needs:" attribute to make sure they run sequentially. -jobs: - update-rust-packaging: - strategy: - fail-fast: false - matrix: - branch: [main] - ubuntu-version: [devel] - include: - - branch: noble - ubuntu-version: noble - - name: Update ${{ matrix.ubuntu-version }} packaging related Rust files - runs-on: ubuntu-latest - container: - image: ubuntu:${{ matrix.ubuntu-version }} - env: - CARGO_VENDOR_DIR: ${{ github.workspace }}/vendor_rust - UPDATE_BRANCH: auto-update-rust-packaging-${{ matrix.ubuntu-version }} - steps: - - name: Install dependencies - env: - DEBIAN_FRONTEND: noninteractive - CARGO_VENDOR_FILTERER_NOBLE_VERSION: 0.5.16 - shell: bash - run: | - set -euo pipefail - - apt-get update -y - apt-get install -y dh-cargo git - - if [ "${{ matrix.ubuntu-version }}" = "noble" ]; then - # Special behavior on noble as dh-cargo is not new enough there - apt-get install -y libssl-dev pkg-config - cargo install --locked --root=/usr \ - cargo-vendor-filterer@${{ env.CARGO_VENDOR_FILTERER_NOBLE_VERSION }} - else - apt-get install -y cargo-vendor-filterer - fi - - - name: Checkout the code - uses: actions/checkout@v6 - with: - ref: ${{ matrix.branch }} - - - name: Vendor the dependencies - env: - CARGO_PATH: /usr/share/cargo/bin/cargo - shell: bash - run: | - set -euo pipefail - - sh -x debian/vendor-rust.sh - - - name: Update XS-Vendored-Sources-Rust - shell: bash - run: | - set -euo pipefail - - echo "Running dh-cargo-vendored-sources" - VENDORED_SOURCES=$(/usr/share/cargo/bin/dh-cargo-vendored-sources 2>&1) \ - || cmd_status=$? - echo "${VENDORED_SOURCES}" - - OUTPUT=$(echo "$VENDORED_SOURCES" | grep ^XS-Vendored-Sources-Rust: || true) - if [ -z "${OUTPUT}" ]; then - if [ "${cmd_status:-0}" -ne 0 ]; then - # dh-cargo-vendored-sources failed because of other reason, so let's fail with it! - echo "dh-cargo-vendored-sources failed unexpectedly (exit code ${cmd_status})" - exit "${cmd_status}" - fi - - echo "XS-Vendored-Sources-Rust is up to date. No change is needed."; - exit 0 - fi - sed -i "s/^XS-Vendored-Sources-Rust:.*/$OUTPUT/" debian/control - - echo "modified=true" >> "${GITHUB_ENV}" - echo "update_branch=${UPDATE_BRANCH}" >> "${GITHUB_ENV}" - - - name: Create Pull Request - if: ${{ env.modified == 'true' }} - uses: peter-evans/create-pull-request@v8 - with: - commit-message: Auto update packaging related Rust files - title: | - [${{ matrix.ubuntu-version }}] Auto update packaging related Rust files - labels: control, automated pr - branch: ${{ env.update_branch }} - delete-branch: true - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Push branch - if: ${{ env.modified == 'true' }} - shell: bash - run: | - set -eu - - git config --system --add safe.directory "${PWD}" - git push origin ${{ env.update_branch }}:${{ matrix.branch }} diff --git a/.github/workflows/automatic-doc-checks.yaml b/.github/workflows/automatic-doc-checks.yaml deleted file mode 100644 index 766c005cfc..0000000000 --- a/.github/workflows/automatic-doc-checks.yaml +++ /dev/null @@ -1,26 +0,0 @@ -name: Main Documentation Checks - -on: - push: - branches: [main] - paths: - - '.github/workflows/automatic-doc-checks.yaml' - - 'docs/**' - pull_request: - paths: - - '.github/workflows/automatic-doc-checks.yaml' - - 'docs/**' - schedule: - - cron: '0 12 * * MON' - # Manual trigger - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - documentation-checks: - uses: canonical/documentation-workflows/.github/workflows/documentation-checks.yaml@main - with: - working-directory: "./docs" diff --git a/.github/workflows/brokers-qa.yaml b/.github/workflows/brokers-qa.yaml deleted file mode 100644 index f09f43e394..0000000000 --- a/.github/workflows/brokers-qa.yaml +++ /dev/null @@ -1,202 +0,0 @@ -name: Brokers QA & sanity checks -on: - push: - branches: - - main - tags: - - "*" - paths: - - "authd-oidc-brokers/**" - - "!authd-oidc-brokers/e2e-tests/**" - - "!authd-oidc-brokers/po/**" - - "!authd-oidc-brokers/.gitignore" - - pull_request: - paths: - - "authd-oidc-brokers/**" - - "!authd-oidc-brokers/e2e-tests/**" - - "!authd-oidc-brokers/po/**" - - "!authd-oidc-brokers/.gitignore" - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -defaults: - run: - working-directory: ./authd-oidc-brokers - -jobs: - go-sanity: - name: "Go: Code sanity" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - with: - submodules: recursive - - - name: Build libhimmelblau - # The code sanity check fails if himmelblau.h does not exist, so we generate it first. - run: go generate --tags withmsentraid ./internal/providers/msentraid/... - - - name: Go code sanity check - uses: canonical/desktop-engineering/gh-actions/go/code-sanity@v2 - with: - golangci-lint-configfile: ".golangci.yaml" - working-directory: "./authd-oidc-brokers" - tools-directory: "./authd-oidc-brokers/tools" - go-tags: "withmsentraid" - - shell-sanity: - name: "Shell: Code sanity" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - name: Run ShellCheck - uses: ludeeus/action-shellcheck@master - - go-tests: - name: "Go: Tests" - runs-on: ubuntu-24.04 # ubuntu-latest-runner - strategy: - fail-fast: false - matrix: - test: [ "coverage", "asan" ] - steps: - - uses: actions/checkout@v6 - with: - submodules: recursive - - uses: actions/setup-go@v6 - with: - go-version-file: go.mod - - - uses: canonical/desktop-engineering/gh-actions/common/dpkg-install-speedup@main - - name: Install dependencies - run: | - set -eu - sudo apt-get update - sudo apt-get install -y git-delta - - - name: Install coverage collection dependencies - if: matrix.test == 'coverage' - run: | - set -eu - - go install github.com/AlekSi/gocov-xml@latest - go install github.com/adombeck/gocov/gocov@latest - dotnet tool install -g dotnet-reportgenerator-globaltool - - - name: Build libhimmelblau - run: go generate --tags withmsentraid ./internal/providers/msentraid/... - - - name: Prepare tests artifacts path - run: | - set -eu - - artifacts_dir=$(mktemp -d --tmpdir authd-test-artifacts-XXXXXX) - echo AUTHD_TEST_ARTIFACTS_DIR="${artifacts_dir}" >> $GITHUB_ENV - - - name: Install gotestfmt and our wrapper script - uses: canonical/desktop-engineering/gh-actions/go/gotestfmt@main - - - name: Run tests (with coverage collection) - if: matrix.test == 'coverage' - run: | - set -euo pipefail - - # The coverage is not written if the output directory does not exist, so we need to create it. - cov_dir=${PWD}/coverage - raw_cov_dir=${cov_dir}/raw_files - codecov_dir=${cov_dir}/codecov - - mkdir -p "${raw_cov_dir}" "${codecov_dir}" - - # Print executed commands to ease debugging - set -x - - # 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 -tags withmsentraid -json -cover -covermode=set ./... -shuffle=on -args -test.gocoverdir="${raw_cov_dir}" 2>&1 | \ - gotestfmt --logfile "${AUTHD_TEST_ARTIFACTS_DIR}/gotestfmt.cover.log" - - # Convert the raw coverage data into textfmt so we can merge the Rust one into it - go tool covdata textfmt -i="${raw_cov_dir}" -o="${cov_dir}/coverage.out" - - # Filter out the testutils package - grep -v -e "testutils" "${cov_dir}/coverage.out" >"${cov_dir}/coverage.out.filtered" - - # Generate the Cobertura report for Go - gocov convert "${cov_dir}/coverage.out.filtered" | gocov-xml > "${cov_dir}/coverage.xml" - reportgenerator -reports:"${cov_dir}/coverage.xml" -targetdir:"${codecov_dir}" -reporttypes:Cobertura - - # Store the coverage directory for the next steps - echo COVERAGE_DIR="${codecov_dir}" >> ${GITHUB_ENV} - - - name: Run msentraid tests (with Address Sanitizer) - if: matrix.test == 'asan' - 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" - GO_TESTS_TIMEOUT: 30m - # 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 - - # For llvm-symbolizer - sudo apt-get install -y llvm - - # We only run the msentraid tests with ASAN because only these use cgo. - pushd ./internal/providers/msentraid - go test -asan -gcflags=all="${GO_GC_FLAGS}" -c - go tool test2json -p internal/providers/msentraid ./msentraid.test \ - -test.v=test2json \ - -test.failfast \ - -test.timeout ${GO_TESTS_TIMEOUT} | \ - gotestfmt --logfile "${AUTHD_TEST_ARTIFACTS_DIR}/gotestfmt.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_TEST_ARTIFACTS_DIR}"/*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} - - - name: Upload coverage to Codecov - if: matrix.test == 'coverage' - uses: codecov/codecov-action@v5 - with: - directory: ${{ env.COVERAGE_DIR }} - files: ${{ env.COVERAGE_DIR }}/Cobertura.xml - token: ${{ secrets.CODECOV_TOKEN }} - - - name: Upload coverage report as artifact - if: matrix.test == 'coverage' && github.ref == 'refs/heads/main' - uses: actions/upload-artifact@v6 - with: - name: coverage - path: ${{ env.COVERAGE_DIR }} - - - name: Upload test artifacts - if: failure() - uses: actions/upload-artifact@v6 - with: - name: authd-${{ github.job }}-artifacts-${{ github.run_attempt }} - path: ${{ env.AUTHD_TEST_ARTIFACTS_DIR }} diff --git a/.github/workflows/build-deb.yaml b/.github/workflows/build-deb.yaml deleted file mode 100644 index da96eeb5d5..0000000000 --- a/.github/workflows/build-deb.yaml +++ /dev/null @@ -1,320 +0,0 @@ -name: Build debian packages - -on: - push: - branches: - - main - paths: - - '**' - - '!.github/**' - - '.github/workflows/build-deb.yaml' - - '!.gitignore' - - '!.gitmodules' - - '!.golangci.yaml' - - '!AGENTS.md' - - '!CODE_OF_CONDUCT.md' - - '!CONTRIBUTING.md' - - '!COPYING' - - '!COPYING.LESSER' - - '!README.md' - - '!SECURITY.md' - - '!authd-oidc-brokers/**' - - '!docs/**' - - '!examplebroker/**' - - '!gotestcov' - - '!snap/**' - tags: - - "*" - pull_request: - paths: - - '**' - - '!.github/**' - - '.github/workflows/build-deb.yaml' - - '!.gitignore' - - '!.gitmodules' - - '!.golangci.yaml' - - '!AGENTS.md' - - '!CODE_OF_CONDUCT.md' - - '!CONTRIBUTING.md' - - '!COPYING' - - '!COPYING.LESSER' - - '!README.md' - - '!SECURITY.md' - - '!authd-oidc-brokers/**' - - '!docs/**' - - '!examplebroker/**' - - '!gotestcov' - - '!snap/**' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -env: - UBUNTU_VERSIONS: | - ["noble", "plucky", "questing", "devel"] - CARGO_VENDOR_FILTERER_VERSION: 0.5.16 - -jobs: - define-versions: - name: Define build versions - runs-on: ubuntu-latest - outputs: - ubuntu-versions: ${{ env.UBUNTU_VERSIONS }} - steps: - - run: 'true' - - build-deb-package: - name: Build ubuntu package - runs-on: ubuntu-latest - needs: define-versions - strategy: - fail-fast: false - matrix: - ubuntu-version: ${{ fromJSON(needs.define-versions.outputs.ubuntu-versions) }} - outputs: - run-id: ${{ github.run_id }} - pkg-name: ${{ steps.outputs.outputs.pkg-name }} - # FIXME: Use dynamic outputs when possible: https://github.com/actions/runner/pull/2477 - pkg-version-devel: ${{ steps.outputs.outputs.pkg-version-devel }} - pkg-version-questing: ${{ steps.outputs.outputs.pkg-version-questing }} - pkg-version-plucky: ${{ steps.outputs.outputs.pkg-version-plucky }} - pkg-version-noble: ${{ steps.outputs.outputs.pkg-version-noble }} - pkg-dsc-devel: ${{ steps.outputs.outputs.pkg-dsc-devel }} - pkg-dsc-questing: ${{ steps.outputs.outputs.pkg-dsc-questing }} - pkg-dsc-plucky: ${{ steps.outputs.outputs.pkg-dsc-plucky }} - pkg-dsc-noble: ${{ steps.outputs.outputs.pkg-dsc-noble }} - pkg-src-changes-devel: ${{ steps.outputs.outputs.pkg-src-changes-devel }} - pkg-src-changes-questing: ${{ steps.outputs.outputs.pkg-src-changes-questing }} - pkg-src-changes-plucky: ${{ steps.outputs.outputs.pkg-src-changes-plucky }} - pkg-src-changes-noble: ${{ steps.outputs.outputs.pkg-src-changes-noble }} - - steps: - - name: Checkout authd code - uses: actions/checkout@v6 - - - name: Build debian packages and sources - uses: canonical/desktop-engineering/gh-actions/common/build-debian@main - with: - docker-image: ubuntu:${{ matrix.ubuntu-version }} - # Add the Go backports PPA if we're testing a Ubuntu release which - # doesn't have the required Go version in main. - extra-apt-repositories: ${{ (matrix.ubuntu-version == 'noble' || matrix.ubuntu-version == 'plucky' || matrix.ubuntu-version == 'questing') && 'ppa:ubuntu-enterprise-desktop/golang' || '' }} - # Extra build dependencies: - # - systemd-dev: Required to read compile time variables from systemd via pkg-config. - extra-source-build-deps: | - ca-certificates - git - libssl-dev - systemd-dev - extra-source-build-script: | - if [ "${{ matrix.ubuntu-version }}" == noble ]; then - cargo install --locked --root=/usr \ - cargo-vendor-filterer@${{ env.CARGO_VENDOR_FILTERER_VERSION }} - command -v cargo-vendor-filterer - fi - - # FIXME: Use dynamic outputs when possible: https://github.com/actions/runner/pull/2477 - - name: Generate outputs - id: outputs - run: | - ( - echo "pkg-name=${{ env.PKG_NAME }}" - echo "pkg-version-${{ matrix.ubuntu-version }}=${{ env.PKG_VERSION }}" - echo "pkg-dsc-${{ matrix.ubuntu-version }}=${{ env.PKG_DSC }}" - echo "pkg-src-changes-${{ matrix.ubuntu-version }}=${{ env.PKG_SOURCE_CHANGES }}" - ) >> "${GITHUB_OUTPUT}" - - check-modified-files: - name: Check modified files - runs-on: ubuntu-latest - needs: - - build-deb-package - outputs: - list: ${{ fromJSON(steps.git-diff.outputs.modified_files) }} - - steps: - - name: Checkout authd code - uses: actions/checkout@v6 - with: - fetch-depth: 100 - - - id: git-diff - name: Check modified files - run: | - set -ue - - base_ref=${{ github.event.pull_request.base.sha }} - if [ -z "${base_ref}" ]; then - base_ref=${{ github.event.before }} - fi - if [ -z "${base_ref}" ]; then - base_ref=$(git log --root --reverse -n1 --format=%H) - fi - - # Build a JSON array of modified paths. - modified_files=$(git diff --name-only "${base_ref}" HEAD | \ - while read line; do - jq -n --arg path "$line" '$path' - done | jq -n '. |= [inputs]') - echo "${modified_files}" - - escaped_json=$(echo "${modified_files}" | jq '.| tostring') - echo "modified_files=${escaped_json}" >> "${GITHUB_OUTPUT}" - - synchronize-packaging-branches: - name: Update packaging branch - runs-on: ubuntu-latest - needs: - - define-versions - - build-deb-package - permissions: - contents: write - strategy: - fail-fast: false - matrix: - ubuntu-version: ${{ fromJSON(needs.define-versions.outputs.ubuntu-versions) }} - env: - PACKAGING_BRANCH: ubuntu-packaging-${{ matrix.ubuntu-version }} - - # Run only on: - # - Push events to main - # - On github release - if: ${{ (github.event_name == 'push' && github.ref == 'refs/heads/main') || - github.event_name == 'release' }} - - steps: - # FIXME: Use dynamic outputs when possible: https://github.com/actions/runner/pull/2477 - - name: Setup job variables - run: | - set -exuo pipefail - - json_output='${{ toJSON(needs.build-deb-package.outputs) }}' - for var in $(echo "${json_output}" | jq -r 'keys | .[]'); do - if [[ "${var}" != *"-${{ matrix.ubuntu-version }}" ]]; then - continue; - fi - - v=$(echo "${json_output}" | jq -r ".\"${var}\"") - var="${var%-${{ matrix.ubuntu-version }}}" - echo "${var//-/_}=${v}" >> "${GITHUB_ENV}" - done - - - name: Download artifacts - uses: actions/download-artifact@v7 - with: - run-id: ${{ needs.build-deb-package.outputs.run-id }} - merge-multiple: true - - - name: Install dependencies - run: | - set -euo pipefail - - sudo apt-get update -y - sudo apt-get install -y --no-install-suggests --no-install-recommends \ - dpkg-dev devscripts - - - name: Checkout code - uses: actions/checkout@v6 - with: - fetch-depth: 100 - path: repo - - - name: Extract the debian sources - run: | - set -euo pipefail - - dpkg-source -x ${{ env.pkg_dsc }} sources - - - name: Commit packaging sources - run: | - set -exuo pipefail - - # Create or switch to the packaging branch - if git -C repo fetch --depth=1 origin "${{ env.PACKAGING_BRANCH }}:${{ env.PACKAGING_BRANCH }}"; then - git -C repo checkout "${{ env.PACKAGING_BRANCH }}" - else - git -C repo checkout -b "${{ env.PACKAGING_BRANCH }}" - fi - - # Replace the repository content with the package sources - mv repo/.git sources/ - cd sources - - # Drop the ubuntu version, as the PPA recipe will add it anyways - version=$(dpkg-parsechangelog -SVersion) - sanitized_version=$(echo "${version}" | sed "s,~[0-9.]\+\$,,") - perl -pe "s|\Q${version}\E|${sanitized_version}|" debian/changelog > \ - debian/changelog.sanitized - mv debian/changelog.sanitized debian/changelog - dpkg-parsechangelog - - git config --global user.name "Ubuntu Enterprise Desktop" - git config --global user.email "ubuntu-devel-discuss@lists.ubuntu.com" - - git add --all - git commit \ - --allow-empty \ - -m "Update ubuntu ${{ matrix.ubuntu-version }} package sources" \ - -m "Use upstream commit ${GITHUB_SHA}" - - - name: Push to packaging branch - run: | - set -exuo pipefail - - git -C sources push origin "${{ env.PACKAGING_BRANCH }}:${{ env.PACKAGING_BRANCH }}" - - run-autopkgtests: - name: Run autopkgtests - runs-on: ubuntu-latest - needs: - - define-versions - - build-deb-package - - check-modified-files - strategy: - fail-fast: false - matrix: - ubuntu-version: ${{ fromJSON(needs.define-versions.outputs.ubuntu-versions) }} - - # Run autopkgtests only on: - # - Push events to main - # - When a file in the debian subdir is modified - # - When this file is modified - # - On new tags - # - On github release - if: ${{ (github.event_name == 'push' && github.ref == 'refs/heads/main') || - contains(needs.check-modified-files.outputs.list, 'debian/') || - contains(needs.check-modified-files.outputs.list, '.github/workflows/build-deb.yaml') || - startsWith(github.ref, 'refs/tags/') || - github.event_name == 'release' }} - - steps: - # FIXME: Use dynamic outputs when possible: https://github.com/actions/runner/pull/2477 - - name: Setup job variables - run: | - set -exuo pipefail - - json_output='${{ toJSON(needs.build-deb-package.outputs) }}' - for var in $(echo "${json_output}" | jq -r 'keys | .[]'); do - if [[ "${var}" != *"-${{ matrix.ubuntu-version }}" ]]; then - continue; - fi - - v=$(echo "${json_output}" | jq -r ".\"${var}\"") - var="${var%-${{ matrix.ubuntu-version }}}" - echo "${var//-/_}=${v}" >> "${GITHUB_ENV}" - done - - - name: Download artifacts - uses: actions/download-artifact@v7 - with: - run-id: ${{ needs.build-deb-package.outputs.run-id }} - pattern: ${{ needs.build-deb-package.outputs.pkg-name }}_${{ env.pkg_version }}-* - merge-multiple: true - - - name: Run autopkgtests - uses: canonical/desktop-engineering/gh-actions/common/run-autopkgtest@main - with: - lxd-image: ubuntu:${{ matrix.ubuntu-version }} - source-changes: ${{ env.pkg_src_changes }} - autopkgtest-args: --add-apt-source=ppa:ubuntu-enterprise-desktop/golang diff --git a/.github/workflows/cla-check.yaml b/.github/workflows/cla-check.yaml deleted file mode 100644 index 566269953a..0000000000 --- a/.github/workflows/cla-check.yaml +++ /dev/null @@ -1,16 +0,0 @@ -name: Check if CLA is signed -on: [pull_request_target] - -jobs: - cla-check: - name: Check if CLA is signed - runs-on: ubuntu-latest - steps: - - name: Check if CLA signed - uses: canonical/has-signed-canonical-cla@v2 - with: - accept-existing-contributors: true - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true \ No newline at end of file diff --git a/.github/workflows/e2e-tests.yaml b/.github/workflows/e2e-tests.yaml new file mode 100644 index 0000000000..b80d0cb49e --- /dev/null +++ b/.github/workflows/e2e-tests.yaml @@ -0,0 +1,42 @@ +name: e2e-tests + +on: + workflow_dispatch: + schedule: + - cron: '0 0 * * 1' # Runs every Monday at midnight + pull_request: + paths: + - '**' + - '!.github/workflows/**' + - '.github/workflows/run-e2e-tests.yaml' + - '.github/workflows/provision-and-run-e2e-tests.yaml' + - '.github/workflows/e2e-tests.yaml' + - '!.gitignore' + - '!.golangci.yaml' + - '!AGENTS.md' + - '!CODE_OF_CONDUCT.md' + - '!CONTRIBUTING.md' + - '!COPYING' + - '!COPYING.LESSER' + - '!README.md' + - '!SECURITY.md' + - "!authd-oidc-brokers/po/**" + - "!authd-oidc-brokers/.gitignore" + - '!docs/**' + - '!examplebroker/**' + - '!gotestcov' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + provision-and-run-e2e-tests: + strategy: + matrix: + release: [noble, questing] + fail-fast: false + uses: ./.github/workflows/provision-and-run-e2e-tests.yaml + with: + release: ${{ matrix.release }} + secrets: inherit diff --git a/.github/workflows/git.yaml b/.github/workflows/git.yaml deleted file mode 100644 index 2555375d76..0000000000 --- a/.github/workflows/git.yaml +++ /dev/null @@ -1,25 +0,0 @@ -name: Git Checks - -on: [pull_request] - -jobs: - block-fixup: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v6 - - name: Block Fixup Commit Merge - run: | - PR_REF="${GITHUB_REF%/merge}/head" - BASE_REF="${GITHUB_BASE_REF}" - git fetch --no-tags --prune --progress --no-recurse-submodules --depth=1 origin "${BASE_REF}:__ci_base" - git fetch --no-tags --prune --progress --no-recurse-submodules --shallow-exclude="${BASE_REF}" origin "${PR_REF}:__ci_pr" - COMMIT_LIST=$(/usr/bin/git log --pretty=format:%s __ci_base..__ci_pr) - echo "Fixup commits:" - if echo "${COMMIT_LIST}" | grep -iE '^(fixup|squash|wip)'; then - exit 1 - fi - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true diff --git a/.github/workflows/provision-and-run-e2e-tests.yaml b/.github/workflows/provision-and-run-e2e-tests.yaml new file mode 100644 index 0000000000..b8f835c3c5 --- /dev/null +++ b/.github/workflows/provision-and-run-e2e-tests.yaml @@ -0,0 +1,131 @@ +name: provision-e2e-test (reusable) + +on: + workflow_call: + inputs: + release: + required: true + type: string + secrets: + E2E_VM_SSH_PRIV_KEY: + required: true + E2E_VM_SSH_PUB_KEY: + required: true + +env: + DEBIAN_FRONTEND: noninteractive + VM_NAME_BASE: e2e-runner + ARTIFACTS_DIR: /tmp/e2e-artifacts + E2E_TESTS_DIR: e2e-tests + OCI_REPO: ghcr.io/${{ github.repository }}/e2e-runner + +jobs: + provision-vm: + name: Provision VM + runs-on: ubuntu-latest + permissions: + contents: read + actions: write + packages: write + steps: + - name: Checkout repo + uses: actions/checkout@v6 + + - name: Compute OCI tag + id: compute-oci-tag + run: echo "oci_tag=$(echo ${{ hashFiles('e2e-tests/vm/**','.github/actions/e2e-tests/set-up-ssh-keys/**','.github/workflows/provision-and-run-e2e-tests.yaml') }})" >> $GITHUB_OUTPUT + + - name: Restore cached VM image (if available) + id: restore-cache + uses: actions/cache/restore@v4 + with: + path: ${{ env.ARTIFACTS_DIR }}/${{ env.VM_NAME_BASE }}-${{ inputs.release }}.qcow2 + key: >- + e2e-runner-vm-${{ inputs.release }}-${{ hashFiles( + 'e2e-tests/vm/**', + '.github/actions/e2e-tests/set-up-ssh-keys/**', + '.github/workflows/provision-and-run-e2e-tests.yaml' + ) }} + fail-on-cache-miss: false + + - uses: canonical/desktop-engineering/gh-actions/common/dpkg-install-speedup@main + if: steps.restore-cache.outputs.cache-hit != 'true' + + - name: Install APT dependencies for VM provisioning + if: steps.restore-cache.outputs.cache-hit != 'true' + run: ./${{ env.E2E_TESTS_DIR }}/vm/install-provision-deps.sh + + - name: Set up SSH keys for the VM provisioning + id: set-ssh-keys + if: steps.restore-cache.outputs.cache-hit != 'true' + uses: ./.github/actions/e2e-tests/set-up-ssh-keys + with: + E2E_VM_SSH_PRIV_KEY: ${{ secrets.E2E_VM_SSH_PRIV_KEY }} + E2E_VM_SSH_PUB_KEY: ${{ secrets.E2E_VM_SSH_PUB_KEY }} + + - name: Provision the VM + id: provision-vm + if: steps.restore-cache.outputs.cache-hit != 'true' + run: | + set -eu + export PATH="$(realpath "${{ env.E2E_TESTS_DIR }}/vm/helpers"):${PATH}" + mkdir -p ${{ env.ARTIFACTS_DIR }} + env VM_NAME_BASE="${{ env.VM_NAME_BASE }}" \ + RELEASE="${{ inputs.release }}" \ + ARTIFACTS_DIR="${{ env.ARTIFACTS_DIR }}" \ + SSH_PUBLIC_KEY_FILE="${{ steps.set-ssh-keys.outputs.keys-path }}/id_rsa.pub" \ + ./${{ env.E2E_TESTS_DIR }}/vm/provision-ubuntu.sh + + - name: Clean previous cache + if: always() && steps.provision-vm.outcome == 'success' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + run: | + set -eu + gh cache delete e2e-runner-${{ inputs.release }} || true + echo "Previous cache (if any) deleted" + + - name: Cache the VM image + uses: actions/cache/save@v4 + if: always() && steps.provision-vm.outcome == 'success' + with: + path: ${{ env.ARTIFACTS_DIR }}/${{ env.VM_NAME_BASE }}-${{ inputs.release }}.qcow2 + key: >- + e2e-runner-vm-${{ inputs.release }}-${{ hashFiles( + 'e2e-tests/vm/**', + '.github/actions/e2e-tests/set-up-ssh-keys/**', + '.github/workflows/provision-and-run-e2e-tests.yaml' + ) }} + + - uses: oras-project/setup-oras@v1 + - name: Upload VM image as OCI artifact + if: steps.provision-vm.outcome == 'success' || steps.restore-cache.outputs.cache-hit == 'true' + env: + IMAGE_PATH: ${{ env.ARTIFACTS_DIR }}/${{ env.VM_NAME_BASE }}-${{ inputs.release }}.qcow2 + run: | + set -euo pipefail + + echo "${{ secrets.GITHUB_TOKEN }}" \ + | oras login ghcr.io -u "${{ github.actor }}" --password-stdin + + # Chunk qcow2 into stable fixed-size layers (64 MiB) + WORKDIR=$(mktemp -d) + split -b 64M "$IMAGE_PATH" "$WORKDIR/chunk-" + + # Push as a layered OCI artifact (each chunk becomes a layer) + cd "$WORKDIR" + OCI_TAG=${{ steps.compute-oci-tag.outputs.oci_tag }} + oras push "${OCI_REPO}:${OCI_TAG}" chunk-* --artifact-type application/vnd.qemu.qcow2 + + e2e-test: + needs: provision-vm + strategy: + matrix: + broker: [authd-msentraid, authd-google] + fail-fast: false + uses: ./.github/workflows/run-e2e-tests.yaml + with: + release: ${{ inputs.release }} + broker: ${{ matrix.broker }} + secrets: inherit diff --git a/.github/workflows/qa.yaml b/.github/workflows/qa.yaml deleted file mode 100644 index c002aec618..0000000000 --- a/.github/workflows/qa.yaml +++ /dev/null @@ -1,403 +0,0 @@ -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' - - '!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' - - '!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: >- - apparmor-profiles - 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 - - 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@v6 - 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@v6 - 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@v6 - 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@v7 - with: - name: coverage-test-output - path: /tmp/coverage-test-output - - - name: Download raw coverage data - uses: actions/download-artifact@v7 - 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 - 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 - # 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 - - # For llvm-symbolizer - sudo apt-get install -y llvm - - 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() diff --git a/.github/workflows/run-e2e-tests.yaml b/.github/workflows/run-e2e-tests.yaml new file mode 100644 index 0000000000..5cb958b234 --- /dev/null +++ b/.github/workflows/run-e2e-tests.yaml @@ -0,0 +1,188 @@ +name: run-e2e-test (reusable) + +on: + workflow_call: + inputs: + release: + required: true + type: string + broker: + required: true + type: string + secrets: + E2E_VM_SSH_PRIV_KEY: + required: true + E2E_VM_SSH_PUB_KEY: + required: true + E2E_MSENTRA_ISSUER_ID: + required: false + E2E_MSENTRA_CLIENT_ID: + required: false + E2E_MSENTRA_USERNAME: + required: false + E2E_MSENTRA_PASSWORD: + required: false + E2E_MSENTRA_TOTP_SECRET: + required: false + E2E_GOOGLE_CLIENT_ID: + required: false + E2E_GOOGLE_CLIENT_SECRET: + required: false + E2E_GOOGLE_USERNAME: + required: false + E2E_GOOGLE_PASSWORD: + required: false + E2E_GOOGLE_TOTP_SECRET: + required: false + +env: + DEBIAN_FRONTEND: noninteractive + VM_NAME_BASE: e2e-runner + ARTIFACTS_DIR: /tmp/e2e-artifacts + E2E_TESTS_DIR: e2e-tests + OUTPUT_DIR: /tmp/e2e-${{ inputs.broker }}-${{ inputs.release }}/output + +jobs: + run-tests: + name: Run e2e-tests (${{ inputs.broker }}) + runs-on: ubuntu-latest + permissions: + contents: read + actions: write + packages: read + pages: write + id-token: write # required by actions/deploy-pages + steps: + - uses: oras-project/setup-oras@v1 + - name: Download VM image OCI artifact + env: + OCI_REPO: ghcr.io/${{ github.repository }}/e2e-runner + OCI_TAG: ${{ inputs.release }} + IMAGE_PATH: ${{ env.ARTIFACTS_DIR }}/${{ env.VM_NAME_BASE }}-${{ inputs.release }}.qcow2 + run: | + set -euo pipefail + + mkdir -p "$(dirname "$IMAGE_PATH")" + + echo "${{ secrets.GITHUB_TOKEN }}" \ + | oras login ghcr.io -u "${{ github.actor }}" --password-stdin + + oras pull "${OCI_REPO}:${OCI_TAG}" + cat chunk-* > "$IMAGE_PATH" + rm chunk-* + + - name: Checkout repo + uses: actions/checkout@v6 + + - uses: canonical/desktop-engineering/gh-actions/common/dpkg-install-speedup@main + + - name: Install APT dependencies for provisioning + run: ${{ env.E2E_TESTS_DIR }}/vm/install-provision-deps.sh + + - name: Set up SSH keys for the VM provisioning + id: set-ssh-keys + uses: ./.github/actions/e2e-tests/set-up-ssh-keys + with: + E2E_VM_SSH_PRIV_KEY: ${{ secrets.E2E_VM_SSH_PRIV_KEY }} + E2E_VM_SSH_PUB_KEY: ${{ secrets.E2E_VM_SSH_PUB_KEY }} + + - name: Provision authd and the broker + run: | + set -eux + export PATH="$(realpath "${{ env.E2E_TESTS_DIR }}/vm/helpers"):${PATH}" + + # Generate the libvirt domain XML file + template="${{ env.E2E_TESTS_DIR }}/vm/e2e-runner-template.xml" + env \ + IMAGE_FILE=${{ env.ARTIFACTS_DIR }}/${{ env.VM_NAME_BASE }}-${{ inputs.release }}.qcow2 \ + VM_NAME=${{ env.VM_NAME_BASE }}-${{ inputs.release }} \ + envsubst \ + < "${template}" \ + > "${{ env.ARTIFACTS_DIR }}/${{ env.VM_NAME_BASE }}.xml" + + # Create the provisioning config file + cat > "${{ env.E2E_TESTS_DIR }}/vm/config.sh" <<-EOF + export SSH_PUBLIC_KEY_FILE=${{ steps.set-ssh-keys.outputs.keys-path }}/id_rsa.pub + export VM_NAME_BASE=${{ env.VM_NAME_BASE }} + export RELEASE=${{ inputs.release }} + export BROKERS=${{ inputs.broker }} + + export AUTHD_MSENTRAID_ISSUER_ID=${{ secrets.E2E_MSENTRA_ISSUER_ID }} + export AUTHD_MSENTRAID_CLIENT_ID=${{ secrets.E2E_MSENTRA_CLIENT_ID }} + export AUTHD_MSENTRAID_USER=${{ secrets.E2E_MSENTRA_USERNAME }} + + export AUTHD_GOOGLE_CLIENT_ID=${{ secrets.E2E_GOOGLE_CLIENT_ID }} + export AUTHD_GOOGLE_CLIENT_SECRET=${{ secrets.E2E_GOOGLE_CLIENT_SECRET }} + export AUTHD_GOOGLE_USER=${{ secrets.E2E_GOOGLE_USERNAME }} + EOF + + # Provision the VM + ${{ env.E2E_TESTS_DIR }}/vm/provision-authd.sh + + - name: Checkout YARF repo + uses: actions/checkout@v6 + with: + repository: adombeck/yarf + path: ${{ env.E2E_TESTS_DIR }}/.yarf + + - name: Install APT dependencies for running the E2E tests + run: ${{ env.E2E_TESTS_DIR }}/install-deps.sh + + - name: Configure YARF + run: ${{ env.E2E_TESTS_DIR }}/setup-yarf.sh + + - name: Run tests + id: run-tests + run: | + set -eux + export PATH="$(realpath "${{ env.E2E_TESTS_DIR }}/vm/helpers"):${PATH}" + + # Set the credentials for the broker + if [ "${{ inputs.broker }}" = "authd-msentraid" ]; then + E2E_USER=${{ secrets.E2E_MSENTRA_USERNAME }} + E2E_PASSWORD=${{ secrets.E2E_MSENTRA_PASSWORD }} + TOTP_SECRET=${{ secrets.E2E_MSENTRA_TOTP_SECRET }} + elif [ "${{ inputs.broker }}" = "authd-google" ]; then + E2E_USER=${{ secrets.E2E_GOOGLE_USERNAME }} + E2E_PASSWORD=${{ secrets.E2E_GOOGLE_PASSWORD }} + TOTP_SECRET=${{ secrets.E2E_GOOGLE_TOTP_SECRET }} + fi + + # Run the tests + ${{ env.E2E_TESTS_DIR }}/run-tests.sh \ + --user "${E2E_USER}" \ + --password "${E2E_PASSWORD}" \ + --totp-secret "${TOTP_SECRET}" \ + --release "${{ inputs.release }}" \ + --broker "${{ inputs.broker }}" \ + --output-dir "${{ env.OUTPUT_DIR }}" + + - name: Upload test results + id: upload-results + if: always() + uses: actions/upload-artifact@v6 + with: + name: e2e-${{ inputs.broker }}-output-${{ inputs.release }}-${{ github.run_id }} + path: ${{ env.OUTPUT_DIR }} + + - name: Prepare Pages directory + if: always() + run: | + mkdir -p pages + cd pages + target_dir=pr/${{ github.event.pull_request.number }}/run-${{ github.run_id }} + mkdir -p "$target_dir" + cp ${{ env.OUTPUT_DIR }}/{log,report}.html "$target_dir" + OWNER="${{ github.repository_owner }}" + REPO="$(basename "${{ github.repository }}")" + echo "https://$OWNER.github.io/$REPO/$target_dir" + + - name: Upload Pages artifact + if: always() + uses: actions/upload-pages-artifact@v3 + with: + path: pages + + - name: Publish test results + if: always() + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/tics-run.yaml b/.github/workflows/tics-run.yaml deleted file mode 100644 index b0c9035261..0000000000 --- a/.github/workflows/tics-run.yaml +++ /dev/null @@ -1,82 +0,0 @@ -name: TICS QA Analysis - -on: - schedule: - - cron: '0 0 * * 1' # Runs every Monday at midnight - workflow_dispatch: - - -env: - DEBIAN_FRONTEND: noninteractive - build_dependencies: >- - clang-tools - clang - libglib2.0-dev - libpam-dev - libpwquality-dev - -jobs: - tics: - name: TIOBE TICS Framework - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: actions/setup-go@v6 - with: - go-version-file: go.mod - - - - name: Update Rust version - run: | - rustup update stable - - - uses: canonical/desktop-engineering/gh-actions/common/dpkg-install-speedup@main - - name: Install dependencies - run: | - set -eu - - sudo apt-get update - sudo apt-get install -y ${{ env.build_dependencies }} - - go install honnef.co/go/tools/cmd/staticcheck@latest - - - name: Fetch last successful QA run id - env: - GITHUB_TOKEN: ${{ github.token }} - run: | - set -eu - echo "LAST_QA_ID=$(gh run list --workflow 'QA & sanity checks' --limit 1 --status success --json databaseId -b main | jq '.[].databaseId')" >> $GITHUB_ENV - - - name: Download coverage artifact - uses: actions/download-artifact@v7 - with: - github-token: ${{ github.token }} - path: .artifacts/ - run-id: ${{ env.LAST_QA_ID }} - - - name: TICS Scan - env: - TICSAUTHTOKEN: ${{ secrets.TICSAUTHTOKEN }} - GH_TOKEN: ${{ github.token }} - run: | - set -e - - # Move coverage to expected directory - mkdir coverage - mv .artifacts/coverage/Cobertura.xml coverage/coverage.xml - - # Install TICS - . <(curl --silent --show-error 'https://canonical.tiobe.com/tiobeweb/TICS/api/public/v1/fapi/installtics/Script?cfg=GoProjects&platform=linux&url=https://canonical.tiobe.com/tiobeweb/TICS/') - - # TICS needs to build the artifacts in order to run the analysis. - # Since it uses the GOTOOLCHAIN=local stanza, it's better if we prebuild it to make sure that the Go - # toolchain setup by the action is properly updated to the one we defined in go.mod. Prebuilding also - # helps to speed up the TICS analysis, as we would already have the build cache populated. - find pam -name '*.so' -print -delete - go generate -C pam -x - go build ./cmd/authd - go -C ./authd-oidc-brokers build -o authd-vanilla ./cmd/authd-oidc - go -C ./authd-oidc-brokers build -tags=withmsentraid -o authd-msentraid ./cmd/authd-oidc - go -C ./authd-oidc-brokers build -tags=withgoogle -o authd-google ./cmd/authd-oidc - - TICSQServer -project authd -tmpdir /tmp/tics -branchdir . diff --git a/.github/workflows/validate-dependabot.yaml b/.github/workflows/validate-dependabot.yaml deleted file mode 100644 index d58c0e89e2..0000000000 --- a/.github/workflows/validate-dependabot.yaml +++ /dev/null @@ -1,23 +0,0 @@ -name: dependabot validate - -on: - pull_request: - paths: - - '.github/dependabot.yaml' - -jobs: - validate: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: marocchino/validate-dependabot@v3 - id: validate - - uses: marocchino/sticky-pull-request-comment@v2 - if: always() - with: - header: validate-dependabot - message: ${{ steps.validate.outputs.markdown }} - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true \ No newline at end of file diff --git a/.gitignore b/.gitignore index e03372947d..8c35dcd098 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,6 @@ snap/icon.svg snap/snapcraft.yaml authd-oidc-brokers/conf/authd.conf authd-oidc-brokers/conf/broker.conf + +# Useful for running e2e-tests locally +e2e-tests/e2e-tests.env diff --git a/authd-oidc-brokers/e2e-tests/resources/authd/Journal.py b/authd-oidc-brokers/e2e-tests/resources/authd/Journal.py deleted file mode 100644 index 576fba7695..0000000000 --- a/authd-oidc-brokers/e2e-tests/resources/authd/Journal.py +++ /dev/null @@ -1,65 +0,0 @@ -import os -from ansi2html import Ansi2HTMLConverter - -from robot.api import logger -from robot.api.deco import keyword, library # type: ignore -from robot.libraries.BuiltIn import BuiltIn - -import ExecUtils - -HOST_CID = 2 # 2 always refers to the host -PORT = 55000 - -@library -class Journal: - process = None - output_dir = None - - @keyword - async def start_receiving_journal(self) -> None: - """ - Start receiving journal entries from the VM via vsock. - """ - if self.process: - return - - output_dir = BuiltIn().get_variable_value('${OUTPUT DIR}', '.') - suite_name = BuiltIn().get_variable_value('${SUITE NAME}', 'unknown') - self.output_dir = os.path.join(output_dir, suite_name, "journal") - os.makedirs(self.output_dir, exist_ok=True) - - self.process = ExecUtils.Popen( - [ - "/lib/systemd/systemd-journal-remote", - f"--listen-raw=vsock:{HOST_CID}:{PORT}", - f"--output={self.output_dir}" - ], - ) - - @keyword - async def stop_receiving_journal(self) -> None: - """ - Stop receiving journal entries from the VM. - """ - if self.process: - self.process.terminate() - self.process.wait() - self.process = None - - @keyword - async def log_journal(self) -> None: - """ - Log the journal entries received from the VM. - """ - output = ExecUtils.check_output( - [ - 'journalctl', - '--no-pager', - '--directory', self.output_dir, - ], - env={'SYSTEMD_COLORS': 'true'}, - text=True, - ) - - html_output = Ansi2HTMLConverter(inline=True).convert(output, full=False) - logger.info(html_output, html=True) diff --git a/authd-oidc-brokers/e2e-tests/resources/authd/generate_totp.py b/authd-oidc-brokers/e2e-tests/resources/authd/generate_totp.py deleted file mode 100644 index 5d4e6a0c9b..0000000000 --- a/authd-oidc-brokers/e2e-tests/resources/authd/generate_totp.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import base64 -import hashlib -import hmac -import struct -import time - -def generate_totp(secret: str) -> str: - key = base64.b32decode(secret, True) - msg = struct.pack(">Q", int(time.time()) // 30) - hashed_obj = hmac.new(key, msg, hashlib.sha1).digest() - o = hashed_obj[19] & 15 - - totp_code = str((struct.unpack(">I", hashed_obj[o:o + 4])[0] & 0x7fffffff) % 1000000) - while len(totp_code) != 6: - totp_code += '0' - - return totp_code - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("totp_secret") - args = parser.parse_args() - - print(generate_totp(args.totp_secret)) diff --git a/authd-oidc-brokers/e2e-tests/tests/authd_disabled.robot b/authd-oidc-brokers/e2e-tests/tests/authd_disabled.robot deleted file mode 100644 index a5e3ebbc90..0000000000 --- a/authd-oidc-brokers/e2e-tests/tests/authd_disabled.robot +++ /dev/null @@ -1,48 +0,0 @@ -*** Settings *** -Resource ./resources/authd/utils.resource -Resource ./resources/authd/authd.resource - -Resource ./resources/broker/broker.resource - -Test Tags robot:exit-on-failure - -Test Setup Test Setup -Test Teardown Test Teardown - - -*** Keywords *** -Test Setup - Journal.Start Receiving Journal - Restore Snapshot %{BROKER}-edge-configured - -Test Teardown - Journal.Stop Receiving Journal - Journal.Log Journal - Log Videos On Error - - -*** Variables *** -${username} %{E2E_USER} - - -*** Test Cases *** -Test that disabling authd prevents remote logins - [Documentation] This test verifies that when authd is disabled, remote users cannot log in, while local users can still access the system. - - # Log in with local user - Log In - - # Disable authd - Disable Authd Socket And Service - - # Ensure local sudo user can still log in - Open Terminal - Enter Sudo Mode In Terminal - Close Terminal In Sudo Mode - - # Check that remote user cannot log in - Open Terminal In Sudo Mode - Try Log In With Remote User ${username} - Check That Log In Fails Because Authd Is Disabled - Cancel Operation - Close Terminal In Sudo Mode diff --git a/authd-oidc-brokers/e2e-tests/tests/broker_disabled.robot b/authd-oidc-brokers/e2e-tests/tests/broker_disabled.robot deleted file mode 100644 index 9014783aa6..0000000000 --- a/authd-oidc-brokers/e2e-tests/tests/broker_disabled.robot +++ /dev/null @@ -1,48 +0,0 @@ -*** Settings *** -Resource ./resources/authd/utils.resource -Resource ./resources/authd/authd.resource - -Resource ./resources/broker/broker.resource - -Test Tags robot:exit-on-failure - -Test Setup Test Setup -Test Teardown Test Teardown - - -*** Keywords *** -Test Setup - Journal.Start Receiving Journal - Restore Snapshot %{BROKER}-edge-configured - -Test Teardown - Journal.Stop Receiving Journal - Journal.Log Journal - Log Videos On Error - - -*** Variables *** -${username} %{E2E_USER} - - -*** Test Cases *** -Test that disabling broker prevents remote logins - [Documentation] This test verifies that when the broker is disabled, remote users cannot log in, while local users can still access the system. - - # Log in with local user - Log In - - # Disable broker - Disable Broker And Purge Config - - # Ensure local sudo user can still log in - Open Terminal - Enter Sudo Mode In Terminal - Close Terminal In Sudo Mode - - # Check that remote user cannot log in - Open Terminal In Sudo Mode - Try Log In With Remote User ${username} - Check That User Is Redirected To Local Broker - Cancel Operation - Close Terminal In Sudo Mode diff --git a/authd-oidc-brokers/e2e-tests/tests/config_issuer_invalid.robot b/authd-oidc-brokers/e2e-tests/tests/config_issuer_invalid.robot deleted file mode 100644 index 2ddae0d8d0..0000000000 --- a/authd-oidc-brokers/e2e-tests/tests/config_issuer_invalid.robot +++ /dev/null @@ -1,43 +0,0 @@ -*** Settings *** -Resource ./resources/authd/utils.resource -Resource ./resources/authd/authd.resource - -Resource ./resources/broker/broker.resource - -Test Tags robot:exit-on-failure - -Test Setup Test Setup -Test Teardown Test Teardown - - -*** Keywords *** -Test Setup - Journal.Start Receiving Journal - Restore Snapshot %{BROKER}-edge-configured - -Test Teardown - Journal.Stop Receiving Journal - Journal.Log Journal - Log Videos On Error - - -*** Variables *** -${username} %{E2E_USER} -${local_password} qwer1234 - - -*** Test Cases *** -Test that invalid broker issuer prevents remote logins - [Documentation] This test verifies that when the broker is configured with an invalid issuer, remote users cannot log in, while local users can still access the system. - - # Log in with local user - Log In - - # Change broker configuration to an invalid issuer - Change Broker Configuration issuer invalid - - # Try to log in with remote user when broker has invalid issuer - Open Terminal - Start Log In With Remote User Through CLI: QR Code ${username} - Select Provider - Check That Remote User Has No Available Authentication Modes diff --git a/authd-oidc-brokers/e2e-tests/tests/config_owner_auto_update.robot b/authd-oidc-brokers/e2e-tests/tests/config_owner_auto_update.robot deleted file mode 100644 index 5bf602ca9d..0000000000 --- a/authd-oidc-brokers/e2e-tests/tests/config_owner_auto_update.robot +++ /dev/null @@ -1,44 +0,0 @@ -*** Settings *** -Resource ./resources/authd/utils.resource -Resource ./resources/authd/authd.resource - -Resource ./resources/broker/broker.resource - -Test Tags robot:exit-on-failure - -Test Setup Test Setup -Test Teardown Test Teardown - - -*** Keywords *** -Test Setup - Journal.Start Receiving Journal - Restore Snapshot %{BROKER}-edge-configured - -Test Teardown - Journal.Stop Receiving Journal - Journal.Log Journal - Log Videos On Error - - -*** Variables *** -${username} %{E2E_USER} -${local_password} qwer1234 - - -*** Test Cases *** -Test that owner is auto-updated in broker configuration - [Documentation] This test verifies that when a local user logs in, the broker configuration is automatically updated to set the owner to the logged-in user. - - # Log in with local user - Log In - - # Try to log in with remote user - Open Terminal - Log In With Remote User Through CLI: QR Code ${username} ${local_password} - Log Out From Terminal Session - Close Focused Window - - # Check that owner was updated in broker configuration - Open Terminal In Sudo Mode - Check If Owner Was Registered ${username} diff --git a/authd-oidc-brokers/e2e-tests/tests/deny_login_if_user_not_allowed.robot b/authd-oidc-brokers/e2e-tests/tests/deny_login_if_user_not_allowed.robot deleted file mode 100644 index 191154c52c..0000000000 --- a/authd-oidc-brokers/e2e-tests/tests/deny_login_if_user_not_allowed.robot +++ /dev/null @@ -1,43 +0,0 @@ -*** Settings *** -Resource ./resources/authd/utils.resource -Resource ./resources/authd/authd.resource - -Resource ./resources/broker/broker.resource - -Test Tags robot:exit-on-failure - -Test Setup Test Setup -Test Teardown Test Teardown - - -*** Keywords *** -Test Setup - Journal.Start Receiving Journal - Restore Snapshot %{BROKER}-edge-configured - -Test Teardown - Journal.Stop Receiving Journal - Journal.Log Journal - Log Videos On Error - - -*** Variables *** -${username} %{E2E_USER} - - -*** Test Cases *** -Test that changing owner prevents remote logins - [Documentation] This test verifies that when the broker owner is changed to a different user, the original remote user cannot log in, while local users can still access the system. - - # Log in with local user - Log In - - # Change owner to another user - Change Broker Configuration owner different-user - - # Log in with remote user with device authentication - Open Terminal - Start Log In With Remote User Through CLI: QR Code ${username} - Select Provider - Continue Log In With Remote User: Authenticate In External Browser ${username} - Check That Remote User Is Not Allowed To Log In diff --git a/authd-oidc-brokers/e2e-tests/tests/deny_login_if_username_does_not_match.robot b/authd-oidc-brokers/e2e-tests/tests/deny_login_if_username_does_not_match.robot deleted file mode 100644 index fd7c7fbf81..0000000000 --- a/authd-oidc-brokers/e2e-tests/tests/deny_login_if_username_does_not_match.robot +++ /dev/null @@ -1,42 +0,0 @@ -*** Settings *** -Resource ./resources/authd/utils.resource -Resource ./resources/authd/authd.resource - -Resource ./resources/broker/broker.resource - -Test Tags robot:exit-on-failure - -Test Setup Test Setup -Test Teardown Test Teardown - - -*** Keywords *** -Test Setup - Journal.Start Receiving Journal - Restore Snapshot %{BROKER}-edge-configured - -Test Teardown - Journal.Stop Receiving Journal - Journal.Log Journal - Log Videos On Error - - -*** Variables *** -${username} %{E2E_USER} -${local_password} qwer1234 -${remote_group} %{E2E_USER}-group - - -*** Test Cases *** -Test that login fails if usernames do not match - [Documentation] This test verifies that when attempting to log in with a remote user whose username does not match the requested username, the login fails, while local users can still access the system. - - # Log in with local user - Log In - - # Fail to log in if usernames do not match - Open Terminal - Start Log In With Remote User Through CLI: QR Code different_user - Select Provider - Continue Log In With Remote User: Authenticate In External Browser ${username} - Check That Authenticated User Does Not Match Requested User different_user diff --git a/authd-oidc-brokers/e2e-tests/tests/login.robot b/authd-oidc-brokers/e2e-tests/tests/login.robot deleted file mode 100644 index 995b12b09a..0000000000 --- a/authd-oidc-brokers/e2e-tests/tests/login.robot +++ /dev/null @@ -1,45 +0,0 @@ -*** Settings *** -Resource ./resources/authd/utils.resource -Resource ./resources/authd/authd.resource -Resource ./resources/broker/broker.resource - -Test Tags robot:exit-on-failure - -Test Setup Test Setup -Test Teardown Test Teardown - - -*** Keywords *** -Test Setup - Journal.Start Receiving Journal - Restore Snapshot %{BROKER}-edge-configured - -Test Teardown - Journal.Stop Receiving Journal - Journal.Log Journal - Log Videos On Error - - -*** Variables *** -${username} %{E2E_USER} -${local_password} qwer1234 - - -*** Test Cases *** -Test login with CLI - [Documentation] Test login via CLI with device authentication and local password. - - # Log in with local user - Log In - - # Log in with remote user with device authentication - Open Terminal - Log In With Remote User Through CLI: QR Code ${username} ${local_password} - # Check remote user is properly added to the system - Check If User Was Added Properly ${username} - Log Out From Terminal Session - Close Focused Window - - # Log in with remote user with local password - Open Terminal In Sudo Mode - Log In With Remote User Through CLI: Local Password ${username} ${local_password} diff --git a/authd-oidc-brokers/e2e-tests/tests/migration_authd.robot b/authd-oidc-brokers/e2e-tests/tests/migration_authd.robot deleted file mode 100644 index 7be42a410a..0000000000 --- a/authd-oidc-brokers/e2e-tests/tests/migration_authd.robot +++ /dev/null @@ -1,58 +0,0 @@ -*** Settings *** -Resource ./resources/authd/utils.resource -Resource ./resources/authd/authd.resource - -Resource ./resources/broker/broker.resource - -Test Tags robot:exit-on-failure - -Test Setup Test Setup -Test Teardown Test Teardown - - -*** Keywords *** -Test Setup - Journal.Start Receiving Journal - Restore Snapshot %{BROKER}-stable-configured - -Test Teardown - Journal.Stop Receiving Journal - Journal.Log Journal - Log Videos On Error - - -*** Variables *** -${username} %{E2E_USER} -${local_password} qwer1234 -${remote_group} %{E2E_USER}-group - - -*** Test Cases *** -Test login after updating authd to edge version - [Documentation] Test login via CLI with device authentication and local password after switching to the edge PPA for authd. - - # Log in with local user - Log In - - # Log in with remote user with device authentication - Open Terminal - Log In With Remote User Through CLI: QR Code ${username} ${local_password} - # Check remote user is properly added to the system - Check If User Was Added Properly ${username} - Log Out From Terminal Session - Close Focused Window - - # Log in with remote user with local password - Open Terminal In Sudo Mode - Log In With Remote User Through CLI: Local Password ${username} ${local_password} - Log Out From Terminal Session - Close Terminal In Sudo Mode - - # Switch to the edge PPA for authd - Enable Edge Repository For Authd - Update And Upgrade Packages - - # Log in with remote user with local password after upgrading - Open Terminal In Sudo Mode - Log In With Remote User Through CLI: Local Password ${username} ${local_password} - Check Home Directory ${username} diff --git a/authd-oidc-brokers/e2e-tests/tests/migration_authd_broker.robot b/authd-oidc-brokers/e2e-tests/tests/migration_authd_broker.robot deleted file mode 100644 index f824226543..0000000000 --- a/authd-oidc-brokers/e2e-tests/tests/migration_authd_broker.robot +++ /dev/null @@ -1,59 +0,0 @@ -*** Settings *** -Resource ./resources/authd/utils.resource -Resource ./resources/authd/authd.resource - -Resource ./resources/broker/broker.resource - -Test Tags robot:exit-on-failure - -Test Setup Test Setup -Test Teardown Test Teardown - - -*** Keywords *** -Test Setup - Journal.Start Receiving Journal - Restore Snapshot %{BROKER}-stable-configured - -Test Teardown - Journal.Stop Receiving Journal - Journal.Log Journal - Log Videos On Error - - -*** Variables *** -${username} %{E2E_USER} -${local_password} qwer1234 -${remote_group} %{E2E_USER}-group - - -*** Test Cases *** -Test login after upgrading authd and broker to edge channel - [Documentation] This test verifies that after upgrading both authd and the broker to the edge channel, remote users can still log in using device authentication and local password, and their accounts are properly set up on the system. - - # Log in with local user - Log In - - # Log in with remote user with device authentication - Open Terminal - Log In With Remote User Through CLI: QR Code ${username} ${local_password} - # Check remote user is properly added to the system - Check If User Was Added Properly ${username} - Log Out From Terminal Session - Close Focused Window - - # Log in with remote user with local password - Open Terminal In Sudo Mode - Log In With Remote User Through CLI: Local Password ${username} ${local_password} - Log Out From Terminal Session - Close Terminal In Sudo Mode - - # Switch to the edge channel for the broker snap and the edge PPA for authd - Enable Edge Repository For Authd - Enable Edge Broker - Update And Upgrade Packages - - # Log in with remote user with local password after upgrading - Open Terminal In Sudo Mode - Log In With Remote User Through CLI: Local Password ${username} ${local_password} - Check Home Directory ${username} diff --git a/authd-oidc-brokers/e2e-tests/tests/migration_broker.robot b/authd-oidc-brokers/e2e-tests/tests/migration_broker.robot deleted file mode 100644 index d34693031c..0000000000 --- a/authd-oidc-brokers/e2e-tests/tests/migration_broker.robot +++ /dev/null @@ -1,58 +0,0 @@ -*** Settings *** -Resource ./resources/authd/utils.resource -Resource ./resources/authd/authd.resource - -Resource ./resources/broker/broker.resource - -Test Tags robot:exit-on-failure - -Test Setup Test Setup -Test Teardown Test Teardown - - -*** Keywords *** -Test Setup - Journal.Start Receiving Journal - Restore Snapshot %{BROKER}-stable-configured - -Test Teardown - Journal.Stop Receiving Journal - Journal.Log Journal - Log Videos On Error - - -*** Variables *** -${username} %{E2E_USER} -${local_password} qwer1234 -${remote_group} %{E2E_USER}-group - - -*** Test Cases *** -Test login with broker on edge channel - [Documentation] Test login with broker on edge channel with device authentication and local password, before and after upgrading authd and broker to edge channel. - - # Log in with local user - Log In - - # Log in with remote user with device authentication - Open Terminal - Log In With Remote User Through CLI: QR Code ${username} ${local_password} - # Check remote user is properly added to the system - Check If User Was Added Properly ${username} - Log Out From Terminal Session - Close Focused Window - - # Log in with remote user with local password - Open Terminal In Sudo Mode - Log In With Remote User Through CLI: Local Password ${username} ${local_password} - Log Out From Terminal Session - Close Terminal In Sudo Mode - - # Switch to edge channel for the broker snap - Enable Edge Broker - Update And Upgrade Packages - - # Log in with remote user with local password after upgrading - Open Terminal In Sudo Mode - Log In With Remote User Through CLI: Local Password ${username} ${local_password} - Check Home Directory ${username} diff --git a/authd-oidc-brokers/e2e-tests/tests/mixed_case_username.robot b/authd-oidc-brokers/e2e-tests/tests/mixed_case_username.robot deleted file mode 100644 index 87bf153ab3..0000000000 --- a/authd-oidc-brokers/e2e-tests/tests/mixed_case_username.robot +++ /dev/null @@ -1,47 +0,0 @@ -*** Settings *** -Resource ./resources/authd/utils.resource -Resource ./resources/authd/authd.resource - -Resource ./resources/broker/broker.resource - -Test Tags robot:exit-on-failure - -Test Setup Test Setup -Test Teardown Test Teardown - - -*** Keywords *** -Test Setup - Journal.Start Receiving Journal - Restore Snapshot %{BROKER}-edge-configured - -Test Teardown - Journal.Stop Receiving Journal - Journal.Log Journal - Log Videos On Error - - -*** Variables *** -${username} %{E2E_USER} -${local_password} qwer1234 -${remote_group} %{E2E_USER}-group - - -*** Test Cases *** -Test login with mixed case username - [Documentation] Test login with mixed case username via CLI with device authentication and local password. - - # Log in with local user - Log In - - # Log in with remote user using mixed case username with device authentication - Open Terminal - Log In With Remote User Through CLI: QR Code ${username} ${local_password} - # Check remote user is properly added to the system - Check If User Was Added Properly ${username} - Log Out From Terminal Session - Close Focused Window - - # Log in with remote user using mixed case username with local password - Open Terminal In Sudo Mode - Log In With Remote User Through CLI: Local Password ${username} ${local_password} diff --git a/authd-oidc-brokers/e2e-tests/tests/password_change.robot b/authd-oidc-brokers/e2e-tests/tests/password_change.robot deleted file mode 100644 index 344cc7e05d..0000000000 --- a/authd-oidc-brokers/e2e-tests/tests/password_change.robot +++ /dev/null @@ -1,53 +0,0 @@ -*** Settings *** -Resource ./resources/authd/utils.resource -Resource ./resources/authd/authd.resource - -Resource ./resources/broker/broker.resource - -Test Tags robot:exit-on-failure - -Test Setup Test Setup -Test Teardown Test Teardown - - -*** Keywords *** -Test Setup - Journal.Start Receiving Journal - Restore Snapshot %{BROKER}-edge-configured - -Test Teardown - Journal.Stop Receiving Journal - Journal.Log Journal - Log Videos On Error - - -*** Variables *** -${username} %{E2E_USER} -${local_password} qwer1234 -${new_password} passwd1234 -${remote_group} %{E2E_USER}-group - - -*** Test Cases *** -Test changing local password of remote user - [Documentation] This test verifies that a remote user can change their local password and subsequently log in using the new password. - - # Log in with local user - Log In - - # Log in with remote user with device authentication - Open Terminal - Log In With Remote User Through CLI: QR Code ${username} ${local_password} - Log Out From Terminal Session - Close Focused Window - - # Change local password of remote user - Open Terminal - Log In With Remote User Through CLI: Local Password ${username} ${local_password} - Change Password ${local_password} ${new_password} - Log Out From Terminal Session - Close Focused Window - - # Log in with remote user with local password - Open Terminal In Sudo Mode - Log In With Remote User Through CLI: Local Password ${username} ${new_password} diff --git a/authd-oidc-brokers/e2e-tests/tests/regenerate_qrcode.robot b/authd-oidc-brokers/e2e-tests/tests/regenerate_qrcode.robot deleted file mode 100644 index cb7c2d86f1..0000000000 --- a/authd-oidc-brokers/e2e-tests/tests/regenerate_qrcode.robot +++ /dev/null @@ -1,55 +0,0 @@ -*** Settings *** -Resource ./resources/authd/utils.resource -Resource ./resources/authd/authd.resource - -Resource ./resources/broker/broker.resource - -Test Tags robot:exit-on-failure - -Test Setup Test Setup -Test Teardown Test Teardown - - -*** Keywords *** -Test Setup - Journal.Start Receiving Journal - Restore Snapshot %{BROKER}-edge-configured - -Test Teardown - Journal.Stop Receiving Journal - Journal.Log Journal - Log Videos On Error - - -*** Variables *** -${username} %{E2E_USER} -${local_password} qwer1234 -${remote_group} %{E2E_USER}-group - - -*** Test Cases *** -Test login with CLI and QR code regeneration - [Documentation] This test verifies that a remote user can log in using device authentication via CLI with QR code regeneration, and subsequently log in using a local password. - - # Log in with local user - Log In - - # Log in with remote user with device authentication - Open Terminal - Start Log In With Remote User Through CLI: QR Code ${username} - Select Provider - # Let's try regenerating the QR code a couple of times - Regenerate QR Code - Regenerate QR Code - Regenerate QR Code - # Now we should be able to log in with the remote user using the latest QR code - Continue Log In With Remote User: Authenticate In External Browser ${username} - Continue Log In With Remote User Through CLI: Define Local Password ${username} ${local_password} - # Check remote user is properly added to the system - Check If User Was Added Properly ${username} - Log Out From Terminal Session - Close Focused Window - - # Log in with remote user with local password - Open Terminal In Sudo Mode - Log In With Remote User Through CLI: Local Password ${username} ${local_password} diff --git a/authd-oidc-brokers/e2e-tests/tests/ssh_deny_login_if_prefix_not_allowed.robot b/authd-oidc-brokers/e2e-tests/tests/ssh_deny_login_if_prefix_not_allowed.robot deleted file mode 100644 index 420d7418b7..0000000000 --- a/authd-oidc-brokers/e2e-tests/tests/ssh_deny_login_if_prefix_not_allowed.robot +++ /dev/null @@ -1,41 +0,0 @@ -*** Settings *** -Resource ./resources/authd/utils.resource -Resource ./resources/authd/authd.resource - -Resource ./resources/broker/broker.resource - -Test Tags robot:exit-on-failure - -Test Setup Test Setup -Test Teardown Test Teardown - - -*** Keywords *** -Test Setup - Journal.Start Receiving Journal - Restore Snapshot %{BROKER}-edge-configured - Change Broker Configuration ssh_allowed_suffixes_first_auth %{E2E_USER} - -Test Teardown - Journal.Stop Receiving Journal - Journal.Log Journal - Log Videos On Error - -*** Variables *** -${local_password} qwer1234 -${remote_group} %{E2E_USER}-group - - -*** Test Cases *** -Test that login is denied if user is not allowed to log in via SSH - [Documentation] Test that login via SSH is denied when the user is not allowed by the ssh_allowed_suffixes_first_auth setting. - - # Log in with local user - Log In - - # Try to log in with not allowed remote user with device authentication through SSH - ${domain} = Fetch From Right %{E2E_USER} @ - ${username} = Set Variable other-user@${domain} - Open Terminal - Start Log In With Remote User Through SSH: QR Code ${username} - Check That Login Is Handled By PAM Unix diff --git a/authd-oidc-brokers/e2e-tests/tests/ssh_login.robot b/authd-oidc-brokers/e2e-tests/tests/ssh_login.robot deleted file mode 100644 index c84106f1f4..0000000000 --- a/authd-oidc-brokers/e2e-tests/tests/ssh_login.robot +++ /dev/null @@ -1,48 +0,0 @@ -*** Settings *** -Resource ./resources/authd/utils.resource -Resource ./resources/authd/authd.resource - -Resource ./resources/broker/broker.resource - -Test Tags robot:exit-on-failure - -Test Setup Test Setup -Test Teardown Test Teardown - - -*** Keywords *** -Test Setup - Journal.Start Receiving Journal - Restore Snapshot %{BROKER}-edge-configured - Change Broker Configuration ssh_allowed_suffixes_first_auth %{E2E_USER} - -Test Teardown - Journal.Stop Receiving Journal - Journal.Log Journal - Log Videos On Error - - -*** Variables *** -${username} %{E2E_USER} -${local_password} qwer1234 -${remote_group} %{E2E_USER}-group - - -*** Test Cases *** -Test login with SSH - [Documentation] Test login via SSH with device authentication and local password. - - # Log in with local user - Log In - - # Log in with remote user with device authentication through SSH - Open Terminal - Log In With Remote User Through SSH: QR Code ${username} ${local_password} - # Check remote user is properly added to the system - Check If User Was Added Properly ${username} - Log Out From SSH Session - Close Focused Window - - # Log in with remote user with local password through SSH - Open Terminal - Log In With Remote User Through SSH: Local Password ${username} ${local_password} diff --git a/authd-oidc-brokers/e2e-tests/.gitignore b/e2e-tests/.gitignore similarity index 100% rename from authd-oidc-brokers/e2e-tests/.gitignore rename to e2e-tests/.gitignore diff --git a/authd-oidc-brokers/e2e-tests/install-deps.sh b/e2e-tests/install-deps.sh similarity index 82% rename from authd-oidc-brokers/e2e-tests/install-deps.sh rename to e2e-tests/install-deps.sh index e0723f8e74..d0b9032eb0 100755 --- a/authd-oidc-brokers/e2e-tests/install-deps.sh +++ b/e2e-tests/install-deps.sh @@ -3,7 +3,7 @@ set -euo pipefail # Install packages required for running the e2e tests -sudo apt-get -y install \ +sudo apt-get update && sudo apt-get -y install \ bsdutils \ ffmpeg \ gir1.2-webkit2-4.1 \ @@ -17,4 +17,5 @@ sudo apt-get -y install \ python3-gi \ python3-tk \ socat \ + systemd-journal-remote \ xvfb diff --git a/authd-oidc-brokers/e2e-tests/resources/authd-google/Browser.py b/e2e-tests/resources/authd-google/Browser.py similarity index 86% rename from authd-oidc-brokers/e2e-tests/resources/authd-google/Browser.py rename to e2e-tests/resources/authd-google/Browser.py index 894dc68bdd..76f5cf1fa8 100644 --- a/authd-oidc-brokers/e2e-tests/resources/authd-google/Browser.py +++ b/e2e-tests/resources/authd-google/Browser.py @@ -1,18 +1,18 @@ import os +import subprocess from robot.api.deco import keyword, library # type: ignore -from robot.libraries.Process import Process from robot.api import logger SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) def run_command(args): - result = Process().run_process(args[0], *args[1:]) - if result.rc == 0: + result = subprocess.run(args) + if result.returncode == 0: return cmd = " ".join(args) - logger.error(f"Command '{cmd}' failed:\n{result.stderr}") + logger.error(f"Command '{cmd}' failed with code {result.returncode}:\n{result.stderr}") raise RuntimeError(f"Command '{cmd}' failed") diff --git a/authd-oidc-brokers/e2e-tests/resources/authd-google/broker.resource b/e2e-tests/resources/authd-google/broker.resource similarity index 88% rename from authd-oidc-brokers/e2e-tests/resources/authd-google/broker.resource rename to e2e-tests/resources/authd-google/broker.resource index d1e3ed2ee5..be717fd450 100644 --- a/authd-oidc-brokers/e2e-tests/resources/authd-google/broker.resource +++ b/e2e-tests/resources/authd-google/broker.resource @@ -35,9 +35,7 @@ Log In With Remote User Through CLI: QR Code Start Log In With Remote User Through CLI: QR Code [Arguments] ${username} - Hid.Type String machinectl login - Hid.Keys Combo Return - Match Text ubuntu login: 60 + Try machinectl login Prompt Hid.Type String ${username} Hid.Keys Combo Return @@ -56,7 +54,7 @@ Select Provider Regenerate QR Code # As long as we are in the login process, we can regenerate the QR code - Match Text Request new login code 30 + Match Text Request new login code 120 Hid.Keys Combo Return Match Text google.com/device 15 @@ -79,7 +77,7 @@ Continue Log In With Remote User Through CLI: Define Local Password [Arguments] ${username} ${local_password} # The terminal should now be visible and focused again. # Check that we're prompted to set a local password. - Match Text New password: 60 + Match Text New password: 120 # Set a local password Hid.Type String ${local_password} @@ -91,23 +89,21 @@ Continue Log In With Remote User Through CLI: Define Local Password Hid.Keys Combo Return # Wait for the login to complete - ${timeout_sec} = Set Variable 30 + ${timeout_sec} = Set Variable 120 Match Text ${username}@ubuntu ${timeout_sec} Log In With Remote User Through CLI: Local Password [Arguments] ${username} ${local_password} - Hid.Type String machinectl login - Hid.Keys Combo Return - Match Text ubuntu login: 60 + Try machinectl login Prompt Hid.Type String ${username} Hid.Keys Combo Return Builtin.Sleep 2 - Match Text Enter your local password: 30 + Match Text Enter your local password: 120 Hid.Type String ${local_password} Hid.Keys Combo Return Builtin.Sleep 2 - Match Text ${username}@ubuntu:~$ 30 + Match Text ${username}@ubuntu:~$ 120 # Uses sed to change the broker configuration. @@ -153,39 +149,39 @@ Start Log In With Remote User Through SSH: QR Code Select Provider through SSH - Match Text 2. Google 30 - Match Text Choose your provider: 30 + Match Text 2. Google 120 + Match Text Choose your provider: 120 Hid.Type String 2 Hid.Keys Combo Return Continue Log In With Remote User Through SSH: QR Code - Match Text Choose action: 30 + Match Text Choose action: 120 Hid.Type String 1 Hid.Keys Combo Return Continue Log In With Remote User Through SSH: Define Local Password [Arguments] ${username} ${local_password} - Match Text Create a local password: 30 + Match Text Create a local password: 120 Hid.Type String ${local_password} Hid.Keys Combo Return - Match Text Confirm Password: 30 + Match Text Confirm Password: 120 Hid.Type String ${local_password} Hid.Keys Combo Return - Match Text ${username}@ubuntu:~$ 30 + Match Text ${username}@ubuntu:~$ 120 Log In With Remote User Through SSH: Local Password [Arguments] ${username} ${local_password} Hid.Type String ssh ${username}@localhost Hid.Keys Combo Return - Match Text Enter your local password: 30 + Match Text Enter your local password: 120 Hid.Type String ${local_password} Hid.Keys Combo Return - Match Text ${username}@ubuntu:~$ 30 + Match Text ${username}@ubuntu:~$ 120 Log In With Remote User Through GDM: QR Code @@ -210,7 +206,7 @@ Start Log In With Remote User Through GDM: QR Code Select Broker Through GDM - Match Text Select the broker 30 + Match Text Select the broker 120 Move Pointer To Google Left Button Click Builtin.Sleep 1 @@ -218,11 +214,11 @@ Select Broker Through GDM Continue Log In With Remote User Through GDM: Define Local Password [Arguments] ${local_password} - Match Text Create a local password 30 + Match Text Create a local password 120 Hid.Type String ${local_password} Hid.Keys Combo Return - Match Text Please, type the new passphrase again 30 + Match Text Please, type the new passphrase again 120 Hid.Type String ${local_password} Hid.Keys Combo Return @@ -255,7 +251,7 @@ Check If User Was Added Properly Check User Information [Arguments] ${username} - Match Text ${username}:x: 30 + Match Text ${username}:x: 120 Check Configuration Value @@ -266,28 +262,28 @@ Check Configuration Value Hid.Keys Combo Shift_L | Hid.Type String grep ${config_key} Hid.Keys Combo Return - Match Text ${expected_value} 30 + Match Text ${expected_value} 120 Check If Owner Was Registered [Arguments] ${username} Hid.Type String cat ${GOOGLE_BROKER_CFG_DIR}/20-owner-autoregistration.conf Hid.Keys Combo Return - Match Text owner = ${username} 30 + Match Text owner = ${username} 120 Check Home Directory [Arguments] ${username} Hid.Type String echo $HOME Hid.Keys Combo Return - Match Text /home/${username} 30 + Match Text /home/${username} 120 Hid.Type String stat -c %U:%G $HOME Hid.Keys Combo Return Match Text ${username}:${username} Check That Remote User Is Not Allowed To Log In - Match Text authentication failure: user not allowed in broker configuration 60 + Match Text authentication failure: user not allowed in broker configuration 120 Check That Login Is Handled By PAM Unix diff --git a/authd-oidc-brokers/e2e-tests/resources/authd-google/browser_login.py b/e2e-tests/resources/authd-google/browser_login.py similarity index 92% rename from authd-oidc-brokers/e2e-tests/resources/authd-google/browser_login.py rename to e2e-tests/resources/authd-google/browser_login.py index 52db47c174..94b9b08d55 100755 --- a/authd-oidc-brokers/e2e-tests/resources/authd-google/browser_login.py +++ b/e2e-tests/resources/authd-google/browser_login.py @@ -93,12 +93,18 @@ def login(browser, username: str, password: str, device_code: str, totp_secret: browser.send_key_taps( ascii_string_to_key_events(password) + [Gdk.KEY_Return]) + browser.wait_for_pattern("2-Step Verification") + browser.wait_for_stable_page() + browser.capture_snapshot(screenshot_dir, "device-login-enter-totp-code") + browser.send_key_taps( + ascii_string_to_key_events(generate_totp(totp_secret)) + [Gdk.KEY_Return]) + browser.wait_for_pattern("Choose an account") browser.wait_for_stable_page() browser.capture_snapshot(screenshot_dir, "device-login-choose-account") browser.send_key_taps([Gdk.KEY_Return]) - browser.wait_for_pattern("signing back in") + browser.wait_for_pattern("signing back in", timeout_ms=20000) browser.wait_for_stable_page() browser.capture_snapshot(screenshot_dir, "device-login-confirmation") # Sadly, just pressing Enter is not enough here, we need to tab to the correct button. diff --git a/authd-oidc-brokers/e2e-tests/resources/authd-msentraid/Browser.py b/e2e-tests/resources/authd-msentraid/Browser.py similarity index 86% rename from authd-oidc-brokers/e2e-tests/resources/authd-msentraid/Browser.py rename to e2e-tests/resources/authd-msentraid/Browser.py index 894dc68bdd..4b0af08a11 100644 --- a/authd-oidc-brokers/e2e-tests/resources/authd-msentraid/Browser.py +++ b/e2e-tests/resources/authd-msentraid/Browser.py @@ -1,18 +1,19 @@ import os +import subprocess from robot.api.deco import keyword, library # type: ignore -from robot.libraries.Process import Process from robot.api import logger + SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) def run_command(args): - result = Process().run_process(args[0], *args[1:]) - if result.rc == 0: + result = subprocess.run(args) + if result.returncode == 0: return cmd = " ".join(args) - logger.error(f"Command '{cmd}' failed:\n{result.stderr}") + logger.error(f"Command '{cmd}' failed with code {result.returncode}:\n{result.stderr}") raise RuntimeError(f"Command '{cmd}' failed") diff --git a/authd-oidc-brokers/e2e-tests/resources/authd-msentraid/broker.resource b/e2e-tests/resources/authd-msentraid/broker.resource similarity index 88% rename from authd-oidc-brokers/e2e-tests/resources/authd-msentraid/broker.resource rename to e2e-tests/resources/authd-msentraid/broker.resource index a2c8dd9610..5aab47368e 100644 --- a/authd-oidc-brokers/e2e-tests/resources/authd-msentraid/broker.resource +++ b/e2e-tests/resources/authd-msentraid/broker.resource @@ -39,9 +39,7 @@ Log In With Remote User Through CLI: QR Code Start Log In With Remote User Through CLI: QR Code [Arguments] ${username} - Hid.Type String machinectl login - Hid.Keys Combo Return - Match Text ubuntu login: 60 + Try machinectl login Prompt Hid.Type String ${username} Hid.Keys Combo Return @@ -60,7 +58,7 @@ Select Provider Regenerate QR Code # As long as we are in the login process, we can regenerate the QR code - Match Text Request new login code 30 + Match Text Request new login code 120 Hid.Keys Combo Return Match Text microsoft.com/devicelogin 15 @@ -83,7 +81,7 @@ Continue Log In With Remote User Through CLI: Define Local Password [Arguments] ${username} ${local_password} # The terminal should now be visible and focused again. # Check that we're prompted to set a local password. - Match Text New password: 60 + Match Text New password: 120 # Set a local password Hid.Type String ${local_password} @@ -95,23 +93,21 @@ Continue Log In With Remote User Through CLI: Define Local Password Hid.Keys Combo Return # Wait for the login to complete - ${timeout_sec} = Set Variable 30 + ${timeout_sec} = Set Variable 120 Match Text ${username}@ubuntu ${timeout_sec} Log In With Remote User Through CLI: Local Password [Arguments] ${username} ${local_password} - Hid.Type String machinectl login - Hid.Keys Combo Return - Match Text ubuntu login: 60 + Try machinectl login Prompt Hid.Type String ${username} Hid.Keys Combo Return Builtin.Sleep 2 - Match Text Enter your local password: 30 + Match Text Enter your local password: 120 Hid.Type String ${local_password} Hid.Keys Combo Return Builtin.Sleep 2 - Match Text ${username}@ubuntu:~$ 30 + Match Text ${username}@ubuntu:~$ 120 # Uses sed to change the broker configuration. @@ -157,39 +153,39 @@ Start Log In With Remote User Through SSH: QR Code Select Provider through SSH - Match Text 2. Microsoft Entra ID 30 - Match Text Choose your provider: 30 + Match Text 2. Microsoft Entra ID 120 + Match Text Choose your provider: 120 Hid.Type String 2 Hid.Keys Combo Return Continue Log In With Remote User Through SSH: QR Code - Match Text Choose action: 30 + Match Text Choose action: 120 Hid.Type String 1 Hid.Keys Combo Return Continue Log In With Remote User Through SSH: Define Local Password [Arguments] ${username} ${local_password} - Match Text Create a local password: 30 + Match Text Create a local password: 120 Hid.Type String ${local_password} Hid.Keys Combo Return - Match Text Confirm Password: 30 + Match Text Confirm Password: 120 Hid.Type String ${local_password} Hid.Keys Combo Return - Match Text ${username}@ubuntu:~$ 30 + Match Text ${username}@ubuntu:~$ 120 Log In With Remote User Through SSH: Local Password [Arguments] ${username} ${local_password} Hid.Type String ssh ${username}@localhost Hid.Keys Combo Return - Match Text Enter your local password: 30 + Match Text Enter your local password: 120 Hid.Type String ${local_password} Hid.Keys Combo Return - Match Text ${username}@ubuntu:~$ 30 + Match Text ${username}@ubuntu:~$ 120 Log In With Remote User Through GDM: QR Code @@ -204,6 +200,7 @@ Log In With Remote User Through GDM: QR Code Start Log In With Remote User Through GDM: QR Code [Arguments] ${username} Match Text Not listed 180 + Fail Move Pointer To Not listed Left Button Click @@ -214,7 +211,7 @@ Start Log In With Remote User Through GDM: QR Code Select Broker Through GDM - Match Text Select the broker 30 + Match Text Select the broker 120 Move Pointer To Microsoft Entra ID Left Button Click Builtin.Sleep 1 @@ -222,11 +219,11 @@ Select Broker Through GDM Continue Log In With Remote User Through GDM: Define Local Password [Arguments] ${local_password} - Match Text Create a local password 30 + Match Text Create a local password 120 Hid.Type String ${local_password} Hid.Keys Combo Return - Match Text Please, type the new passphrase again 30 + Match Text Please, type the new passphrase again 120 Hid.Type String ${local_password} Hid.Keys Combo Return @@ -262,12 +259,12 @@ Check If User Was Added Properly Check User Information [Arguments] ${username} - Match Text ${username}:x: 30 + Match Text ${username}:x: 120 Check User Groups [Arguments] ${username} ${remote_group} - Match Text ${username} sudo ${remote_group} 30 + Match Text ${username} sudo ${remote_group} 120 Check Configuration Value @@ -278,28 +275,28 @@ Check Configuration Value Hid.Keys Combo Shift_L | Hid.Type String grep ${config_key} Hid.Keys Combo Return - Match Text ${expected_value} 30 + Match Text ${expected_value} 120 Check If Owner Was Registered [Arguments] ${username} Hid.Type String cat ${ENTRAID_BROKER_CFG_DIR}/20-owner-autoregistration.conf Hid.Keys Combo Return - Match Text owner = ${username} 30 + Match Text owner = ${username} 120 Check Home Directory [Arguments] ${username} Hid.Type String echo $HOME Hid.Keys Combo Return - Match Text /home/${username} 30 + Match Text /home/${username} 120 Hid.Type String stat -c %U:%G $HOME Hid.Keys Combo Return Match Text ${username}:${username} Check That Remote User Is Not Allowed To Log In - Match Text authentication failure: user not allowed in broker configuration 60 + Match Text authentication failure: user not allowed in broker configuration 120 Check That Login Is Handled By PAM Unix diff --git a/authd-oidc-brokers/e2e-tests/resources/authd-msentraid/browser_login.py b/e2e-tests/resources/authd-msentraid/browser_login.py similarity index 100% rename from authd-oidc-brokers/e2e-tests/resources/authd-msentraid/browser_login.py rename to e2e-tests/resources/authd-msentraid/browser_login.py diff --git a/authd-oidc-brokers/e2e-tests/resources/authd/ExecUtils.py b/e2e-tests/resources/authd/ExecUtils.py similarity index 100% rename from authd-oidc-brokers/e2e-tests/resources/authd/ExecUtils.py rename to e2e-tests/resources/authd/ExecUtils.py diff --git a/e2e-tests/resources/authd/Journal.py b/e2e-tests/resources/authd/Journal.py new file mode 100644 index 0000000000..93639780e4 --- /dev/null +++ b/e2e-tests/resources/authd/Journal.py @@ -0,0 +1,132 @@ +import os +from ansi2html import Ansi2HTMLConverter +import select +import subprocess +import time + +from robot.api import logger +from robot.api.deco import keyword, library # type: ignore +from robot.libraries.BuiltIn import BuiltIn + +import ExecUtils +import VMUtils + +HOST_CID = 2 # 2 always refers to the host +PORT = 55000 + + +@library +class Journal: + process = None + output_dir = None + + @keyword + async def start_receiving_journal(self) -> None: + """ + Start receiving journal entries from the VM via vsock. + """ + if self.process: + return + + output_dir = BuiltIn().get_variable_value('${OUTPUT DIR}', '.') + suite_name = BuiltIn().get_variable_value('${SUITE NAME}', 'unknown') + self.output_dir = os.path.join(output_dir, suite_name, "journal") + os.makedirs(self.output_dir, exist_ok=True) + + if os.getenv("SYSTEMD_SUPPORTS_VSOCK"): + self.process = ExecUtils.Popen( + [ + "/lib/systemd/systemd-journal-remote", + f"--listen-raw=vsock:{HOST_CID}:{PORT}", + f"--output={self.output_dir}", + ], + ) + else: + self.process = stream_journal_from_vm_via_tcp(output_dir=self.output_dir) + + @keyword + async def stop_receiving_journal(self) -> None: + """ + Stop receiving journal entries from the VM. + """ + if self.process: + self.process.terminate() + self.process.wait() + self.process = None + + @keyword + async def log_journal(self) -> None: + """ + Log the journal entries received from the VM. + """ + output = ExecUtils.check_output( + [ + 'journalctl', + '--no-pager', + '--directory', self.output_dir, + ], + env={'SYSTEMD_COLORS': 'true'}, + text=True, + ) + + html_output = Ansi2HTMLConverter(inline=True).convert(output, full=False) + logger.info(html_output, html=True) + +def stream_journal_from_vm_via_tcp(output_dir, timeout=60): + vm_name = VMUtils.vm_name() + vm_ip = VMUtils.vm_ip() + deadline = time.time() + timeout + + while time.time() < deadline: + # Start socat to connect to the VM's TCP port + socat = subprocess.Popen( + ["socat", "-d", "-d", f"TCP:{vm_ip}:{PORT}", "-"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + bufsize=1, + ) + + connected = False + stderr_buf = [] + + # Read socat's stderr until we see a successful connection or timeout + while True: + r, _, _ = select.select([socat.stderr], [], [], 1) + if not r: + if socat.poll() is not None: + break + continue + + line = socat.stderr.readline() + if not line: + break + + stderr_buf.append(line) + + if "successfully connected" in line: + connected = True + break + + if not connected: + logger.error("".join(stderr_buf)) + socat.kill() + time.sleep(1) + continue + + # TCP connection confirmed + journal_remote = subprocess.Popen( + [ + "/lib/systemd/systemd-journal-remote", + f"--output={output_dir}/{vm_name}.journal", + "-", + ], + stdin=socat.stdout, + ) + + socat.stdout.close() + return journal_remote + + raise RuntimeError( + f"Failed to connect to VM journal stream within {timeout}s" + ) diff --git a/authd-oidc-brokers/e2e-tests/resources/authd/SSH.py b/e2e-tests/resources/authd/SSH.py similarity index 100% rename from authd-oidc-brokers/e2e-tests/resources/authd/SSH.py rename to e2e-tests/resources/authd/SSH.py diff --git a/authd-oidc-brokers/e2e-tests/resources/authd/Snapshot.py b/e2e-tests/resources/authd/Snapshot.py similarity index 53% rename from authd-oidc-brokers/e2e-tests/resources/authd/Snapshot.py rename to e2e-tests/resources/authd/Snapshot.py index 2f46157cc3..6951b54ca9 100644 --- a/authd-oidc-brokers/e2e-tests/resources/authd/Snapshot.py +++ b/e2e-tests/resources/authd/Snapshot.py @@ -1,17 +1,8 @@ -import os - -from robot.api import logger from robot.api.deco import keyword, library # type: ignore import ExecUtils +import VMUtils -VM_NAME_BASE="e2e-runner" - -def vm_name() -> str: - release = os.environ.get("RELEASE") - if not release: - raise Exception("RELEASE environment variable is not set") - return f"{VM_NAME_BASE}-{release}" @library class Snapshot: @@ -23,7 +14,8 @@ async def restore(self, name: str) -> None: Args: name: The name of the snapshot to revert to. """ + vm_name = VMUtils.vm_name() ExecUtils.run( - ["virsh", "snapshot-revert", vm_name(), name], + ["virsh", "snapshot-revert", vm_name, name], check=True, ) diff --git a/authd-oidc-brokers/e2e-tests/resources/authd/StringUtils.py b/e2e-tests/resources/authd/StringUtils.py similarity index 100% rename from authd-oidc-brokers/e2e-tests/resources/authd/StringUtils.py rename to e2e-tests/resources/authd/StringUtils.py diff --git a/e2e-tests/resources/authd/VMUtils.py b/e2e-tests/resources/authd/VMUtils.py new file mode 100644 index 0000000000..28d282aa38 --- /dev/null +++ b/e2e-tests/resources/authd/VMUtils.py @@ -0,0 +1,34 @@ +import os +import subprocess +import time + +VM_NAME_BASE="e2e-runner" + +def vm_name() -> str: + release = os.environ.get("RELEASE") + if not release: + raise Exception("RELEASE environment variable is not set") + return f"{VM_NAME_BASE}-{release}" + + +def vm_ip(timeout=60): + deadline = time.time() + timeout + + while time.time() < deadline: + p = subprocess.run( + ["virsh", "domifaddr", "--domain", vm_name(), "--source", "agent"], + stdout=subprocess.PIPE, + text=True, + check=False, + ) + + for line in p.stdout.splitlines(): + parts = line.split() + if len(parts) >= 4 and parts[2] == "ipv4": + ip = parts[3].split("/")[0] + if ip != "127.0.0.1": + return ip + + time.sleep(1) + + raise RuntimeError(f"Timed out waiting for IPv4 address of VM '{vm_name()}'") diff --git a/authd-oidc-brokers/e2e-tests/resources/authd/VideoLogger.py b/e2e-tests/resources/authd/VideoLogger.py similarity index 100% rename from authd-oidc-brokers/e2e-tests/resources/authd/VideoLogger.py rename to e2e-tests/resources/authd/VideoLogger.py diff --git a/authd-oidc-brokers/e2e-tests/resources/authd/authd.resource b/e2e-tests/resources/authd/authd.resource similarity index 88% rename from authd-oidc-brokers/e2e-tests/resources/authd/authd.resource rename to e2e-tests/resources/authd/authd.resource index f9af984d62..d6edcd6142 100644 --- a/authd-oidc-brokers/e2e-tests/resources/authd/authd.resource +++ b/e2e-tests/resources/authd/authd.resource @@ -56,29 +56,27 @@ Change Password Try Log In With Remote User [Arguments] ${username} - Hid.Type String machinectl login - Hid.Keys Combo Return - Match Text ubuntu login: 60 + Try machinectl login Prompt Hid.Type String ${username} Hid.Keys Combo Return # Conditional checks Check That Remote User Has No Available Authentication Modes - Match Text could not get authentication modes: no authentication modes available for user 30 + Match Text could not get authentication modes: Error connecting to provider. Check your network connection. 120 Check That Log In Fails Because Authd Is Disabled - Match Text could not connect to unix:///run/authd.sock 30 + Match Text could not connect to unix:///run/authd.sock 120 Check That User Is Redirected To Local Broker - Match Text Password: 30 + Match Text Password: 120 Check That Authenticated User Does Not Match Requested User [Arguments] ${requested_user} - Match Text Authentication failure: requested username "${requested_user}" does not match the authenticated username 30 + Match Text Authentication failure: requested username "${requested_user}" does not match the authenticated username 120 Check That Remote User Can Run Sudo Commands diff --git a/authd-oidc-brokers/e2e-tests/resources/authd/browser_window.py b/e2e-tests/resources/authd/browser_window.py similarity index 99% rename from authd-oidc-brokers/e2e-tests/resources/authd/browser_window.py rename to e2e-tests/resources/authd/browser_window.py index 513532ab3f..e0abf78259 100644 --- a/authd-oidc-brokers/e2e-tests/resources/authd/browser_window.py +++ b/e2e-tests/resources/authd/browser_window.py @@ -61,7 +61,6 @@ def on_event(_, event): and event.type != Gdk.EventType.KEY_RELEASE ): return - print(f"event: ({event}, {event.keyval})") return False self.web_view.add_events( @@ -428,6 +427,7 @@ def ascii_string_to_key_events(string): def render_video(screenshot_dir: str, video_path: str, framerate: int = 1): ExecUtils.check_call([ "ffmpeg", + "-loglevel", "warning", "-y", "-framerate", str(framerate), "-pattern_type", "glob", diff --git a/e2e-tests/resources/authd/generate_totp.py b/e2e-tests/resources/authd/generate_totp.py new file mode 100644 index 0000000000..6edb79364b --- /dev/null +++ b/e2e-tests/resources/authd/generate_totp.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +import argparse +import base64 +import hashlib +import hmac +import struct +import time + +TIME_WINDOW = 5 + +def generate_totp(secret: str) -> str: + # The code is generated according to the current time and is valid for 30 seconds. + # This means that if we generate the code just before the time window changes, + # it might be invalid by the time we use it. To avoid this, we make sure the time + # is safely within a new window before generating the code. + while time.time() % 30 > (30 - TIME_WINDOW): + continue + + key = base64.b32decode(secret, True) + msg = struct.pack(">Q", int(time.time()) // 30) + hashed_obj = hmac.new(key, msg, hashlib.sha1).digest() + o = hashed_obj[19] & 15 + + totp_code = (struct.unpack(">I", hashed_obj[o:o + 4])[0] & 0x7fffffff) % 1000000 + + return f"{totp_code:06d}" + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("totp_secret") + args = parser.parse_args() + + print(generate_totp(args.totp_secret)) diff --git a/authd-oidc-brokers/e2e-tests/resources/authd/ssh.sh b/e2e-tests/resources/authd/ssh.sh similarity index 100% rename from authd-oidc-brokers/e2e-tests/resources/authd/ssh.sh rename to e2e-tests/resources/authd/ssh.sh diff --git a/authd-oidc-brokers/e2e-tests/resources/authd/utils.resource b/e2e-tests/resources/authd/utils.resource similarity index 82% rename from authd-oidc-brokers/e2e-tests/resources/authd/utils.resource rename to e2e-tests/resources/authd/utils.resource index f0ac5db531..dfe5dac07a 100644 --- a/authd-oidc-brokers/e2e-tests/resources/authd/utils.resource +++ b/e2e-tests/resources/authd/utils.resource @@ -23,7 +23,7 @@ Log In Wait Until User Selected - Wait Until Keyword Succeeds 30 sec 1 sec + Wait Until Keyword Succeeds 120 sec 1 sec ... Try Select User @@ -51,10 +51,10 @@ Try Desktop Ready Open Terminal Run Command x-terminal-emulator Hid.Move Pointer To Proportional 1 1 - Match Text @ubuntu:~$ 30 + Match Text @ubuntu:~$ 120 Hid.Keys Combo F11 Hid.Move Pointer To Proportional 1 1 - Match Text @ubuntu:~$ 30 + Match Text @ubuntu:~$ 120 Open Terminal In Sudo Mode @@ -67,10 +67,10 @@ Enter Sudo Mode In Terminal Hid.Keys Combo Return Hid.Type String sudo -s Hid.Keys Combo Return - Match Text root@ubuntu 30 + Match Text root@ubuntu 120 Hid.Type String clear Hid.Keys Combo Return - Match Text root@ubuntu 30 + Match Text root@ubuntu 120 Close Focused Window @@ -80,14 +80,14 @@ Close Focused Window Close Terminal In Sudo Mode Hid.Type String clear Hid.Keys Combo Return - Match Text root@ubuntu 30 + Match Text root@ubuntu 120 Hid.Type String exit Hid.Keys Combo Return Close Focused Window Log Out From Terminal Session - Match Text @ubuntu 30 + Match Text @ubuntu 120 # We are in a machinectl session, so we need to ^] thrice to exit properly Hid.Keys Combo Control_L ] Hid.Keys Combo Control_L ] @@ -97,7 +97,7 @@ Log Out From Terminal Session Log Out From SSH Session Hid.Type String exit Hid.Keys Combo Return - Match Text ubuntu@ubuntu 30 + Match Text ubuntu@ubuntu 120 Run Command In Terminal @@ -154,6 +154,23 @@ Wait Until System Time Synced ... Sync System Time +Try machinectl login Prompt + VAR ${tries} 3 + WHILE ${tries} > 0 + Hid.Type String machinectl login + Hid.Keys Combo Return + ${match} = Run Keyword And Return Status + ... Match Text ubuntu login: 120 + IF '${match}' == 'True' + BREAK + END + Hid.Keys Combo Control_L ] + Hid.Keys Combo Control_L ] + Hid.Keys Combo Control_L ] + ${tries} = Evaluate ${tries} - 1 + END + + ### Setup ### Restore Snapshot diff --git a/authd-oidc-brokers/e2e-tests/resources/broker b/e2e-tests/resources/broker similarity index 100% rename from authd-oidc-brokers/e2e-tests/resources/broker rename to e2e-tests/resources/broker diff --git a/authd-oidc-brokers/e2e-tests/resources/kvm.resource b/e2e-tests/resources/kvm.resource similarity index 100% rename from authd-oidc-brokers/e2e-tests/resources/kvm.resource rename to e2e-tests/resources/kvm.resource diff --git a/authd-oidc-brokers/e2e-tests/resources/video_input_vars.py b/e2e-tests/resources/video_input_vars.py similarity index 100% rename from authd-oidc-brokers/e2e-tests/resources/video_input_vars.py rename to e2e-tests/resources/video_input_vars.py diff --git a/authd-oidc-brokers/e2e-tests/run-tests.sh b/e2e-tests/run-tests.sh similarity index 84% rename from authd-oidc-brokers/e2e-tests/run-tests.sh rename to e2e-tests/run-tests.sh index f9ba46c36a..54916f99e2 100755 --- a/authd-oidc-brokers/e2e-tests/run-tests.sh +++ b/e2e-tests/run-tests.sh @@ -29,8 +29,9 @@ Options: -s, --totp-secret Secret to generate OTP codes for the user's MFA (can also be set via TOTP_SECRET environment variable) -b, --broker Broker to test (can also be set via BROKER environment variable) -r, --release Ubuntu release to test (e.g., 'questing', can also be set via RELEASE environment variable) - --rerunfailed Re-run only the tests that failed in the previous run + -o, --output-dir DIR Directory to store test outputs (default: temporary directory) -h, --help Show this help message and exit + --rerunfailed Re-run only the tests that failed in the previous run EOF } @@ -40,6 +41,7 @@ TEST_RUNS_DIR="${XDG_RUNTIME_DIR}/authd-e2e-test-runs" # Parse command line arguments TESTS_TO_RUN="" +OUTPUT_DIR="" while [[ $# -gt 0 ]]; do key="$1" @@ -68,6 +70,10 @@ while [[ $# -gt 0 ]]; do RERUNFAILED=1 shift ;; + --output-dir|-o) + OUTPUT_DIR="$2" + shift 2 + ;; -h|--help) usage exit 0 @@ -107,6 +113,11 @@ if [ -n "${RERUNFAILED:-}" ] && [ -z "${PREVIOUS_TEST_RUN_DIR}" ]; then exit 1 fi +systemd_ver=$(systemctl --version | awk 'NR==1 {print $2}') +if dpkg --compare-versions "$systemd_ver" "ge" "256"; then + SYSTEMD_SUPPORTS_VSOCK=1 +fi + ROBOT_ARGS=() if [ -n "${RERUNFAILED:-}" ]; then echo "Rerunning failed tests from previous run in ${PREVIOUS_TEST_RUN_DIR}" @@ -129,6 +140,13 @@ TEST_RUN_DIR=$(mktemp -d --tmpdir="${TEST_RUNS_DIR}" "${BROKER}-XXXXXX") ln -sf --no-target-directory "${TEST_RUN_DIR}" "${TEST_RUNS_DIR}/${BROKER}-latest" cd "${TEST_RUN_DIR}" +if [ -z "${OUTPUT_DIR:-}" ]; then + OUTPUT_DIR=output + echo "No output directory specified, using current test run directory." +fi + +mkdir -p "${OUTPUT_DIR}" resources + # Activate YARF environment YARF_DIR="${ROOT_DIR}/.yarf" if [ ! -d "${YARF_DIR}" ]; then @@ -150,18 +168,21 @@ for test_file in $TESTS_TO_RUN; do ln -s "${test_file}" tests done -E2E_USER="$E2E_USER" \ -E2E_PASSWORD="$E2E_PASSWORD" \ -TOTP_SECRET="$TOTP_SECRET" \ -BROKER="$BROKER" \ -RELEASE="$RELEASE" \ -VNC_PORT="$VNC_PORT" \ -robot \ - --loglevel DEBUG \ - --pythonpath "${YARF_DIR}/yarf/rf_libraries/libraries/vnc" \ - "${ROBOT_ARGS[@]}" \ - "$@" \ - tests \ - || test_result=$? +env \ + E2E_USER="$E2E_USER" \ + E2E_PASSWORD="$E2E_PASSWORD" \ + TOTP_SECRET="$TOTP_SECRET" \ + BROKER="$BROKER" \ + RELEASE="$RELEASE" \ + VNC_PORT="$VNC_PORT" \ + SYSTEMD_SUPPORTS_VSOCK="${SYSTEMD_SUPPORTS_VSOCK:-}" \ + robot \ + --loglevel DEBUG \ + --pythonpath "${YARF_DIR}/yarf/rf_libraries/libraries/vnc" \ + --outputdir "${OUTPUT_DIR}" \ + "${ROBOT_ARGS[@]}" \ + "$@" \ + tests \ + || test_result=$? exit "${test_result:-0}" diff --git a/authd-oidc-brokers/e2e-tests/setup-yarf.sh b/e2e-tests/setup-yarf.sh similarity index 100% rename from authd-oidc-brokers/e2e-tests/setup-yarf.sh rename to e2e-tests/setup-yarf.sh diff --git a/authd-oidc-brokers/e2e-tests/testing.md b/e2e-tests/testing.md similarity index 100% rename from authd-oidc-brokers/e2e-tests/testing.md rename to e2e-tests/testing.md diff --git a/authd-oidc-brokers/e2e-tests/tests/login_gdm.robot b/e2e-tests/tests/login_gdm.robot similarity index 96% rename from authd-oidc-brokers/e2e-tests/tests/login_gdm.robot rename to e2e-tests/tests/login_gdm.robot index 7eff3deeb8..a5b4d9c253 100644 --- a/authd-oidc-brokers/e2e-tests/tests/login_gdm.robot +++ b/e2e-tests/tests/login_gdm.robot @@ -4,7 +4,7 @@ Resource ./resources/authd/authd.resource Resource ./resources/broker/broker.resource -Test Tags robot:exit-on-failure +# Test Tags robot:exit-on-failure Test Setup Test Setup Test Teardown Test Teardown @@ -12,8 +12,8 @@ Test Teardown Test Teardown *** Keywords *** Test Setup - Journal.Start Receiving Journal Restore Snapshot %{BROKER}-edge-configured + Journal.Start Receiving Journal Test Teardown Journal.Stop Receiving Journal diff --git a/authd-oidc-brokers/e2e-tests/tests/resources b/e2e-tests/tests/resources similarity index 100% rename from authd-oidc-brokers/e2e-tests/tests/resources rename to e2e-tests/tests/resources diff --git a/authd-oidc-brokers/e2e-tests/vm/cloud-init-template-noble.yaml b/e2e-tests/vm/cloud-init-template-noble.yaml similarity index 89% rename from authd-oidc-brokers/e2e-tests/vm/cloud-init-template-noble.yaml rename to e2e-tests/vm/cloud-init-template-noble.yaml index d582eb22ba..76e811f300 100644 --- a/authd-oidc-brokers/e2e-tests/vm/cloud-init-template-noble.yaml +++ b/e2e-tests/vm/cloud-init-template-noble.yaml @@ -27,7 +27,9 @@ packages: - gsettings-ubuntu-schemas - openssh-server - policykit-desktop-privileges - # Required to bind sshd to VSOCK socket + # Required to get the VM's IP address + - qemu-guest-agent + # Required to bind sshd to VSOCK socket and to forward the journal to the host - socat # Required for add-apt-repository - software-properties-common @@ -81,11 +83,11 @@ write_files: [Install] WantedBy=multi-user.target - # Export the journal via VSOCK - - path: /etc/systemd/system/journal-vsock-export.service + # Export the journal to the host + - path: /etc/systemd/system/journal-export.service content: | [Unit] - Description=Exports the journal over VSOCK + Description=Exports the journal to the host After=systemd-journald.service After=network.target Requires=network.target @@ -94,7 +96,7 @@ write_files: [Service] Type=simple - ExecStart=/bin/bash -o pipefail -c 'journalctl -b --lines=all -o export -f | socat - VSOCK-CONNECT:2:55000' + ExecStart=/bin/bash -o pipefail -c 'journalctl -b --lines=all -o export -f | socat - ${SOCAT_ADDRESS}' OOMScoreAdjust=-1000 Restart=always RestartSec=1 @@ -114,7 +116,7 @@ runcmd: - sudo systemctl mask systemd-networkd-wait-online.service # Enable our systemd services - sudo systemctl enable sshd-vsock.service - - sudo systemctl enable journal-vsock-export.service + - sudo systemctl enable journal-export.service # Avoid APT downloads and upgrades during the tests - sudo systemctl mask apt-daily.service - sudo systemctl mask apt-daily.timer diff --git a/authd-oidc-brokers/e2e-tests/vm/cloud-init-template-questing.yaml b/e2e-tests/vm/cloud-init-template-questing.yaml similarity index 76% rename from authd-oidc-brokers/e2e-tests/vm/cloud-init-template-questing.yaml rename to e2e-tests/vm/cloud-init-template-questing.yaml index cbf40a13bb..3a1eb731b1 100644 --- a/authd-oidc-brokers/e2e-tests/vm/cloud-init-template-questing.yaml +++ b/e2e-tests/vm/cloud-init-template-questing.yaml @@ -26,8 +26,12 @@ packages: - gsettings-ubuntu-schemas - openssh-server - policykit-desktop-privileges + # Required to get the VM's IP address + - qemu-guest-agent # Default terminal on questing - ptyxis + # Required to forward the journal to the host + - socat - spice-vdagent # Required for add-apt-repository - software-properties-common @@ -65,11 +69,26 @@ write_files: Match User *@* KbdInteractiveAuthentication yes - # Export the journal via VSOCK - - path: /etc/systemd/journald.conf.d/00-forward-to-vsock.conf + # Export the journal to the host + - path: /etc/systemd/system/journal-export.service content: | - [Journal] - ForwardToSocket=vsock:2:55000 + [Unit] + Description=Exports the journal to the host + After=systemd-journald.service + After=network.target + Requires=network.target + # Disable rate limiting + StartLimitIntervalSec=0 + + [Service] + Type=simple + ExecStart=/bin/bash -o pipefail -c 'journalctl -b --lines=all -o export -f | socat - ${SOCAT_ADDRESS}' + OOMScoreAdjust=-1000 + Restart=always + RestartSec=1 + + [Install] + WantedBy=basic.target runcmd: # Disable apport @@ -81,6 +100,8 @@ runcmd: - dconf update # Disable systemd-networkd-wait-online to speed up boot time - sudo systemctl mask systemd-networkd-wait-online.service + # Enable our systemd services + - sudo systemctl enable journal-export.service # Avoid APT downloads and upgrades during the tests - sudo systemctl mask apt-daily.service - sudo systemctl mask apt-daily.timer diff --git a/authd-oidc-brokers/e2e-tests/vm/config.sh.template b/e2e-tests/vm/config.sh.template similarity index 100% rename from authd-oidc-brokers/e2e-tests/vm/config.sh.template rename to e2e-tests/vm/config.sh.template diff --git a/authd-oidc-brokers/e2e-tests/vm/e2e-runner-template.xml b/e2e-tests/vm/e2e-runner-template.xml similarity index 90% rename from authd-oidc-brokers/e2e-tests/vm/e2e-runner-template.xml rename to e2e-tests/vm/e2e-runner-template.xml index 64d771a4f3..ca8d990dea 100644 --- a/authd-oidc-brokers/e2e-tests/vm/e2e-runner-template.xml +++ b/e2e-tests/vm/e2e-runner-template.xml @@ -36,6 +36,10 @@
+ + +
+ @@ -62,4 +66,5 @@ + diff --git a/e2e-tests/vm/helpers/virsh b/e2e-tests/vm/helpers/virsh new file mode 100755 index 0000000000..be0c547990 --- /dev/null +++ b/e2e-tests/vm/helpers/virsh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +exec sudo -E /usr/bin/virsh "$@" diff --git a/authd-oidc-brokers/e2e-tests/vm/install-provision-deps.sh b/e2e-tests/vm/install-provision-deps.sh similarity index 67% rename from authd-oidc-brokers/e2e-tests/vm/install-provision-deps.sh rename to e2e-tests/vm/install-provision-deps.sh index 26f82edbd5..39a5491969 100755 --- a/authd-oidc-brokers/e2e-tests/vm/install-provision-deps.sh +++ b/e2e-tests/vm/install-provision-deps.sh @@ -3,10 +3,14 @@ set -euo pipefail # Install packages required for provisioning the e2e-tests VM -sudo apt-get -y install \ +sudo apt-get update && sudo apt-get -y install \ bsdutils \ cloud-image-utils \ + guestfish \ libvirt-clients-qemu \ libvirt-daemon-system \ qemu-kvm \ + retry \ + socat \ + xvfb \ wget diff --git a/authd-oidc-brokers/e2e-tests/vm/lib/libprovision.sh b/e2e-tests/vm/lib/libprovision.sh similarity index 97% rename from authd-oidc-brokers/e2e-tests/vm/lib/libprovision.sh rename to e2e-tests/vm/lib/libprovision.sh index ff491a63ec..4af6cd6a28 100755 --- a/authd-oidc-brokers/e2e-tests/vm/lib/libprovision.sh +++ b/e2e-tests/vm/lib/libprovision.sh @@ -61,7 +61,7 @@ function wait_for_system_running() { retry --times 30 --delay 3 -- "$SSH" -- true # shellcheck disable=SC2016 local cmd='output=$(systemctl is-system-running --wait) || [ $output = degraded ]' - retry --times 3 --delay 3 -- timeout 30 -- "$SSH" -- "$cmd" + retry --times 3 --delay 3 -- timeout 30 "$SSH" -- "$cmd" } function reboot_system() { diff --git a/authd-oidc-brokers/e2e-tests/vm/provision-authd.sh b/e2e-tests/vm/provision-authd.sh similarity index 95% rename from authd-oidc-brokers/e2e-tests/vm/provision-authd.sh rename to e2e-tests/vm/provision-authd.sh index a17b7b499a..a1ab50ac9f 100755 --- a/authd-oidc-brokers/e2e-tests/vm/provision-authd.sh +++ b/e2e-tests/vm/provision-authd.sh @@ -63,14 +63,14 @@ assert_env_vars RELEASE VM_NAME_BASE BROKERS IFS=',' read -r -a BROKER_ARRAY <<< "${BROKERS}" -ARTIFACTS_DIR="${DATA_DIR}/${RELEASE}" +ARTIFACTS_DIR="${ARTIFACTS_DIR:-${DATA_DIR}/${RELEASE}}" if [ -z "${VM_NAME:-}" ]; then VM_NAME="${VM_NAME_BASE}-${RELEASE}" fi # Check if we have all required artifacts -IMAGE="${ARTIFACTS_DIR}/${VM_NAME_BASE}.qcow2" +IMAGE="${ARTIFACTS_DIR}/${VM_NAME}.qcow2" if [ ! -f "${IMAGE}" ]; then echo "Image not found: ${IMAGE}. Please run e2e-tests/vm/provision-ubuntu.sh first." exit 1 @@ -158,15 +158,14 @@ else # Ensure the VM is running to perform initial setup boot_system # Create a pre-authd setup snapshot - PRE_AUTHD_SNAPSHOT="" force_create_snapshot "$PRE_AUTHD_SNAPSHOT" fi # Install authd stable and create a snapshot -retry --times 3 --delay 1 -- timeout 30 -- "$SSH" -- \ +retry --times 3 --delay 1 -- timeout 30 "$SSH" -- \ "sudo add-apt-repository -y ppa:ubuntu-enterprise-desktop/authd" -timeout 600 -- \ +timeout 600 \ "$SSH" -- \ 'sudo apt-get install -y authd && \ sudo mkdir -p /etc/systemd/system/authd.service.d && \ @@ -187,10 +186,10 @@ virsh snapshot-delete --domain "${VM_NAME}" --snapshotname "authd-stable-install restore_snapshot_and_sync_time "$PRE_AUTHD_SNAPSHOT" # Install authd edge and create a snapshot -retry --times 3 --delay 1 -- timeout 30 -- "$SSH" -- \ +retry --times 3 --delay 1 -- timeout 30 "$SSH" -- \ "sudo add-apt-repository -y ppa:ubuntu-enterprise-desktop/authd-edge" -timeout 600 -- \ +timeout 600 \ "$SSH" -- \ 'sudo apt-get install -y authd && \ sudo mkdir -p /etc/systemd/system/authd.service.d && \ diff --git a/authd-oidc-brokers/e2e-tests/vm/provision-ubuntu.sh b/e2e-tests/vm/provision-ubuntu.sh similarity index 82% rename from authd-oidc-brokers/e2e-tests/vm/provision-ubuntu.sh rename to e2e-tests/vm/provision-ubuntu.sh index d3c6f1ba61..dddf32d723 100755 --- a/authd-oidc-brokers/e2e-tests/vm/provision-ubuntu.sh +++ b/e2e-tests/vm/provision-ubuntu.sh @@ -90,7 +90,7 @@ sudo -v # Installing all the packages can take some time, so we set the timeout to 15 minutes CLOUT_INIT_TIMEOUT=900 -ARTIFACTS_DIR="${DATA_DIR}/${RELEASE}" +ARTIFACTS_DIR="${ARTIFACTS_DIR:-${DATA_DIR}/${RELEASE}}" CLOUD_INIT_TEMPLATE="${SCRIPT_DIR}/cloud-init-template-${RELEASE}.yaml" if [ -z "${VM_NAME:-}" ]; then @@ -102,6 +102,15 @@ function cloud_init_finished() { sudo guestfish --ro -a "${image}" -i stat /var/lib/cloud/instance/boot-finished &>/dev/null } +function cloud_init_successful() { + local image=$1 + if ! sudo guestfish --ro -a "${image}" -i cat /var/lib/cloud/data/result.json | grep -q '"errors": \[\]'; then + # Print the result.json to show the errors + sudo guestfish --ro -a "${image}" -i cat /var/lib/cloud/data/result.json + return 1 + fi +} + # Print executed commands to ease debugging set -x @@ -110,7 +119,7 @@ IMAGE_URL="https://cloud-images.ubuntu.com/${RELEASE}/current/${RELEASE}-server- SOURCE_IMAGE="${CACHE_DIR}/$(basename "${IMAGE_URL}")" if [ ! -f "${SOURCE_IMAGE}" ]; then mkdir -p "${CACHE_DIR}" - wget -O "${SOURCE_IMAGE}" "${IMAGE_URL}" + wget "${CI:+--progress=dot:giga}" -O "${SOURCE_IMAGE}" "${IMAGE_URL}" else echo "Source image already exists: ${SOURCE_IMAGE}" fi @@ -131,7 +140,7 @@ if [ "${FORCE:-}" = true ]; then fi # Copy and resize the image -IMAGE="${ARTIFACTS_DIR}/${VM_NAME_BASE}.qcow2" +IMAGE="${ARTIFACTS_DIR}/${VM_NAME}.qcow2" if [ ! -f "${IMAGE}" ]; then mkdir -p "${ARTIFACTS_DIR}" cp "${SOURCE_IMAGE}" "${IMAGE}" @@ -147,8 +156,16 @@ if [ ! -f "${CLOUD_INIT_ISO}" ]; then CLOUD_INIT_DIR="$(mktemp -d)" trap 'rm -rf ${CLOUD_INIT_DIR}' EXIT + systemd_ver=$(systemctl --version | awk 'NR==1 {print $2}') + if dpkg --compare-versions "$systemd_ver" "ge" "256"; then + SOCAT_ADDRESS="VSOCK-CONNECT:2:55000" + else + SOCAT_ADDRESS="TCP-LISTEN:55000,bind=0.0.0.0,reuseaddr" + fi + SSH_PUBLIC_KEY=$(cat "${SSH_PUBLIC_KEY_FILE}") \ - envsubst < "${CLOUD_INIT_TEMPLATE}" > "${CLOUD_INIT_DIR}/user-data" + SOCAT_ADDRESS="${SOCAT_ADDRESS}" \ + envsubst < "${CLOUD_INIT_TEMPLATE}" > "${CLOUD_INIT_DIR}/user-data" cloud-localds "${CLOUD_INIT_ISO}" "${CLOUD_INIT_DIR}/user-data" else @@ -193,9 +210,18 @@ if ! cloud_init_finished "${IMAGE}"; then echo "Waiting for VM to finish cloud-init setup..." script -q -e -f /dev/null -c "virsh console $VM_NAME" & VM_CONSOLE_PID=$! - virsh await "${VM_NAME}" --condition domain-inactive --timeout "${CLOUT_INIT_TIMEOUT}" + + timeout "${CLOUT_INIT_TIMEOUT}" retry --delay 1 -- \ + sh -c "sudo virsh domstate \"${VM_NAME}\" | grep -q '^shut off'" + kill "${VM_CONSOLE_PID}" || true + # Check if the cloud-init finished successfully + if ! cloud_init_successful "${IMAGE}"; then + echo "cloud-init did not finish successfully." >&2 + exit 1 + fi + # Detach the cloud-init ISO virsh detach-disk "${VM_NAME}" vdb --config diff --git a/authd-oidc-brokers/e2e-tests/vm/provision.sh b/e2e-tests/vm/provision.sh similarity index 100% rename from authd-oidc-brokers/e2e-tests/vm/provision.sh rename to e2e-tests/vm/provision.sh diff --git a/authd-oidc-brokers/e2e-tests/vm/ssh.sh b/e2e-tests/vm/ssh.sh similarity index 100% rename from authd-oidc-brokers/e2e-tests/vm/ssh.sh rename to e2e-tests/vm/ssh.sh