diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..ddedcce --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,118 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +concurrency: + group: ci-${{ github.head_ref || github.ref }} + cancel-in-progress: true + +jobs: + unit: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version-file: go.mod + cache: true + + - name: Unit tests + run: make unit + + - name: Vet + run: make vet + + - name: Lint + run: make lint + + # TODO: Replace with fetching a released binary once bink publishes GitHub + # releases. + build-bink: + runs-on: ubuntu-latest + steps: + - name: Checkout bink + uses: actions/checkout@v6 + with: + repository: alicefr/bink + + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version-file: go.mod + cache: true + + - name: Install build dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + libgpgme-dev \ + libbtrfs-dev \ + libdevmapper-dev \ + pkg-config + + - name: Build bink + run: make build-bink + + - name: Upload bink binary + uses: actions/upload-artifact@v7 + with: + name: bink + path: bink + + e2e: + runs-on: ubuntu-latest + needs: build-bink + timeout-minutes: 30 + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Download bink binary + uses: actions/download-artifact@v8 + with: + name: bink + + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version-file: go.mod + cache: true + + - name: Set up KVM + run: sudo chmod 666 /dev/kvm + + - name: Configure kernel + run: | + # Unload AppArmor profiles — the passt profile blocks remount + # operations needed for passt's self-sandboxing inside containers. + sudo aa-teardown 2>/dev/null || true + # Allow unprivileged user namespace creation (needed by passt + # inside containers). + sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 + + - name: Enable KSM + run: | + sudo sh -c 'echo 1 > /sys/kernel/mm/ksm/run' + sudo sh -c 'echo 5000 > /sys/kernel/mm/ksm/pages_to_scan' + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y podman + + - name: Start podman socket + run: systemctl --user start podman.socket + + - name: Run e2e tests + run: | + chmod +x bink + make e2e V=1 + env: + BINK_PATH: ${{ github.workspace }}/bink diff --git a/internal/controller/crd_test.go b/internal/controller/crd_test.go index 1766860..60480a9 100644 --- a/internal/controller/crd_test.go +++ b/internal/controller/crd_test.go @@ -221,7 +221,7 @@ func TestBootcNodePoolEnumValidation(t *testing.T) { testutil.WithRebootPolicy("Invalid"), ) if err := k8sClient.Create(ctx, pool); err == nil { - k8sClient.Delete(ctx, pool) + _ = k8sClient.Delete(ctx, pool) t.Fatal("Expected creation with invalid rebootPolicy to fail, but it succeeded") } } @@ -232,7 +232,7 @@ func TestBootcNodeEnumValidation(t *testing.T) { node := testutil.NewNode("invalid-image-state", testImageDigestRefA) node.Spec.DesiredImageState = "Invalid" if err := k8sClient.Create(ctx, node); err == nil { - k8sClient.Delete(ctx, node) + _ = k8sClient.Delete(ctx, node) t.Fatal("Expected creation with invalid desiredImageState to fail, but it succeeded") } } @@ -242,7 +242,7 @@ func TestBootcNodePoolMinLengthValidation(t *testing.T) { pool := testutil.NewPool("empty-image-ref", "") if err := k8sClient.Create(ctx, pool); err == nil { - k8sClient.Delete(ctx, pool) + _ = k8sClient.Delete(ctx, pool) t.Fatal("Expected creation with empty image.ref to fail, but it succeeded") } } @@ -252,7 +252,7 @@ func TestBootcNodeMinLengthValidation(t *testing.T) { node := testutil.NewNode("empty-desired-image", "") if err := k8sClient.Create(ctx, node); err == nil { - k8sClient.Delete(ctx, node) + _ = k8sClient.Delete(ctx, node) t.Fatal("Expected creation with empty desiredImage to fail, but it succeeded") } }