Integration Tests #126
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Integration Tests | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| protocol: | |
| description: 'Protocol to test' | |
| required: false | |
| default: 'all' | |
| type: choice | |
| options: | |
| - all | |
| - nfs | |
| - nvmeof | |
| - iscsi | |
| - smb | |
| test: | |
| description: 'Specific test to run (leave empty for all tests of selected protocol)' | |
| required: false | |
| default: '' | |
| type: choice | |
| options: | |
| - '' | |
| - basic | |
| - snapshot | |
| - detached-snapshot | |
| - concurrent | |
| - persistence | |
| - statefulset | |
| - volume-expansion | |
| - zfsprops | |
| - name-templating | |
| - clone-volume | |
| - delete-strategy-retain | |
| - block-mode | |
| - snapshot-stress | |
| - dual-mount | |
| - connection-resilience | |
| - pod-restart | |
| - access-modes | |
| - snapshot-restore | |
| - volume-stress | |
| - error-handling | |
| - encryption | |
| pr: | |
| description: 'PR number to test (builds image from PR code, leave empty for normal run)' | |
| required: false | |
| default: '' | |
| type: string | |
| # Restrict permissions to minimum required | |
| permissions: | |
| contents: read | |
| # Ensure only one integration test runs at a time (shared NASty server) | |
| concurrency: | |
| group: integration-tests | |
| cancel-in-progress: false | |
| env: | |
| SSH_OPTS: "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=10 -i /tmp/vm-key -p 2222" | |
| SCP_OPTS: "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=10 -i /tmp/vm-key -P 2222" | |
| jobs: | |
| # ========================================== | |
| # Setup Phase | |
| # ========================================== | |
| # Check if driver files changed (skip integration tests for plugin-only changes) | |
| check-driver-changes: | |
| name: Check Driver Changes | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'workflow_run' | |
| outputs: | |
| should_run: ${{ steps.check.outputs.should_run }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ github.event.workflow_run.head_sha }} | |
| fetch-depth: 2 | |
| - name: Check for driver changes | |
| id: check | |
| run: | | |
| CHANGED=$(git diff --name-only HEAD^..HEAD 2>/dev/null || echo "") | |
| echo "Changed files: $CHANGED" | |
| if echo "$CHANGED" | grep -qE '^(cmd/nasty-csi-driver/|pkg/|Dockerfile|go\.(mod|sum))'; then | |
| echo "Driver files changed - will run integration tests" | |
| echo "should_run=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "No driver files changed - skipping integration tests" | |
| echo "should_run=false" >> $GITHUB_OUTPUT | |
| fi | |
| compute-tag: | |
| name: Compute Image Tag | |
| runs-on: ubuntu-latest | |
| needs: check-driver-changes | |
| if: | | |
| always() && | |
| (github.event_name != 'workflow_run' || | |
| (github.event.workflow_run.conclusion == 'success' && | |
| (github.event.workflow_run.name == 'Release' || github.event.workflow_run.head_branch == 'main'))) && | |
| (needs.check-driver-changes.outputs.should_run != 'false') | |
| outputs: | |
| image_tag: ${{ steps.tag.outputs.tag }} | |
| steps: | |
| - name: Checkout PR code | |
| if: inputs.pr != '' | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: refs/pull/${{ inputs.pr }}/merge | |
| - name: Set up Docker Buildx | |
| if: inputs.pr != '' | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Build PR image | |
| if: inputs.pr != '' | |
| run: | | |
| PR_TAG="pr-${{ inputs.pr }}" | |
| IMAGE="ghcr.io/nasty-project/nasty-csi:${PR_TAG}" | |
| echo "=== Building image from PR #${{ inputs.pr }} ===" | |
| docker buildx build \ | |
| --load \ | |
| --build-arg VERSION="0.0.0-${PR_TAG}" \ | |
| --build-arg GIT_COMMIT="$(git rev-parse --short HEAD)" \ | |
| --build-arg BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ | |
| -t "${IMAGE}" . | |
| docker save "${IMAGE}" -o /tmp/nasty-csi-pr-image.tar | |
| echo "Image saved to /tmp/nasty-csi-pr-image.tar" | |
| - name: Upload PR image artifact | |
| if: inputs.pr != '' | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: pr-image | |
| path: /tmp/nasty-csi-pr-image.tar | |
| retention-days: 1 | |
| - name: Compute image tag from ref | |
| id: tag | |
| env: | |
| WORKFLOW_HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }} | |
| WORKFLOW_EVENT: ${{ github.event.workflow_run.event }} | |
| WORKFLOW_NAME: ${{ github.event.workflow_run.name }} | |
| run: | | |
| if [[ -n "${{ inputs.pr }}" ]]; then | |
| TAG="pr-${{ inputs.pr }}" | |
| echo "Using PR image tag: $TAG" | |
| echo "tag=$TAG" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| if [ "${{ github.event_name }}" = "workflow_run" ]; then | |
| REF_NAME="$WORKFLOW_HEAD_BRANCH" | |
| if [ "$WORKFLOW_EVENT" = "push" ] && [[ "$WORKFLOW_HEAD_BRANCH" == v* ]]; then | |
| REF_TYPE="tag" | |
| else | |
| REF_TYPE="branch" | |
| fi | |
| echo "Triggered by workflow: $WORKFLOW_NAME" | |
| else | |
| REF_NAME="${{ github.ref_name }}" | |
| REF_TYPE="${{ github.ref_type }}" | |
| fi | |
| echo "REF_NAME: $REF_NAME, REF_TYPE: $REF_TYPE" | |
| if [ "$REF_TYPE" = "tag" ]; then | |
| TAG="${REF_NAME#v}" | |
| elif [ "$REF_NAME" = "main" ]; then | |
| TAG="latest" | |
| else | |
| TAG=$(echo "$REF_NAME" | sed 's/\//-/g') | |
| fi | |
| echo "Using image tag: $TAG" | |
| echo "tag=$TAG" >> $GITHUB_OUTPUT | |
| # ========================================== | |
| # E2E Tests (QEMU VM on GitHub-hosted runners) | |
| # ========================================== | |
| ginkgo-e2e-nfs: | |
| name: "E2E: NFS" | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 120 | |
| needs: compute-tag | |
| if: | | |
| !cancelled() && needs.compute-tag.result == 'success' && | |
| (github.event.inputs.protocol == 'all' || github.event.inputs.protocol == 'nfs' || github.event.inputs.protocol == '') | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ inputs.pr != '' && format('refs/pull/{0}/merge', inputs.pr) || '' }} | |
| - uses: actions/checkout@v6 | |
| with: | |
| repository: nasty-project/nasty-go | |
| token: ${{ secrets.NASTY_PAT }} | |
| path: nasty-go | |
| - uses: actions/checkout@v6 | |
| with: | |
| repository: nasty-project/nasty-chart | |
| token: ${{ secrets.NASTY_PAT }} | |
| path: nasty-chart | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: go.mod | |
| - name: Boot QEMU VM with K3S | |
| uses: ./.github/actions/qemu-vm | |
| with: | |
| nasty_host: ${{ secrets.NASTY_HOST_TAILSCALE || secrets.NASTY_HOST }} | |
| tailscale_authkey: ${{ secrets.TAILSCALE_AUTHKEY }} | |
| - name: Build and import CSI image | |
| if: inputs.pr == '' | |
| run: | | |
| docker build -t ghcr.io/nasty-project/nasty-csi:e2e-test . | |
| docker save ghcr.io/nasty-project/nasty-csi:e2e-test -o /tmp/csi-image.tar | |
| scp $SCP_OPTS /tmp/csi-image.tar ci@localhost:/tmp/csi-image.tar | |
| ssh $SSH_OPTS ci@localhost "sudo k3s ctr images import /tmp/csi-image.tar" | |
| - name: Download and import PR image | |
| if: inputs.pr != '' | |
| uses: actions/download-artifact@v7 | |
| with: | |
| name: pr-image | |
| path: /tmp | |
| - name: Import PR image into VM | |
| if: inputs.pr != '' | |
| run: | | |
| scp $SCP_OPTS /tmp/nasty-csi-pr-image.tar ci@localhost:/tmp/csi-image.tar | |
| ssh $SSH_OPTS ci@localhost "sudo k3s ctr images import /tmp/csi-image.tar" | |
| - name: Install Ginkgo | |
| run: go install github.com/onsi/ginkgo/v2/ginkgo@latest | |
| - name: Run NFS E2E Tests | |
| env: | |
| NASTY_HOST: ${{ secrets.NASTY_HOST_TAILSCALE || secrets.NASTY_HOST }} | |
| NASTY_API_KEY: ${{ secrets.NASTY_API_KEY }} | |
| NASTY_FILESYSTEM: ${{ secrets.NASTY_FILESYSTEM }} | |
| CSI_IMAGE_TAG: ${{ inputs.pr != '' && needs.compute-tag.outputs.image_tag || 'e2e-test' }} | |
| CSI_IMAGE_PULL_POLICY: Never | |
| KUBECONFIG: /tmp/kubeconfig | |
| CSI_CHART_PATH: ${{ github.workspace }}/nasty-chart | |
| run: | | |
| FOCUS="" | |
| if [ -n "${{ inputs.test }}" ]; then | |
| FOCUS="--focus=${{ inputs.test }}" | |
| fi | |
| ginkgo -v --timeout=100m --flake-attempts=2 $FOCUS ./tests/e2e/nfs/... | |
| - name: Collect diagnostic logs | |
| if: always() | |
| env: | |
| KUBECONFIG: /tmp/kubeconfig | |
| run: | | |
| mkdir -p /tmp/test-logs | |
| kubectl logs -n kube-system -l app.kubernetes.io/component=controller -c nasty-csi-plugin --tail=-1 > /tmp/test-logs/controller.log 2>&1 || true | |
| kubectl logs -n kube-system -l app.kubernetes.io/component=node -c nasty-csi-plugin --tail=-1 > /tmp/test-logs/node.log 2>&1 || true | |
| kubectl get events --all-namespaces --sort-by='.lastTimestamp' > /tmp/test-logs/events.log 2>&1 || true | |
| kubectl get pvc --all-namespaces -o wide > /tmp/test-logs/pvcs.log 2>&1 || true | |
| kubectl get pv -o wide > /tmp/test-logs/pvs.log 2>&1 || true | |
| kubectl get volumesnapshots --all-namespaces -o wide > /tmp/test-logs/snapshots.log 2>&1 || true | |
| kubectl get volumesnapshotcontents -o wide > /tmp/test-logs/snapshot-contents.log 2>&1 || true | |
| ssh $SSH_OPTS ci@localhost "sudo dmesg | tail -100" > /tmp/test-logs/vm-dmesg.log 2>&1 || true | |
| - name: Upload diagnostic logs | |
| if: always() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: e2e-nfs-diagnostics | |
| path: /tmp/test-logs/ | |
| retention-days: 7 | |
| - name: Cleanup VM | |
| if: always() | |
| run: | | |
| if [ -f /tmp/vm.pid ]; then | |
| kill "$(cat /tmp/vm.pid)" 2>/dev/null || true | |
| fi | |
| ginkgo-e2e-nvmeof: | |
| name: "E2E: NVMe-oF" | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 120 | |
| needs: compute-tag | |
| if: | | |
| !cancelled() && needs.compute-tag.result == 'success' && | |
| (github.event.inputs.protocol == 'all' || github.event.inputs.protocol == 'nvmeof' || github.event.inputs.protocol == '') | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ inputs.pr != '' && format('refs/pull/{0}/merge', inputs.pr) || '' }} | |
| - uses: actions/checkout@v6 | |
| with: | |
| repository: nasty-project/nasty-go | |
| token: ${{ secrets.NASTY_PAT }} | |
| path: nasty-go | |
| - uses: actions/checkout@v6 | |
| with: | |
| repository: nasty-project/nasty-chart | |
| token: ${{ secrets.NASTY_PAT }} | |
| path: nasty-chart | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: go.mod | |
| - name: Boot QEMU VM with K3S | |
| uses: ./.github/actions/qemu-vm | |
| with: | |
| nasty_host: ${{ secrets.NASTY_HOST_TAILSCALE || secrets.NASTY_HOST }} | |
| tailscale_authkey: ${{ secrets.TAILSCALE_AUTHKEY }} | |
| - name: Build and import CSI image | |
| if: inputs.pr == '' | |
| run: | | |
| docker build -t ghcr.io/nasty-project/nasty-csi:e2e-test . | |
| docker save ghcr.io/nasty-project/nasty-csi:e2e-test -o /tmp/csi-image.tar | |
| scp $SCP_OPTS /tmp/csi-image.tar ci@localhost:/tmp/csi-image.tar | |
| ssh $SSH_OPTS ci@localhost "sudo k3s ctr images import /tmp/csi-image.tar" | |
| - name: Download and import PR image | |
| if: inputs.pr != '' | |
| uses: actions/download-artifact@v7 | |
| with: | |
| name: pr-image | |
| path: /tmp | |
| - name: Import PR image into VM | |
| if: inputs.pr != '' | |
| run: | | |
| scp $SCP_OPTS /tmp/nasty-csi-pr-image.tar ci@localhost:/tmp/csi-image.tar | |
| ssh $SSH_OPTS ci@localhost "sudo k3s ctr images import /tmp/csi-image.tar" | |
| - name: Install Ginkgo | |
| run: go install github.com/onsi/ginkgo/v2/ginkgo@latest | |
| - name: Run NVMe-oF E2E Tests | |
| env: | |
| NASTY_HOST: ${{ secrets.NASTY_HOST_TAILSCALE || secrets.NASTY_HOST }} | |
| NASTY_API_KEY: ${{ secrets.NASTY_API_KEY }} | |
| NASTY_FILESYSTEM: ${{ secrets.NASTY_FILESYSTEM }} | |
| CSI_IMAGE_TAG: ${{ inputs.pr != '' && needs.compute-tag.outputs.image_tag || 'e2e-test' }} | |
| CSI_IMAGE_PULL_POLICY: Never | |
| KUBECONFIG: /tmp/kubeconfig | |
| CSI_CHART_PATH: ${{ github.workspace }}/nasty-chart | |
| run: | | |
| FOCUS="" | |
| if [ -n "${{ inputs.test }}" ]; then | |
| FOCUS="--focus=${{ inputs.test }}" | |
| fi | |
| ginkgo -v --timeout=100m --flake-attempts=2 $FOCUS ./tests/e2e/nvmeof/... | |
| - name: Collect diagnostic logs | |
| if: always() | |
| env: | |
| KUBECONFIG: /tmp/kubeconfig | |
| run: | | |
| mkdir -p /tmp/test-logs | |
| kubectl logs -n kube-system -l app.kubernetes.io/component=controller -c nasty-csi-plugin --tail=-1 > /tmp/test-logs/controller.log 2>&1 || true | |
| kubectl logs -n kube-system -l app.kubernetes.io/component=node -c nasty-csi-plugin --tail=-1 > /tmp/test-logs/node.log 2>&1 || true | |
| kubectl get events --all-namespaces --sort-by='.lastTimestamp' > /tmp/test-logs/events.log 2>&1 || true | |
| kubectl get pvc --all-namespaces -o wide > /tmp/test-logs/pvcs.log 2>&1 || true | |
| ssh $SSH_OPTS ci@localhost "sudo dmesg | tail -100" > /tmp/test-logs/vm-dmesg.log 2>&1 || true | |
| - name: Upload diagnostic logs | |
| if: always() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: e2e-nvmeof-diagnostics | |
| path: /tmp/test-logs/ | |
| retention-days: 7 | |
| - name: Cleanup VM | |
| if: always() | |
| run: | | |
| if [ -f /tmp/vm.pid ]; then | |
| kill "$(cat /tmp/vm.pid)" 2>/dev/null || true | |
| fi | |
| ginkgo-e2e-iscsi: | |
| name: "E2E: iSCSI" | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 120 | |
| needs: compute-tag | |
| if: | | |
| !cancelled() && needs.compute-tag.result == 'success' && | |
| (github.event.inputs.protocol == 'all' || github.event.inputs.protocol == 'iscsi' || github.event.inputs.protocol == '') | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ inputs.pr != '' && format('refs/pull/{0}/merge', inputs.pr) || '' }} | |
| - uses: actions/checkout@v6 | |
| with: | |
| repository: nasty-project/nasty-go | |
| token: ${{ secrets.NASTY_PAT }} | |
| path: nasty-go | |
| - uses: actions/checkout@v6 | |
| with: | |
| repository: nasty-project/nasty-chart | |
| token: ${{ secrets.NASTY_PAT }} | |
| path: nasty-chart | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: go.mod | |
| - name: Boot QEMU VM with K3S | |
| uses: ./.github/actions/qemu-vm | |
| with: | |
| nasty_host: ${{ secrets.NASTY_HOST_TAILSCALE || secrets.NASTY_HOST }} | |
| tailscale_authkey: ${{ secrets.TAILSCALE_AUTHKEY }} | |
| - name: Build and import CSI image | |
| if: inputs.pr == '' | |
| run: | | |
| docker build -t ghcr.io/nasty-project/nasty-csi:e2e-test . | |
| docker save ghcr.io/nasty-project/nasty-csi:e2e-test -o /tmp/csi-image.tar | |
| scp $SCP_OPTS /tmp/csi-image.tar ci@localhost:/tmp/csi-image.tar | |
| ssh $SSH_OPTS ci@localhost "sudo k3s ctr images import /tmp/csi-image.tar" | |
| - name: Download and import PR image | |
| if: inputs.pr != '' | |
| uses: actions/download-artifact@v7 | |
| with: | |
| name: pr-image | |
| path: /tmp | |
| - name: Import PR image into VM | |
| if: inputs.pr != '' | |
| run: | | |
| scp $SCP_OPTS /tmp/nasty-csi-pr-image.tar ci@localhost:/tmp/csi-image.tar | |
| ssh $SSH_OPTS ci@localhost "sudo k3s ctr images import /tmp/csi-image.tar" | |
| - name: Install Ginkgo | |
| run: go install github.com/onsi/ginkgo/v2/ginkgo@latest | |
| - name: Run iSCSI E2E Tests | |
| env: | |
| NASTY_HOST: ${{ secrets.NASTY_HOST_TAILSCALE || secrets.NASTY_HOST }} | |
| NASTY_API_KEY: ${{ secrets.NASTY_API_KEY }} | |
| NASTY_FILESYSTEM: ${{ secrets.NASTY_FILESYSTEM }} | |
| CSI_IMAGE_TAG: ${{ inputs.pr != '' && needs.compute-tag.outputs.image_tag || 'e2e-test' }} | |
| CSI_IMAGE_PULL_POLICY: Never | |
| KUBECONFIG: /tmp/kubeconfig | |
| CSI_CHART_PATH: ${{ github.workspace }}/nasty-chart | |
| run: | | |
| FOCUS="" | |
| if [ -n "${{ inputs.test }}" ]; then | |
| FOCUS="--focus=${{ inputs.test }}" | |
| fi | |
| ginkgo -v --timeout=100m --flake-attempts=2 $FOCUS ./tests/e2e/iscsi/... | |
| - name: Collect diagnostic logs | |
| if: always() | |
| env: | |
| KUBECONFIG: /tmp/kubeconfig | |
| run: | | |
| mkdir -p /tmp/test-logs | |
| kubectl logs -n kube-system -l app.kubernetes.io/component=controller -c nasty-csi-plugin --tail=-1 > /tmp/test-logs/controller.log 2>&1 || true | |
| kubectl logs -n kube-system -l app.kubernetes.io/component=node -c nasty-csi-plugin --tail=-1 > /tmp/test-logs/node.log 2>&1 || true | |
| kubectl get events --all-namespaces --sort-by='.lastTimestamp' > /tmp/test-logs/events.log 2>&1 || true | |
| kubectl get pvc --all-namespaces -o wide > /tmp/test-logs/pvcs.log 2>&1 || true | |
| ssh $SSH_OPTS ci@localhost "sudo dmesg | tail -100" > /tmp/test-logs/vm-dmesg.log 2>&1 || true | |
| - name: Upload diagnostic logs | |
| if: always() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: e2e-iscsi-diagnostics | |
| path: /tmp/test-logs/ | |
| retention-days: 7 | |
| - name: Cleanup VM | |
| if: always() | |
| run: | | |
| if [ -f /tmp/vm.pid ]; then | |
| kill "$(cat /tmp/vm.pid)" 2>/dev/null || true | |
| fi | |
| ginkgo-e2e-smb: | |
| name: "E2E: SMB" | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 120 | |
| needs: compute-tag | |
| if: | | |
| !cancelled() && needs.compute-tag.result == 'success' && | |
| (github.event.inputs.protocol == 'all' || github.event.inputs.protocol == 'smb' || github.event.inputs.protocol == '') | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ inputs.pr != '' && format('refs/pull/{0}/merge', inputs.pr) || '' }} | |
| - uses: actions/checkout@v6 | |
| with: | |
| repository: nasty-project/nasty-go | |
| token: ${{ secrets.NASTY_PAT }} | |
| path: nasty-go | |
| - uses: actions/checkout@v6 | |
| with: | |
| repository: nasty-project/nasty-chart | |
| token: ${{ secrets.NASTY_PAT }} | |
| path: nasty-chart | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: go.mod | |
| - name: Boot QEMU VM with K3S | |
| uses: ./.github/actions/qemu-vm | |
| with: | |
| nasty_host: ${{ secrets.NASTY_HOST_TAILSCALE || secrets.NASTY_HOST }} | |
| tailscale_authkey: ${{ secrets.TAILSCALE_AUTHKEY }} | |
| - name: Build and import CSI image | |
| if: inputs.pr == '' | |
| run: | | |
| docker build -t ghcr.io/nasty-project/nasty-csi:e2e-test . | |
| docker save ghcr.io/nasty-project/nasty-csi:e2e-test -o /tmp/csi-image.tar | |
| scp $SCP_OPTS /tmp/csi-image.tar ci@localhost:/tmp/csi-image.tar | |
| ssh $SSH_OPTS ci@localhost "sudo k3s ctr images import /tmp/csi-image.tar" | |
| - name: Download and import PR image | |
| if: inputs.pr != '' | |
| uses: actions/download-artifact@v7 | |
| with: | |
| name: pr-image | |
| path: /tmp | |
| - name: Import PR image into VM | |
| if: inputs.pr != '' | |
| run: | | |
| scp $SCP_OPTS /tmp/nasty-csi-pr-image.tar ci@localhost:/tmp/csi-image.tar | |
| ssh $SSH_OPTS ci@localhost "sudo k3s ctr images import /tmp/csi-image.tar" | |
| - name: Install Ginkgo | |
| run: go install github.com/onsi/ginkgo/v2/ginkgo@latest | |
| - name: Run SMB E2E Tests | |
| env: | |
| NASTY_HOST: ${{ secrets.NASTY_HOST_TAILSCALE || secrets.NASTY_HOST }} | |
| NASTY_API_KEY: ${{ secrets.NASTY_API_KEY }} | |
| NASTY_FILESYSTEM: ${{ secrets.NASTY_FILESYSTEM }} | |
| CSI_IMAGE_TAG: ${{ inputs.pr != '' && needs.compute-tag.outputs.image_tag || 'e2e-test' }} | |
| CSI_IMAGE_PULL_POLICY: Never | |
| KUBECONFIG: /tmp/kubeconfig | |
| CSI_CHART_PATH: ${{ github.workspace }}/nasty-chart | |
| SMB_USERNAME: ${{ secrets.SMB_USERNAME }} | |
| SMB_PASSWORD: ${{ secrets.SMB_PASSWORD }} | |
| run: | | |
| FOCUS="" | |
| if [ -n "${{ inputs.test }}" ]; then | |
| FOCUS="--focus=${{ inputs.test }}" | |
| fi | |
| ginkgo -v --timeout=100m --flake-attempts=2 $FOCUS ./tests/e2e/smb/... | |
| - name: Collect diagnostic logs | |
| if: always() | |
| env: | |
| KUBECONFIG: /tmp/kubeconfig | |
| NASTY_HOST: ${{ secrets.NASTY_HOST_TAILSCALE || secrets.NASTY_HOST }} | |
| SMB_USERNAME: ${{ secrets.SMB_USERNAME }} | |
| SMB_PASSWORD: ${{ secrets.SMB_PASSWORD }} | |
| run: | | |
| mkdir -p /tmp/test-logs | |
| kubectl logs -n kube-system -l app.kubernetes.io/component=controller -c nasty-csi-plugin --tail=-1 > /tmp/test-logs/controller.log 2>&1 || true | |
| kubectl logs -n kube-system -l app.kubernetes.io/component=node -c nasty-csi-plugin --tail=-1 > /tmp/test-logs/node.log 2>&1 || true | |
| kubectl get events --all-namespaces --sort-by='.lastTimestamp' > /tmp/test-logs/events.log 2>&1 || true | |
| kubectl get pvc --all-namespaces -o wide > /tmp/test-logs/pvcs.log 2>&1 || true | |
| smbclient -L "//${NASTY_HOST}" -U "${SMB_USERNAME}%${SMB_PASSWORD}" --option="client min protocol=SMB3" > /tmp/test-logs/smb-diag.log 2>&1 || true | |
| ssh $SSH_OPTS ci@localhost "sudo dmesg | tail -100" > /tmp/test-logs/vm-dmesg.log 2>&1 || true | |
| - name: Upload diagnostic logs | |
| if: always() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: e2e-smb-diagnostics | |
| path: /tmp/test-logs/ | |
| retention-days: 7 | |
| - name: Cleanup VM | |
| if: always() | |
| run: | | |
| if [ -f /tmp/vm.pid ]; then | |
| kill "$(cat /tmp/vm.pid)" 2>/dev/null || true | |
| fi | |
| ginkgo-e2e-shared: | |
| name: "E2E: Shared" | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 120 | |
| needs: compute-tag | |
| if: | | |
| !cancelled() && needs.compute-tag.result == 'success' | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ inputs.pr != '' && format('refs/pull/{0}/merge', inputs.pr) || '' }} | |
| - uses: actions/checkout@v6 | |
| with: | |
| repository: nasty-project/nasty-go | |
| token: ${{ secrets.NASTY_PAT }} | |
| path: nasty-go | |
| - uses: actions/checkout@v6 | |
| with: | |
| repository: nasty-project/nasty-chart | |
| token: ${{ secrets.NASTY_PAT }} | |
| path: nasty-chart | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: go.mod | |
| - name: Boot QEMU VM with K3S | |
| uses: ./.github/actions/qemu-vm | |
| with: | |
| nasty_host: ${{ secrets.NASTY_HOST_TAILSCALE || secrets.NASTY_HOST }} | |
| tailscale_authkey: ${{ secrets.TAILSCALE_AUTHKEY }} | |
| - name: Build and import CSI image | |
| if: inputs.pr == '' | |
| run: | | |
| docker build -t ghcr.io/nasty-project/nasty-csi:e2e-test . | |
| docker save ghcr.io/nasty-project/nasty-csi:e2e-test -o /tmp/csi-image.tar | |
| scp $SCP_OPTS /tmp/csi-image.tar ci@localhost:/tmp/csi-image.tar | |
| ssh $SSH_OPTS ci@localhost "sudo k3s ctr images import /tmp/csi-image.tar" | |
| - name: Download and import PR image | |
| if: inputs.pr != '' | |
| uses: actions/download-artifact@v7 | |
| with: | |
| name: pr-image | |
| path: /tmp | |
| - name: Import PR image into VM | |
| if: inputs.pr != '' | |
| run: | | |
| scp $SCP_OPTS /tmp/nasty-csi-pr-image.tar ci@localhost:/tmp/csi-image.tar | |
| ssh $SSH_OPTS ci@localhost "sudo k3s ctr images import /tmp/csi-image.tar" | |
| - name: Install Ginkgo | |
| run: go install github.com/onsi/ginkgo/v2/ginkgo@latest | |
| - name: Run Shared E2E Tests | |
| env: | |
| NASTY_HOST: ${{ secrets.NASTY_HOST_TAILSCALE || secrets.NASTY_HOST }} | |
| NASTY_API_KEY: ${{ secrets.NASTY_API_KEY }} | |
| NASTY_FILESYSTEM: ${{ secrets.NASTY_FILESYSTEM }} | |
| CSI_IMAGE_TAG: ${{ inputs.pr != '' && needs.compute-tag.outputs.image_tag || 'e2e-test' }} | |
| CSI_IMAGE_PULL_POLICY: Never | |
| KUBECONFIG: /tmp/kubeconfig | |
| CSI_CHART_PATH: ${{ github.workspace }}/nasty-chart | |
| SMB_USERNAME: ${{ secrets.SMB_USERNAME }} | |
| SMB_PASSWORD: ${{ secrets.SMB_PASSWORD }} | |
| run: | | |
| FOCUS="" | |
| if [ -n "${{ inputs.test }}" ]; then | |
| FOCUS="--focus=${{ inputs.test }}" | |
| fi | |
| ginkgo -v --timeout=100m --flake-attempts=2 $FOCUS ./tests/e2e/ | |
| - name: Collect diagnostic logs | |
| if: always() | |
| env: | |
| KUBECONFIG: /tmp/kubeconfig | |
| run: | | |
| mkdir -p /tmp/test-logs | |
| kubectl logs -n kube-system -l app.kubernetes.io/component=controller -c nasty-csi-plugin --tail=-1 > /tmp/test-logs/controller.log 2>&1 || true | |
| kubectl logs -n kube-system -l app.kubernetes.io/component=node -c nasty-csi-plugin --tail=-1 > /tmp/test-logs/node.log 2>&1 || true | |
| kubectl get events --all-namespaces --sort-by='.lastTimestamp' > /tmp/test-logs/events.log 2>&1 || true | |
| kubectl get pvc --all-namespaces -o wide > /tmp/test-logs/pvcs.log 2>&1 || true | |
| ssh $SSH_OPTS ci@localhost "sudo dmesg | tail -100" > /tmp/test-logs/vm-dmesg.log 2>&1 || true | |
| - name: Upload diagnostic logs | |
| if: always() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: e2e-shared-diagnostics | |
| path: /tmp/test-logs/ | |
| retention-days: 7 | |
| - name: Cleanup VM | |
| if: always() | |
| run: | | |
| if [ -f /tmp/vm.pid ]; then | |
| kill "$(cat /tmp/vm.pid)" 2>/dev/null || true | |
| fi | |
| # ========================================== | |
| # Summary | |
| # ========================================== | |
| test-summary: | |
| name: "Test Summary" | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| needs: [ginkgo-e2e-nfs, ginkgo-e2e-nvmeof, ginkgo-e2e-iscsi, ginkgo-e2e-smb, ginkgo-e2e-shared] | |
| if: always() | |
| steps: | |
| - name: Generate Test Summary | |
| uses: actions/github-script@v8 | |
| with: | |
| script: | | |
| const e2eResults = { | |
| 'NFS E2E': '${{ needs.ginkgo-e2e-nfs.result }}', | |
| 'NVMe-oF E2E': '${{ needs.ginkgo-e2e-nvmeof.result }}', | |
| 'iSCSI E2E': '${{ needs.ginkgo-e2e-iscsi.result }}', | |
| 'SMB E2E': '${{ needs.ginkgo-e2e-smb.result }}', | |
| 'Shared E2E': '${{ needs.ginkgo-e2e-shared.result }}', | |
| }; | |
| const getIcon = (result) => { | |
| switch(result) { | |
| case 'success': return '✅'; | |
| case 'failure': return '❌'; | |
| case 'skipped': return '⏭️'; | |
| case 'cancelled': return '🚫'; | |
| default: return '⚪'; | |
| } | |
| }; | |
| let passed = 0, failed = 0, skipped = 0; | |
| for (const result of Object.values(e2eResults)) { | |
| if (result === 'success') passed++; | |
| else if (result === 'failure') failed++; | |
| else if (result === 'skipped') skipped++; | |
| } | |
| const total = Object.keys(e2eResults).length; | |
| let summary = `# Integration Test Results\n\n`; | |
| if (failed > 0) { | |
| summary += `> ### ${failed} test(s) failed\n\n`; | |
| } else if (passed === total) { | |
| summary += `> ### All ${total} tests passed!\n\n`; | |
| } else { | |
| summary += `> ### ${passed}/${passed + failed} tests passed (${skipped} skipped)\n\n`; | |
| } | |
| summary += `| Passed | Failed | Skipped | Total |\n`; | |
| summary += `|:------:|:------:|:-------:|:-----:|\n`; | |
| summary += `| ${passed} | ${failed} | ${skipped} | ${total} |\n\n`; | |
| summary += `## E2E Tests\n\n`; | |
| summary += `| Test Suite | Status |\n`; | |
| summary += `|------------|:------:|\n`; | |
| for (const [test, result] of Object.entries(e2eResults)) { | |
| summary += `| ${test} | ${getIcon(result)} |\n`; | |
| } | |
| summary += `\n_Note: Shared E2E includes Snapshot Restore, Volume Stress, and Snapshot Stress tests for NFS, NVMe-oF, iSCSI, and SMB._\n`; | |
| await core.summary.addRaw(summary).write(); | |
| if (failed > 0) { | |
| core.setFailed(`${failed} test(s) failed`); | |
| } |