Skip to content

Integration Tests

Integration Tests #127

Workflow file for this run

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`);
}