# This currently test docker and nerdctl on windows (w/o canary)
# Structure is in to allow testing nerdctl on linux as well, though more work is required to make it functional.
name: job-test-in-host

on:
  workflow_call:
    inputs:
      timeout:
        required: true
        type: number
      runner:
        required: true
        type: string
      canary:
        required: false
        default: false
        type: boolean
      binary:
        required: false
        default: nerdctl
        type: string
      go-version:
        required: true
        type: string
      docker-version:
        required: true
        type: string
      containerd-version:
        required: true
        type: string
      containerd-sha:
        required: true
        type: string
      containerd-service-sha:
        required: true
        type: string
      windows-cni-version:
        required: true
        type: string
      linux-cni-version:
        required: true
        type: string
      linux-cni-sha:
        required: true
        type: string

env:
  GOTOOLCHAIN: local

jobs:
  test:
    name: |
      ${{ inputs.binary != 'nerdctl' && format('{0} < ', inputs.binary) || '' }}
      ${{ contains(inputs.runner, 'ubuntu') && ' linux' || ' windows' }}
      ${{ contains(inputs.runner, 'arm') && '(arm)' || '' }}
      ${{ contains(inputs.runner, '22.04') && '(old ubuntu)' || '' }}
      ${{ inputs.canary && ' (canary)' || '' }}
    timeout-minutes: ${{ inputs.timeout }}
    runs-on: "${{ inputs.runner }}"
    defaults:
      run:
        shell: bash

    env:
      SHOULD_RUN: "yes"
      GO_VERSION: ${{ inputs.go-version }}
      # Both Docker and nerdctl on linux need rootful right now
      WITH_SUDO: ${{ contains(inputs.runner, 'ubuntu') }}
      CONTAINERD_VERSION: ${{ inputs.containerd-version }}
      CONTAINERD_SHA: ${{ inputs.containerd-sha }}

    steps:
      - name: "Init: checkout"
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd  # v6.0.2
        with:
          fetch-depth: 1

      - if: ${{ inputs.canary }}
        name: "Init (canary): retrieve latest go and containerd"
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          latest_go="$(. ./hack/provisioning/version/fetch.sh; go::canary::for::go-setup)"
          latest_containerd="$(. ./hack/provisioning/version/fetch.sh; github::project::latest "containerd/containerd")"

          [ "$latest_go" == "" ] || \
            printf "GO_VERSION=%s\n" "$latest_go" >> "$GITHUB_ENV"
          [ "${latest_containerd:1}" == "$CONTAINERD_VERSION" ] || {
            printf "CONTAINERD_VERSION=%s\n" "${latest_containerd:1}" >> "$GITHUB_ENV"
            printf "CONTAINERD_SHA=canary is volatile and I accept the risk\n" >> "$GITHUB_ENV"
          }
          if [ "$latest_go" == "" ] && [ "${latest_containerd:1}" == "$CONTAINERD_VERSION" ]; then
            echo "::warning title=No canary::There is currently no canary versions to test. Steps will not run.";
            printf "SHOULD_RUN=no\n" >> "$GITHUB_ENV"
          fi

      - if: ${{ env.SHOULD_RUN == 'yes' }}
        name: "Init: install go"
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417  # v6.3.0
        with:
          go-version: ${{ env.GO_VERSION }}
          check-latest: true

      # XXX RUNNER_OS and generally env is too unreliable
      # - if: ${{ env.RUNNER_OS == 'Linux' }}
      - if: ${{ contains(inputs.runner, 'ubuntu') && env.SHOULD_RUN == 'yes' }}
        name: "Init (linux): prepare host"
        run: |
          if [ "${{ contains(inputs.binary, 'docker') }}" == true ]; then
            echo "::group:: configure cdi and experimental for docker"
            sudo mkdir -p /etc/docker
            sudo jq -n '.features.cdi = true | .experimental = true' | sudo tee /etc/docker/daemon.json
            echo "::endgroup::"
            echo "::group:: downgrade docker to the specific version we want to test (${{ inputs.docker-version }})"
            sudo apt-get update -qq
            sudo apt-get install -qq ca-certificates curl
            sudo install -m 0755 -d /etc/apt/keyrings
            sudo cp ./hack/provisioning/gpg/docker /etc/apt/keyrings/docker.asc
            sudo chmod a+r /etc/apt/keyrings/docker.asc
            echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
              $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" \
              | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
            sudo apt-get update -qq
            sudo apt-get install -qq --allow-downgrades docker-ce=${{ inputs.docker-version }} docker-ce-cli=${{ inputs.docker-version }}
            sudo systemctl restart docker
            echo "::endgroup::"
          else
            # FIXME: this is missing runc (see top level workflow note about the state of this)
            echo "::group:: install dependencies"
            sudo ./hack/provisioning/linux/containerd.sh uninstall
            ./hack/provisioning/linux/containerd.sh rootful "$CONTAINERD_VERSION" "amd64" "$CONTAINERD_SHA" "${{ inputs.containerd-service-sha }}"
            sudo ./hack/provisioning/linux/cni.sh uninstall
            ./hack/provisioning/linux/cni.sh install "${{ inputs.linux-cni-version }}" "amd64" "${{ inputs.linux-cni-sha }}"
            echo "::endgroup::"

            echo "::group:: build nerctl"
            go install ./cmd/nerdctl
            echo "$HOME/go/bin" >> "$GITHUB_PATH"
            # Since tests are going to run root, we need nerdctl to be in a PATH that will survive `sudo`
            sudo cp "$(which nerdctl)" /usr/local/bin
            echo "::endgroup::"
          fi

          # Register QEMU (tonistiigi/binfmt)
          # `--install all` will only install emulation for architectures that cannot be natively executed
          # Since some arm64 platforms do provide native fallback execution for 32 bits,
          # armv7 emulation may or may not be installed, causing variance in the result of `uname -m`.
          # To avoid that, we explicitly list the architectures we do want emulation for.
          echo "::group:: install binfmt"
          docker run --quiet --privileged --rm tonistiigi/binfmt --install linux/amd64
          docker run --quiet --privileged --rm tonistiigi/binfmt --install linux/arm64
          docker run --quiet --privileged --rm tonistiigi/binfmt --install linux/arm/v7
          echo "::endgroup::"

          # FIXME: remove expect when we are done removing unbuffer from tests
          echo "::group:: installing test dependencies"
          sudo add-apt-repository ppa:criu/ppa -y
          sudo apt-get install -qq expect criu
          echo "::endgroup::"

          # This ensures that bridged traffic goes through netfilter
          sudo modprobe br-netfilter

      - if: ${{ contains(inputs.runner, 'windows') && env.SHOULD_RUN == 'yes' }}
        name: "Init (windows): prepare host"
        env:
          ctrdVersion: ${{ env.CONTAINERD_VERSION }}
        run: |
          # Install WinCNI
          echo "::group:: install wincni"
          GOPATH=$(go env GOPATH) WINCNI_VERSION=${{ inputs.windows-cni-version }} ./hack/provisioning/windows/cni.sh
          echo "::endgroup::"

          # Install containerd
          echo "::group:: install containerd"
          powershell hack/provisioning/windows/containerd.ps1
          echo "::endgroup::"

          # Install nerdctl
          echo "::group:: build nerctl"
          go install ./cmd/nerdctl
          echo "::endgroup::"

          choco install jq

      - if: ${{ env.SHOULD_RUN == 'yes' }}
        name: "Init: install dev tools"
        run: |
          echo "::group:: make install-dev-tools"
          make install-dev-tools
          echo "::endgroup::"

      # ipv6 is tested only on linux
      - if: ${{ contains(inputs.runner, 'ubuntu') && env.SHOULD_RUN == 'yes' }}
        name: "Run (linux): integration tests (IPv6)"
        run: |
          . ./hack/github/action-helpers.sh
          github::md::h2 "ipv6" >> "$GITHUB_STEP_SUMMARY"

          ./hack/test-integration.sh -test.target=${{ inputs.binary }} -test.only-ipv6

      - if: ${{ env.SHOULD_RUN == 'yes' }}
        name: "Run: integration tests"
        run: |
          . ./hack/github/action-helpers.sh
          github::md::h2 "non-flaky" >> "$GITHUB_STEP_SUMMARY"

          ./hack/test-integration.sh -test.target=${{ inputs.binary }} -test.only-flaky=false

      # FIXME: this must go
      - if: ${{ env.SHOULD_RUN == 'yes' }}
        name: "Run: integration tests (flaky)"
        run: |
          . ./hack/github/action-helpers.sh
          github::md::h2 "flaky" >> "$GITHUB_STEP_SUMMARY"

          ./hack/test-integration.sh -test.target=${{ inputs.binary }} -test.only-flaky=true
