name: ovn-ci

on:
  push:
  pull_request:
    branches: [ master ]
  schedule:
    - cron: '0 */12 * * *'

env:
  GO_VERSION: 1.15.5
  K8S_VERSION: v1.19.4
  KIND_CLUSTER_NAME: ovn
  KIND_INSTALL_INGRESS: true
  KIND_ALLOW_SYSTEM_WRITES: true
  # This skips tests tagged as Serial
  # Current Serial tests are not relevant for OVN
  PARALLEL: true

jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
    - name: Set up Go
      uses: actions/setup-go@v1
      with:
        go-version: ${{ env.GO_VERSION }}
      id: go

    - name: Check out code into the Go module directory
      uses: actions/checkout@v2

    - name: Set up environment
      run: |
        export GOPATH=$(go env GOPATH)
        echo "GOPATH=$GOPATH" >> $GITHUB_ENV
        echo "$GOPATH/bin" >> $GITHUB_PATH

    - name: Verify
      run: |
        pushd go-controller
          make gofmt
          make install.tools
          make lint
        popd

    - name: Test
      run: |
        set -x
        pushd go-controller
        make check
        popd

    - name: Build
      run: |
        set -x
        go get golang.org/x/tools/cmd/cover
        pushd go-controller
           make
           make windows
           COVERALLS=1 make check
        popd

        # Combine separate code coverage profiles into one
        go get github.com/modocache/gover
        gover go-controller/ gover.coverprofile

        # Convert coverage profile to LCOV format for coveralls github action
        go get github.com/jandelgado/gcov2lcov
        mkdir -p src/github.com/ovn-org
        ln -sf $(pwd) src/github.com/ovn-org/ovn-kubernetes
        GOPATH=$(pwd) gcov2lcov -infile gover.coverprofile -outfile coverage.lcov

    - name: Build docker image
      run: |
        pushd dist/images
          sudo cp -f ../../go-controller/_output/go/bin/ovn* .
          echo "ref: $(git rev-parse  --symbolic-full-name HEAD)  commit: $(git rev-parse  HEAD)" > git_info
          docker build -t ovn-daemonset-f:dev -f Dockerfile.fedora .
          mkdir _output
          docker save ovn-daemonset-f:dev > _output/image.tar
        popd

    - uses: actions/upload-artifact@v2
      with:
        name: test-image
        path: dist/images/_output/image.tar

    - name: Upload Junit Reports
      if: always()
      uses: actions/upload-artifact@v2
      with:
        name: junit-${{ github.run_id }}
        path: '**/_artifacts/**.xml'

    - name: Submit code coverage to Coveralls
      uses: coverallsapp/github-action@master
      with:
        github-token: ${{ secrets.GITHUB_TOKEN }}
        path-to-lcov: coverage.lcov

  k8s:
    name: Build k8s
    runs-on: ubuntu-latest
    steps:

    - name: Set up environment
      run: |
        export GOPATH=$(go env GOPATH)
        echo "GOPATH=$GOPATH" >> $GITHUB_ENV
        echo "$GOPATH/bin" >> $GITHUB_PATH

    - name: Cache Kubernetes
      id: cache-k8s
      if: github.event_name == 'push' || github.event_name == 'pull_request'
      uses: actions/cache@v2
      with:
        path: ${{ env.GOPATH }}/src/k8s.io/kubernetes/
        key: k8s-go-2-${{ env.K8S_VERSION }}

    - name: Set up Go
      uses: actions/setup-go@v1
      with:
        go-version: ${{ env.GO_VERSION }}
      id: go
      if: steps.cache-k8s.outputs.cache-hit != 'true'

    - name: Build and install Kubernetes
      if: steps.cache-k8s.outputs.cache-hit != 'true'
      run: |
        set -x
        rm -rf $GOPATH/src/k8s.io/kubernetes
        git clone --single-branch --branch $K8S_VERSION https://github.com/kubernetes/kubernetes.git $GOPATH/src/k8s.io/kubernetes/
        pushd $GOPATH/src/k8s.io/kubernetes/
        make WHAT="test/e2e/e2e.test vendor/github.com/onsi/ginkgo/ginkgo cmd/kubectl"
        rm -rf .git

  e2e:
    name: e2e
    if: github.event_name != 'schedule'
    runs-on: ubuntu-latest
    timeout-minutes: 60
    strategy:
      fail-fast: false
      matrix:
        target:
          - shard: shard-conformance
            hybrid-overlay: false
            multicast-enable: false
          - shard: control-plane
            hybrid-overlay: true
            multicast-enable: true
        ha:
         - enabled: "true"
           name: "HA"
         - enabled: "false"
           name: "noHA"
        gateway-mode: [local, shared]
        ipfamily:
         - ip: ipv4
           name: "IPv4"
           ipv4: true
           ipv6: false
         - ip: ipv6
           name: "IPv6"
           ipv4: false
           ipv6: true
         - ip: dualstack
           name: "Dualstack"
           ipv4: true
           ipv6: true
        # Example of how to exclude a fully qualified test:
        # - {"ipfamily": {"ip": ipv4}, "ha": {"enabled": "false"}, "gateway-mode": shared, "target": {"shard": shard-n-other}}
        exclude:
         # Not currently supported but needs to be.
         - {"ipfamily": {"ip": dualstack}, "target": {"shard": control-plane}}
         - {"ipfamily": {"ip": ipv6}, "target": {"shard": control-plane}}
         # Limit matrix combinations for CI. DISABLED items added to exclude list:
         #   DISABLED  v4  ha     local
         #   ENABLED   v4  ha     shared
         #   ENABLED   v4  noha   local
         #   DISABLED  v4  noha   shared
         #   ENABLED   v6  ha     local
         #   ENABLED   v6  ha     shared
         #   DISABLED  v6  noha   local
         #   DISABLED  v6  noha   shared
         #   ENABLED   ds  ha     local
         #   DISABLED  ds  ha     shared
         #   DISABLED  ds  noha   local
         #   DISABLED  ds  noha   shared
         - {"ipfamily": {"ip": ipv4}, "ha": {"enabled": "true"}, "gateway-mode": local}
         - {"ipfamily": {"ip": ipv4}, "ha": {"enabled": "false"}, "gateway-mode": shared}
         - {"ipfamily": {"ip": ipv6}, "ha": {"enabled": "false"}, "gateway-mode": local}
         - {"ipfamily": {"ip": ipv6}, "ha": {"enabled": "false"}, "gateway-mode": shared}
         - {"ipfamily": {"ip": dualstack}, "ha": {"enabled": "true"}, "gateway-mode": shared}
         - {"ipfamily": {"ip": dualstack}, "ha": {"enabled": "false"}}
         # IPv6 multicast is supported but tests fail due to old iperf version
         # in agnhost images. Disable them for now.
         - {"ipfamily": {"ip": dualstack}, "target": {"shard": {"multicast-enable": "true"}}}
         - {"ipfamily": {"ip": ipv6}, "target": {"shard": {"multicast-enable": "true"}}}
    needs: [build]
    env:
      JOB_NAME: "${{ matrix.target.shard }}-${{ matrix.ha.name }}-${{ matrix.gateway-mode }}-${{ matrix.ipfamily.name }}"
      OVN_HA: "${{ matrix.ha.enabled }}"
      KIND_IPV4_SUPPORT: "${{ matrix.ipfamily.ipv4 }}"
      KIND_IPV6_SUPPORT: "${{ matrix.ipfamily.ipv6 }}"
      OVN_HYBRID_OVERLAY_ENABLE: "${{ matrix.target.hybrid-overlay }}"
      OVN_GATEWAY_MODE: "${{ matrix.gateway-mode }}"
      OVN_MULTICAST_ENABLE:  "${{ matrix.target.multicast-enable }}"
    steps:

    - name: Free up disk space
      run: sudo eatmydata apt-get remove --auto-remove -y aspnetcore-* dotnet-* libmono-* mono-* msbuild php-* php7* ghc-* zulu-*

    - name: Set up Go
      uses: actions/setup-go@v1
      with:
        go-version: ${{ env.GO_VERSION }}
      id: go

    - name: Check out code into the Go module directory
      uses: actions/checkout@v2

    - name: Set up environment
      run: |
        export GOPATH=$(go env GOPATH)
        echo "GOPATH=$GOPATH" >> $GITHUB_ENV
        echo "$GOPATH/bin" >> $GITHUB_PATH

    - name: Disable ufw
      # For IPv6 and Dualstack, ufw (Uncomplicated Firewall) should be disabled.
      # Not needed for KIND deployments, so just disable all the time.
      run: |
        sudo ufw disable

    - name: Restore Kubernetes from cache
      id: cache-k8s
      uses: actions/cache@v2
      with:
        path: "${{ env.GOPATH }}/src/k8s.io/kubernetes/"
        key: k8s-go-2-${{ env.K8S_VERSION }}

    # Re-build if kube wasn't in the cache due to
    # https://github.com/actions/cache/issues/107#issuecomment-598188250
    # https://github.com/actions/cache/issues/208#issuecomment-596664572
    - name: Build and install Kubernetes
      if: steps.cache-k8s.outputs.cache-hit != 'true'
      run: |
        set -x
        rm -rf $GOPATH/src/k8s.io/kubernetes/
        git clone --single-branch --branch $K8S_VERSION https://github.com/kubernetes/kubernetes.git $GOPATH/src/k8s.io/kubernetes/
        pushd $GOPATH/src/k8s.io/kubernetes/
        make WHAT="test/e2e/e2e.test vendor/github.com/onsi/ginkgo/ginkgo cmd/kubectl"
        rm -rf .git

    - uses: actions/download-artifact@v2
      with:
        name: test-image
    - name: Load docker image
      run: |
        docker load --input image.tar

    - name: kind setup
      run: |
        export OVN_IMAGE="ovn-daemonset-f:dev"
        make -C test install-kind

    - name: Run Tests
      run: |
        make -C test ${{ matrix.target.shard }}

    - name: Upload Junit Reports
      if: always()
      uses: actions/upload-artifact@v2
      with:
        name: kind-junit-${{ env.JOB_NAME }}-${{ github.run_id }}
        path: './test/_artifacts/*.xml'

    - name: Generate Test Report
      id: xunit-viewer
      if: always()
      uses: AutoModality/action-xunit-viewer@v1
      with:
        results: ./test/_artifacts/

    - name: Upload Test Report
      if: always()
      uses: actions/upload-artifact@v2
      with:
        name: test-report-${{ env.JOB_NAME }}-${{ github.run_id }}
        path: './test/_artifacts/index.html'

    - name: Export logs
      if: always()
      run: |
        mkdir -p /tmp/kind/logs
        kind export logs --name ${KIND_CLUSTER_NAME} --loglevel=debug /tmp/kind/logs

    - name: Upload logs
      if: always()
      uses: actions/upload-artifact@v2
      with:
        name: kind-logs-${{ env.JOB_NAME }}-${{ github.run_id }}
        path: /tmp/kind/logs

  e2e-periodic:
    name: e2e-periodic
    if: github.event_name == 'schedule'
    runs-on: ubuntu-latest
    timeout-minutes: 60
    strategy:
      fail-fast: false
      matrix:
        target:
          - shard: shard-conformance
            hybrid-overlay: false
        ha:
          - enabled: "true"
            name: "HA"
        gateway-mode: [local]
        ipfamily:
          - ip: ipv4
            name: "IPv4"
            ipv4: true
            ipv6: false
          - ip: ipv6
            name: "IPv6"
            ipv4: false
            ipv6: true
          - ip: dualstack
            name: "Dualstack"
            ipv4: true
            ipv6: true
    needs: [ build, k8s ]
    env:
      JOB_NAME: "${{ matrix.target.shard }}-${{ matrix.ha.name }}-${{ matrix.gateway-mode }}-${{ matrix.ipfamily.name }}"
      OVN_HA: "${{ matrix.ha.enabled }}"
      KIND_IPV4_SUPPORT: "${{ matrix.ipfamily.ipv4 }}"
      KIND_IPV6_SUPPORT: "${{ matrix.ipfamily.ipv6 }}"
      OVN_HYBRID_OVERLAY_ENABLE: "${{ matrix.target.hybrid-overlay }}"
      OVN_GATEWAY_MODE: "${{ matrix.gateway-mode }}"
    steps:

      - name: Free up disk space
        run: sudo eatmydata apt-get remove --auto-remove -y aspnetcore-* dotnet-* libmono-* mono-* msbuild php-* php7* ghc-* zulu-*

      - name: Set up Go
        uses: actions/setup-go@v1
        with:
          go-version: ${{ env.GO_VERSION }}
        id: go

      - name: Check out code into the Go module directory
        uses: actions/checkout@v2

      - name: Set up environment
        run: |
          export GOPATH=$(go env GOPATH)
          echo "GOPATH=$GOPATH" >> $GITHUB_ENV
          echo "$GOPATH/bin" >> $GITHUB_PATH

      - name: Disable ufw
        # For IPv6 and Dualstack, ufw (Uncomplicated Firewall) should be disabled.
        # Not needed for KIND deployments, so just disable all the time.
        run: |
          sudo ufw disable

      - name: Restore Kubernetes from cache
        id: cache-k8s
        uses: actions/cache@v2
        with:
          path: "${{ env.GOPATH }}/src/k8s.io/kubernetes/"
          key: k8s-go-2-${{ env.K8S_VERSION }}

      # Re-build if kube wasn't in the cache due to
      # https://github.com/actions/cache/issues/107#issuecomment-598188250
      # https://github.com/actions/cache/issues/208#issuecomment-596664572
      - name: Build and install Kubernetes
        if: steps.cache-k8s.outputs.cache-hit != 'true'
        run: |
          set -x
          rm -rf $GOPATH/src/k8s.io/kubernetes/
          git clone --single-branch --branch $K8S_VERSION https://github.com/kubernetes/kubernetes.git $GOPATH/src/k8s.io/kubernetes/
          pushd $GOPATH/src/k8s.io/kubernetes/
          make WHAT="test/e2e/e2e.test vendor/github.com/onsi/ginkgo/ginkgo cmd/kubectl"
          rm -rf .git

      - uses: actions/download-artifact@v2
        with:
          name: test-image
      - name: Load docker image
        run: |
          docker load --input image.tar

      - name: kind setup
        run: |
          export OVN_IMAGE="ovn-daemonset-f:dev"
          make -C test install-kind

      - name: Run Tests
        run: |
          make -C test ${{ matrix.target.shard }}

      - name: Upload Junit Reports
        if: always()
        uses: actions/upload-artifact@v2
        with:
          name: kind-junit-${{ env.JOB_NAME }}-${{ github.run_id }}
          path: './test/_artifacts/*.xml'

      - name: Generate Test Report
        id: xunit-viewer
        if: always()
        uses: AutoModality/action-xunit-viewer@v1
        with:
          results: ./test/_artifacts/

      - name: Upload Test Report
        if: always()
        uses: actions/upload-artifact@v2
        with:
          name: test-report-${{ env.JOB_NAME }}-${{ github.run_id }}
          path: './test/_artifacts/index.html'

      - name: Export logs
        if: always()
        run: |
          mkdir -p /tmp/kind/logs
          kind export logs --name ${KIND_CLUSTER_NAME} --loglevel=debug /tmp/kind/logs

      - name: Upload logs
        if: always()
        uses: actions/upload-artifact@v2
        with:
          name: kind-logs-${{ env.JOB_NAME }}-${{ github.run_id }}
          path: /tmp/kind/logs