name: CI

on:
  pull_request:
    branches:
      - "*"
    paths-ignore:
      - 'docs/**'
      - 'deploy/**'
      - '**.md'

  push:
    branches:
      - main
    paths-ignore:
      - 'docs/**'
      - 'deploy/**'
      - '**.md'

  workflow_dispatch:
    inputs:
      run_e2e:
        description: 'Force e2e to run'
        required: false
        type: boolean


permissions:
  contents: read

jobs:

  changes:
    permissions:
      contents: read  # for dorny/paths-filter to fetch a list of changed files
      pull-requests: read  # for dorny/paths-filter to read pull requests
    runs-on: ubuntu-latest
    outputs:
      go: ${{ steps.filter.outputs.go }}
      charts: ${{ steps.filter.outputs.charts }}

    steps:

      - name: Checkout
        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3

      - uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2.11.1
        id: filter
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          filters: |
            go:
              - '**/*.go'
              - 'go.mod'
              - 'go.sum'
              - 'rootfs/**/*'
              - 'TAG'
              - 'test/e2e/**/*'
              - 'NGINX_BASE'
            charts:
              - 'charts/ingress-nginx/Chart.yaml'
              - 'charts/ingress-nginx/**/*'
              - 'NGINX_BASE'


  security:
    runs-on: ubuntu-latest
    steps:

      - name: Checkout
        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3

      - name: Run Gosec Security Scanner
        uses: securego/gosec@c5ea1b7bdd9efc3792e513258853552b0ae31e06 # v2.16.0
        with:
          # G601 for zz_generated.deepcopy.go
          # G306 TODO: Expect WriteFile permissions to be 0600 or less
          # G307 TODO: Deferring unsafe method "Close"
          args: -exclude=G109,G601,G104,G204,G304,G306,G307 -tests=false -exclude-dir=test -exclude-dir=images/  -exclude-dir=docs/ ./...

  lint:
    runs-on: ubuntu-latest
    needs: changes
    if: |
      (needs.changes.outputs.go == 'true')
    steps:
      - name: Checkout
        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
      
      - name: Set up Go
        id: go
        uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1
        with:
          go-version: '1.20'
          check-latest: true

      - name: Run Lint
        run: ./hack/verify-golint.sh

  gofmt:
    runs-on: ubuntu-latest
    needs: changes
    if: |
      (needs.changes.outputs.go == 'true')
    steps:
      - name: Checkout
        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
      
      - name: Set up Go
        id: go
        uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1
        with:
          go-version: '1.20'
          check-latest: true

      - name: Run go-fmt
        run: ./hack/verify-gofmt.sh

  test-go:
    runs-on: ubuntu-latest
    needs: changes
    if: |
      (needs.changes.outputs.go == 'true')
    steps:
      - name: Checkout
        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
      
      - name: Set up Go
        id: go
        uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1
        with:
          go-version: '1.20'
          check-latest: true

      - name: Run test
        run: make test

  build:
    name: Build
    runs-on: ubuntu-latest
    needs: changes
    if: |
      (needs.changes.outputs.go == 'true') || (needs.changes.outputs.charts == 'true') || ${{ inputs.run_e2e }}

    steps:
      - name: Checkout
        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3

      - name: Set up Go
        id: go
        uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1
        with:
          go-version: '1.20'
          check-latest: true

      - name: Set up QEMU
        uses: docker/setup-qemu-action@2b82ce82d56a2a04d2637cd93a637ae1b359c0a7 # v2.2.0

      - name: Set up Docker Buildx
        id: buildx
        uses: docker/setup-buildx-action@ecf95283f03858871ff00b787d79c419715afc34 # v2.7.0
        with:
          version: latest

      - name: Available platforms
        run: echo ${{ steps.buildx.outputs.platforms }}

      - name: Prepare Host
        run: |
          sudo apt-get -qq update || true
          sudo apt-get install -y pigz
          curl -LO https://dl.k8s.io/release/v1.25.5/bin/linux/amd64/kubectl
          chmod +x ./kubectl
          sudo mv ./kubectl /usr/local/bin/kubectl

      - name: Build images
        env:
          TAG: 1.0.0-dev
          ARCH: amd64
          REGISTRY: ingress-controller
        run: |
          echo "building images..."
          make clean-image build image image-chroot
          make -C test/e2e-image image

          echo "creating images cache..."
          docker save \
            nginx-ingress-controller:e2e \
            ingress-controller/controller:1.0.0-dev \
            ingress-controller/controller-chroot:1.0.0-dev \
            | pigz > docker.tar.gz

      - name: cache
        uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
        with:
          name: docker.tar.gz
          path: docker.tar.gz
          retention-days: 5
  helm:
    name: Helm chart
    runs-on: ubuntu-latest
    needs:
      - changes
      - build
    if: |
      (needs.changes.outputs.charts == 'true') || ${{ inputs.run_e2e }}

    strategy:
      matrix:
        k8s: [v1.24.12, v1.25.8, v1.26.3,v1.27.1]

    steps:
      - name: Checkout
        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3

      - name: Setup Go
        uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1
        with:
          go-version: '1.20'
          check-latest: true

      - name: cache
        uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
        with:
          name: docker.tar.gz

      - name: Lint
        run: |
          ./build/run-in-docker.sh ./hack/verify-chart-lint.sh

      - name: Run helm-docs
        run: |
          GOBIN=$PWD GO111MODULE=on go install github.com/norwoodj/helm-docs/cmd/helm-docs@v1.11.0
          ./helm-docs --chart-search-root=${GITHUB_WORKSPACE}/charts
          DIFF=$(git diff ${GITHUB_WORKSPACE}/charts/ingress-nginx/README.md)
          if [ ! -z "$DIFF" ]; then
            echo "Please use helm-docs in your clone, of your fork, of the project, and commit a updated README.md for the chart. https://github.com/kubernetes/ingress-nginx/blob/main/RELEASE.md#d-edit-the-valuesyaml-and-run-helm-docs"
          fi
          git diff --exit-code
          rm -f ./helm-docs

      - name: Run Artifact Hub lint
        run: |
          wget https://github.com/artifacthub/hub/releases/download/v1.5.0/ah_1.5.0_linux_amd64.tar.gz
          echo 'ad0e44c6ea058ab6b85dbf582e88bad9fdbc64ded0d1dd4edbac65133e5c87da *ah_1.5.0_linux_amd64.tar.gz' | shasum -c
          tar -xzvf ah_1.5.0_linux_amd64.tar.gz ah
          ./ah lint -p charts/ingress-nginx || exit 1
          rm -f ./ah ./ah_1.5.0_linux_amd64.tar.gz

      - name: fix permissions
        run: |
          sudo mkdir -p $HOME/.kube
          sudo chmod -R 777 $HOME/.kube

      - name: Create Kubernetes ${{ matrix.k8s }} cluster
        id: kind
        run: |
          kind create cluster --image=kindest/node:${{ matrix.k8s }}

      - name: Load images from cache
        run: |
          echo "loading docker images..."
          pigz -dc docker.tar.gz | docker load

      - name: Test
        env:
          KIND_CLUSTER_NAME: kind
          SKIP_CLUSTER_CREATION: true
          SKIP_IMAGE_CREATION: true
        run: |
          kind get kubeconfig > $HOME/.kube/kind-config-kind
          make kind-e2e-chart-tests

  kubernetes:
    name: Kubernetes
    runs-on: ubuntu-latest
    needs:
      - changes
      - build
    if: |
      (needs.changes.outputs.go == 'true') || ${{ inputs.run_e2e }}

    strategy:
      matrix:
        k8s: [v1.24.12, v1.25.8, v1.26.3,v1.27.1]

    steps:
      - name: Checkout
        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3

      - name: cache
        uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
        with:
          name: docker.tar.gz

      - name: Create Kubernetes ${{ matrix.k8s }} cluster
        id: kind
        run: |
          kind create cluster --image=kindest/node:${{ matrix.k8s }} --config test/e2e/kind.yaml

      - name: Load images from cache
        run: |
          echo "loading docker images..."
          pigz -dc docker.tar.gz | docker load

      - name: Run e2e tests
        env:
          KIND_CLUSTER_NAME: kind
          SKIP_CLUSTER_CREATION: true
          SKIP_IMAGE_CREATION: true
        run: |
          kind get kubeconfig > $HOME/.kube/kind-config-kind
          make kind-e2e-test

      - name: Upload e2e junit-reports
        uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
        if: success() || failure()
        with:
          name: e2e-test-reports-${{ matrix.k8s }}
          path: 'test/junitreports/report*.xml'


  kubernetes-chroot:
    name: Kubernetes chroot
    runs-on: ubuntu-latest
    needs:
      - changes
      - build
    if: |
      (needs.changes.outputs.go == 'true') || ${{ inputs.run_e2e }}

    strategy:
      matrix:
        k8s: [v1.24.12, v1.25.8, v1.26.3,v1.27.1]

    steps:

      - name: Checkout
        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3

      - name: cache
        uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
        with:
          name: docker.tar.gz

      - name: Create Kubernetes ${{ matrix.k8s }} cluster
        id: kind
        run: |
          kind create cluster --image=kindest/node:${{ matrix.k8s }} --config test/e2e/kind.yaml

      - name: Load images from cache
        run: |
          echo "loading docker images..."
          pigz -dc docker.tar.gz | docker load

      - name: Run e2e tests
        env:
          KIND_CLUSTER_NAME: kind
          SKIP_CLUSTER_CREATION: true
          SKIP_IMAGE_CREATION: true
          IS_CHROOT: true
        run: |
          kind get kubeconfig > $HOME/.kube/kind-config-kind
          make kind-e2e-test

      - name: Upload e2e junit-reports
        uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
        if: success() || failure()
        with:
          name: e2e-test-reports-chroot-${{ matrix.k8s }}
          path: 'test/junitreports/report*.xml'


  test-image-build:
    permissions:
      contents: read  # for dorny/paths-filter to fetch a list of changed files
      pull-requests: read  # for dorny/paths-filter to read pull requests
    runs-on: ubuntu-latest
    env:
      PLATFORMS: linux/amd64,linux/arm64
    steps:
      - name: Checkout
        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3

      - uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2.11.1
        id: filter-images
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          filters: |
            custom-error-pages:
              - 'images/custom-error-pages/**'
            cfssl:
              - 'images/cfssl/**'
            fastcgi-helloserver:
              - 'images/fastcgi-helloserver/**'
            echo:
              - 'images/echo/**'
            go-grpc-greeter-server:
              - 'images/go-grpc-greeter-server/**'
            httpbun:
              - 'images/httpbun/**'
            kube-webhook-certgen:
              - 'images/kube-webhook-certgen/**'
            ext-auth-example-authsvc:
              - 'images/ext-auth-example-authsvc/**'

      - name: custom-error-pages image build
        if: ${{ steps.filter-images.outputs.custom-error-pages == 'true' }}
        run: |
          cd images/custom-error-pages && make build
      - name: cfssl image build
        if: ${{ steps.filter-images.outputs.cfssl == 'true' }}
        run: |
          cd images/cfssl && make build
      - name: fastcgi-helloserver
        if: ${{ steps.filter-images.outputs.fastcgi-helloserver == 'true' }}
        run: |
          cd images/fastcgi-helloserver && make build
      - name: echo image build
        if: ${{ steps.filter-images.outputs.echo == 'true' }}
        run: |
          cd images/echo && make build
      - name: go-grpc-greeter-server image build
        if: ${{ steps.filter-images.outputs.go-grpc-greeter-server == 'true' }}
        run: |
          cd images/go-grpc-greeter-server && make build
      - name: httpbun image build
        if: ${{ steps.filter-images.outputs.httpbin == 'true' }}
        run: |
          cd images/httpbun && make build
      - name: kube-webhook-certgen image build
        if: ${{ steps.filter-images.outputs.kube-webhook-certgen == 'true' }}
        run: |
          cd images/kube-webhook-certgen && make build
      - name: ext-auth-example-authsvc
        if: ${{ steps.filter-images.outputs.ext-auth-example-authsvc == 'true' }}
        run: |
          cd images/ext-auth-example-authsvc && make build

  test-image:
    permissions:
      contents: read  # for dorny/paths-filter to fetch a list of changed files
      pull-requests: read  # for dorny/paths-filter to read pull requests

    runs-on: ubuntu-latest

    env:
      PLATFORMS: linux/amd64

    strategy:
      matrix:
        k8s: [v1.24.12, v1.25.8, v1.26.3,v1.27.1]

    steps:
      - name: Checkout
        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3

      - uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2.11.1
        id: filter-images
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          filters: |
            kube-webhook-certgen:
              - 'images/kube-webhook-certgen/**'

      - name: Create Kubernetes cluster
        id: kind
        if: ${{ steps.filter-images.outputs.kube-webhook-certgen == 'true' }}
        run: |
          kind create cluster --image=kindest/node:${{ matrix.k8s }}

      - name: Set up Go
        id: go
        if: ${{ steps.filter-images.outputs.kube-webhook-certgen == 'true' }}
        uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1
        with:
          go-version: '1.20'
          check-latest: true

      - name: kube-webhook-certgen image build
        if: ${{ steps.filter-images.outputs.kube-webhook-certgen == 'true' }}
        run: |
          cd images/kube-webhook-certgen && make test test-e2e
