name: e2e

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

on:
  workflow_dispatch:
  push:
    branches:
      - 'master'
      - 'v[0-9]*'
  pull_request:
    branches:
      - 'master'
      - 'v[0-9]*'
    paths-ignore:
      - 'README.md'
      - 'docs/**'

env:
  DESTDIR: "./bin"
  K3S_VERSION: "v1.21.2-k3s1"

jobs:
  build:
    runs-on: ubuntu-22.04
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      -
        name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
        with:
          version: latest
      -
        name: Build
        uses: docker/bake-action@v2
        with:
          targets: binaries
          set: |
            *.cache-from=type=gha,scope=release
            *.cache-from=type=gha,scope=binaries
            *.cache-to=type=gha,scope=binaries
      -
        name: Rename binary
        run: |
          mv ${{ env.DESTDIR }}/build/buildx ${{ env.DESTDIR }}/build/docker-buildx
      -
        name: Upload artifacts
        uses: actions/upload-artifact@v3
        with:
          name: binary
          path: ${{ env.DESTDIR }}/build
          if-no-files-found: error
          retention-days: 7

  driver:
    runs-on: ubuntu-20.04
    needs:
      - build
    strategy:
      fail-fast: false
      matrix:
        driver:
          - docker
          - docker-container
          - kubernetes
          - remote
        buildkit:
          - moby/buildkit:buildx-stable-1
          - moby/buildkit:master
        buildkit-cfg:
          - bkcfg-false
          - bkcfg-true
        multi-node:
          - mnode-false
          - mnode-true
        platforms:
          - linux/amd64
          - linux/amd64,linux/arm64
        include:
          - driver: kubernetes
            driver-opt: qemu.install=true
          - driver: remote
            endpoint: tcp://localhost:1234
        exclude:
          - driver: docker
            multi-node: mnode-true
          - driver: docker
            buildkit-cfg: bkcfg-true
          - driver: docker-container
            multi-node: mnode-true
          - driver: remote
            multi-node: mnode-true
          - driver: remote
            buildkit-cfg: bkcfg-true
    steps:
      -
        name: Checkout
        uses: actions/checkout@v3
      -
        name: Set up QEMU
        uses: docker/setup-qemu-action@v2
        if: matrix.driver == 'docker' || matrix.driver == 'docker-container'
      -
        name: Install buildx
        uses: actions/download-artifact@v3
        with:
          name: binary
          path: /home/runner/.docker/cli-plugins
      -
        name: Fix perms and check
        run: |
          chmod +x /home/runner/.docker/cli-plugins/docker-buildx
          docker buildx version
      -
        name: Init env vars
        run: |
          # BuildKit cfg
          if [ "${{ matrix.buildkit-cfg }}" = "bkcfg-true" ]; then
            cat > "/tmp/buildkitd.toml" <<EOL
          [worker.oci]
            max-parallelism = 2
          EOL
            echo "BUILDKIT_CFG=/tmp/buildkitd.toml" >> $GITHUB_ENV
          fi
          # Multi node
          if [ "${{ matrix.multi-node }}" = "mnode-true" ]; then
            echo "MULTI_NODE=1" >> $GITHUB_ENV
          else
            echo "MULTI_NODE=0" >> $GITHUB_ENV
          fi
      -
        name: Install k3s
        if: matrix.driver == 'kubernetes'
        uses: actions/github-script@v6
        with:
          script: |
            const fs = require('fs');
            
            let wait = function(milliseconds) {
              return new Promise((resolve, reject) => {
                if (typeof(milliseconds) !== 'number') { 
                  throw new Error('milleseconds not a number'); 
                }
                setTimeout(() => resolve("done!"), milliseconds)
              });
            }
            
            try {
              const kubeconfig="/tmp/buildkit-k3s/kubeconfig.yaml";
              core.info(`storing kubeconfig in ${kubeconfig}`);
          
              await exec.exec('docker', ["run", "-d",
                "--privileged",
                "--name=buildkit-k3s",
                "-e", "K3S_KUBECONFIG_OUTPUT="+kubeconfig,
                "-e", "K3S_KUBECONFIG_MODE=666",
                "-v", "/tmp/buildkit-k3s:/tmp/buildkit-k3s",
                "-p", "6443:6443",
                "-p", "80:80",
                "-p", "443:443",
                "-p", "8080:8080",
                "rancher/k3s:${{ env.K3S_VERSION }}", "server"
              ]);
              await wait(10000);
              
              core.exportVariable('KUBECONFIG', kubeconfig);
              
              let nodeName;
              for (let count = 1; count <= 5; count++) {
                try {
                  const nodeNameOutput = await exec.getExecOutput("kubectl get nodes --no-headers -oname");
                  nodeName = nodeNameOutput.stdout
                } catch (error) {
                  core.info(`Unable to resolve node name (${error.message}). Attempt ${count} of 5.`)
                } finally {
                  if (nodeName) {
                    break;
                  }
                  await wait(5000);
                }
              }
              if (!nodeName) {
                throw new Error(`Unable to resolve node name after 5 attempts.`);
              }
              
              await exec.exec(`kubectl wait --for=condition=Ready ${nodeName}`);
            } catch (error) {
              core.setFailed(error.message);
            }
      -
        name: Print KUBECONFIG
        if: matrix.driver == 'kubernetes'
        run: |
          yq ${{ env.KUBECONFIG }}
      -
        name: Launch remote buildkitd
        if: matrix.driver == 'remote'
        run: |
          docker run -d \
            --privileged \
            --name=remote-buildkit \
            -p 1234:1234 \
            ${{ matrix.buildkit }} \
            --addr unix:///run/buildkit/buildkitd.sock \
            --addr tcp://0.0.0.0:1234
      -
        name: Test
        run: |
          make test-driver
        env:
          BUILDKIT_IMAGE: ${{ matrix.buildkit }}
          DRIVER: ${{ matrix.driver }}
          DRIVER_OPT: ${{ matrix.driver-opt }}
          ENDPOINT: ${{ matrix.endpoint }}
          PLATFORMS: ${{ matrix.platforms }}
