name: E2E Test Coverage
on: 
  push:
    paths-ignore:
      - "**.md"
      - "channel.yaml"
      - "install.sh"
      - "tests/**"
      - "!tests/e2e**"
      - "!tests/docker**"
      - ".github/**"
      - "!.github/actions/**"
      - "!.github/workflows/e2e.yaml"
  pull_request:
    paths-ignore:
      - "**.md"
      - "channel.yaml"
      - "install.sh"
      - "tests/**"
      - "!tests/e2e**"
      - "!tests/docker**"
      - ".github/**"
      - "!.github/actions/**"
      - "!.github/workflows/e2e.yaml"
  workflow_dispatch: {}

permissions:
  contents: read

jobs:
  build:
    uses: ./.github/workflows/build-k3s.yaml
    with:
      upload-image: true
  e2e:
    name: "E2E Tests"
    needs: build
    runs-on: ubuntu-24.04
    timeout-minutes: 40
    strategy:
      fail-fast: false
      matrix:
        etest: [autoimport, startup, s3, btrfs, externalip, privateregistry, embeddedmirror, wasm]
      max-parallel: 3
    steps:
      - name: "Checkout"
        uses: actions/checkout@v4
        with: {fetch-depth: 1}
      
      - name: Set up vagrant and libvirt
        uses: ./.github/actions/vagrant-setup
      - name: "Vagrant Cache"
        uses: actions/cache@v4
        with:
          path: |
             ~/.vagrant.d/boxes
          key: vagrant-box-ubuntu-2404
      - name: "Vagrant Plugin(s)"
        run: vagrant plugin install vagrant-k3s vagrant-reload vagrant-scp
      
      - name: Install Go
        uses: actions/setup-go@v5
        with: 
          go-version-file: 'go.mod'
          cache: false
      - name: Install Kubectl
        run: |
           curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
           sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
      - name: "Download k3s binary"
        uses: actions/download-artifact@v4
        with:
          name: k3s
          path: ./dist/artifacts
      
      - name: Run ${{ matrix.etest }} Test
        env:
          E2E_GOCOVER: "true"
        run: | 
          chmod +x ./dist/artifacts/k3s
          cd tests/e2e/${{ matrix.etest }}
          go test -v -timeout=45m ./${{ matrix.etest}}_test.go -ci -local
      - name: On Failure, Upload Journald Logs
        uses: actions/upload-artifact@v4
        if: ${{ failure() }}
        with:
          name: ${{ matrix.etest}}-journald-logs
          path: tests/e2e/${{ matrix.etest }}/*-jlog.txt
          retention-days: 30
      - name: On Failure, Launch Debug Session
        uses: lhotari/action-upterm@v1
        if: ${{ failure() }}
        with:
          ## If no one connects after 5 minutes, shut down server.
          wait-timeout-minutes: 5
      - name: Upload Results To Codecov
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: tests/e2e/${{ matrix.etest }}/coverage.out
          flags: e2etests # optional
          verbose: true # optional (default = false)
  docker:
    needs: build
    name: Docker Tests
    runs-on: ubuntu-latest
    timeout-minutes: 20
    strategy:
      fail-fast: false
      matrix:
        dtest: [basics, bootstraptoken, cacerts, compat, lazypull, upgrade]
    steps:
    - name: Checkout
      uses: actions/checkout@v4
      with:
        fetch-depth: 1
    - name: "Download k3s image"
      uses: actions/download-artifact@v4
      with:
        name: k3s
        path: ./dist/artifacts
    - name: Load k3s image
      run: docker image load -i ./dist/artifacts/k3s-image.tar
    - name: Run ${{ matrix.dtest }} Test
      run: | 
        chmod +x ./dist/artifacts/k3s
        . ./scripts/version.sh
        . ./tests/docker/test-helpers
        . ./tests/docker/test-run-${{ matrix.dtest }}
        echo "Did test-run-${{ matrix.dtest }} pass $?"
  
  build-go-tests:
    name: "Build Go Tests"
    runs-on: ubuntu-latest
    outputs:
      branch_name: ${{ steps.branch_step.outputs.BRANCH_NAME }}
    steps:
    - name: Checkout
      uses: actions/checkout@v4
    - name: Install Go
      uses: ./.github/actions/setup-go
    - name: Build Go Tests
      run: |
        mkdir -p ./dist/artifacts
        go test -c -ldflags="-w -s" -o ./dist/artifacts ./tests/docker/...
    - name: Upload Go Tests
      uses: actions/upload-artifact@v4
      with:
        name: docker-go-tests
        path: ./dist/artifacts/*.test
        compression-level: 9
        retention-days: 1
    # For upgrade and skew tests, we need to know the branch name this run is based off.
    # Since this is predetermined, we can run this step before the docker-go job, saving time.
    # For PRs we can use the base_ref (ie the target branch of the PR).
    # For pushes to k3s-io/k3s from dependabot or updatecli, use master
    # All other pushes should be a valid ref, master or release-1.XX.
    # For pushes to a fork, we need to determine the branch name by finding the parent branch from git show-branch history.
    - name: Determine branch name
      id: branch_step
      run: |
        if [ ${{ github.repository }} = "k3s-io/k3s" ]; then 
          BRANCH_NAME=$(echo ${{ github.base_ref || github.ref_name }})
          if [[ $BRANCH_NAME =~ ^(dependabot|updatecli) ]]; then
            BRANCH_NAME=master
          fi
        elif [ -z "${{ github.base_ref }}" ]; then
          # We are in a fork, and need some git history to determine the branch name
          # For some reason, the first fetch doesn't always get the full history, so we sleep and fetch again
          git fetch origin --depth=100 +refs/heads/*:refs/remotes/origin/*
          sleep 5
          git fetch origin --depth=100 +refs/heads/*:refs/remotes/origin/*
          BRANCH_NAME=$(git show-branch -a 2> /dev/null  | grep '\*' | grep -v `git rev-parse --abbrev-ref HEAD` | head -n1 |  sed 's/.*\[\(.*\/\)\(.*\)\].*/\2/' | sed 's/[\^~].*//')
        else
          BRANCH_NAME=${{ github.base_ref }}
        fi
        echo "Branch Name is $BRANCH_NAME"
        echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_OUTPUT
    # branch name should be either master or release-1.XX
    - name: Fail if branch name does not match pattern
      run: |
        if [[ ! ${{ steps.branch_step.outputs.branch_name }} =~ ^(master|release-[0-9]+\.[0-9]+)$ ]]; then
          echo "Branch name ${{ steps.branch_step.outputs.branch_name }} does not match pattern"
          echo "If this is a PR/fork, ensure you have recently rebased off master/release-1.XX branch"
          exit 1
        fi

  docker-go:
    needs: [build, build-go-tests]
    name: Docker Tests In GO
    runs-on: ubuntu-latest
    timeout-minutes: 20
    strategy:
      fail-fast: false
      matrix:
        dtest: [basics, bootstraptoken, cacerts, etcd, lazypull, skew, upgrade]
    env:
      BRANCH_NAME: ${{ needs.build-go-tests.outputs.branch_name }}
    steps:
    - name: Checkout
      uses: actions/checkout@v4
    - name: "Download K3s image"
      uses: actions/download-artifact@v4
      with:
        name: k3s
        path: ./dist/artifacts
    - name: Load and set K3s image
      run: |
        docker image load -i ./dist/artifacts/k3s-image.tar
        IMAGE_TAG=$(docker image ls --format '{{.Repository}}:{{.Tag}}' | grep 'rancher/k3s')
        echo "K3S_IMAGE=$IMAGE_TAG" >> $GITHUB_ENV
    - name: Download Go Tests
      uses: actions/download-artifact@v4
      with:
        name: docker-go-tests
        path: ./dist/artifacts
    - name: Run ${{ matrix.dtest }} Test
      # Put the compiled test binary back in the same place as the test source
      run: |
        chmod +x ./dist/artifacts/${{ matrix.dtest }}.test
        mv ./dist/artifacts/${{ matrix.dtest }}.test ./tests/docker/${{ matrix.dtest }}/
        cd ./tests/docker/${{ matrix.dtest }}
        if [ ${{ matrix.dtest }} = "upgrade" ] || [ ${{ matrix.dtest }} = "skew" ]; then
          ./${{ matrix.dtest }}.test -k3sImage=$K3S_IMAGE -branch=$BRANCH_NAME
        else
          ./${{ matrix.dtest }}.test -k3sImage=$K3S_IMAGE
        fi