diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index badf1d0..6e097a3 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -51,9 +51,9 @@ jobs: run: make -C example/macOS if: runner.arch == 'ARM64' test: - needs: build + needs: formatting-check runs-on: ${{ matrix.os }} - timeout-minutes: 30 + timeout-minutes: 3 strategy: fail-fast: false # Can't expand the matrix due to the flakiness of the CI infra @@ -73,4 +73,3 @@ jobs: run: make download_kernel - name: Unit Test run: make test - timeout-minutes: 10 diff --git a/Makefile b/Makefile index 4197b2e..d0094fd 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -PUIPUI_LINUX_VERSION := 0.0.1 +PUIPUI_LINUX_VERSION := 1.0.3 ARCH := $(shell uname -m) KERNEL_ARCH := $(shell echo $(ARCH) | sed -e s/arm64/aarch64/) KERNEL_TAR := puipui_linux_v$(PUIPUI_LINUX_VERSION)_$(KERNEL_ARCH).tar.gz @@ -10,7 +10,7 @@ fmt: .PHONY: test test: - go test -p 1 -exec "go run $(PWD)/cmd/codesign" ./... -timeout 60s -v + go test -p 1 -exec "go run $(PWD)/cmd/codesign" ./... -timeout 2m -v .PHONY: test/run test/run: diff --git a/virtualization_test.go b/virtualization_test.go index f42d8dd..d56a736 100644 --- a/virtualization_test.go +++ b/virtualization_test.go @@ -3,6 +3,8 @@ package vz_test import ( "errors" "fmt" + "math" + "net" "os" "runtime" "syscall" @@ -111,15 +113,30 @@ func (c *Container) NewSession(t *testing.T) *ssh.Session { return sshSession } +func getFreePort() (int, error) { + l, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + return 0, err + } + defer l.Close() + return l.Addr().(*net.TCPAddr).Port, nil +} + func newVirtualizationMachine( t *testing.T, configs ...func(*vz.VirtualMachineConfiguration) error, ) *Container { + port, err := getFreePort() + if err != nil { + t.Fatalf("failed to resolve free tcp addr: %v", err) + } + vmlinuz := "./testdata/Image" initramfs := "./testdata/initramfs.cpio.gz" + cmdline := fmt.Sprintf("console=hvc0 vsock_port=%d", port) bootLoader, err := vz.NewLinuxBootLoader( vmlinuz, - vz.WithCommandLine("console=hvc0"), + vz.WithCommandLine(cmdline), vz.WithInitrd(initramfs), ) if err != nil { @@ -172,28 +189,52 @@ func newVirtualizationMachine( time.Sleep(5 * time.Second) } + const max = 5 RETRY: for i := 1; ; i++ { - conn, err := socketDevice.Connect(2222) + conn, err := socketDevice.Connect(uint32(port)) if err != nil { var nserr *vz.NSError - if !errors.As(err, &nserr) || i > 5 { + if !errors.As(err, &nserr) || i > max { t.Fatal(err) } if nserr.Code == int(syscall.ECONNRESET) { t.Logf("retry vsock connect: %d", i) - time.Sleep(time.Second) + time.Sleep(backOffDelay(i)) continue RETRY } t.Fatalf("failed to connect vsock: %v", err) } + t.Log("setup ssh client in container") + + initialized := make(chan struct{}) + retry := make(chan struct{}) + go func() { + select { + case <-initialized: + case <-time.After(5 * time.Second): + close(retry) + t.Log("closed", conn.Close()) + } + }() + sshClient, err := testhelper.NewSshClient(conn, ":22", sshConfig) if err != nil { + select { + case <-retry: + t.Log("retry because ssh handshake has been failed") + continue RETRY + default: + } conn.Close() t.Fatalf("failed to create a new ssh client: %v", err) } + close(initialized) + + t.Logf("container setup done") + return &Container{ VirtualMachine: vm, Client: sshClient, @@ -201,6 +242,12 @@ RETRY: } } +func backOffDelay(retryAttempts int) time.Duration { + factor := 0.5 + delay := math.Exp2(float64(retryAttempts)) * factor + return time.Duration(math.Min(delay, 10)) * time.Second +} + func waitState(t *testing.T, wait time.Duration, vm *vz.VirtualMachine, want vz.VirtualMachineState) { t.Helper() select {