From 64dfd8649cd82c0aa52b941c436a3643003a1c64 Mon Sep 17 00:00:00 2001 From: jackthepunished Date: Tue, 14 Apr 2026 15:04:07 +0300 Subject: [PATCH 1/4] chore(staticcheck): resolve SA1019 warnings in libvirt and generated protobuf --- internal/autoscaler/protos/externalgrpc.pb.go | 2 ++ internal/repositories/libvirt/adapter.go | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/internal/autoscaler/protos/externalgrpc.pb.go b/internal/autoscaler/protos/externalgrpc.pb.go index 6bc0ceb36..efb7c52bf 100644 --- a/internal/autoscaler/protos/externalgrpc.pb.go +++ b/internal/autoscaler/protos/externalgrpc.pb.go @@ -19,6 +19,8 @@ // protoc v3.21.12 // source: cloudprovider/externalgrpc/protos/externalgrpc.proto +//lint:file-ignore SA1019 Generated protobuf compatibility shims may reference deprecated APIs. + package protos import ( diff --git a/internal/repositories/libvirt/adapter.go b/internal/repositories/libvirt/adapter.go index c4eb4c943..9bf54bea0 100644 --- a/internal/repositories/libvirt/adapter.go +++ b/internal/repositories/libvirt/adapter.go @@ -22,10 +22,22 @@ import ( "time" "github.com/digitalocean/go-libvirt" + libvirtsocket "github.com/digitalocean/go-libvirt/socket" "github.com/google/uuid" "github.com/poyrazk/thecloud/internal/core/ports" ) +type libvirtUnixDialer struct { + uri string + timeout time.Duration +} + +var _ libvirtsocket.Dialer = (*libvirtUnixDialer)(nil) + +func (d *libvirtUnixDialer) Dial() (net.Conn, error) { + return net.DialTimeout("unix", d.uri, d.timeout) +} + const ( defaultPoolName = "default" userDataFileName = "user-data" @@ -110,8 +122,8 @@ func NewLibvirtAdapter(logger *slog.Logger, uri string) (*LibvirtAdapter, error) } } - //nolint:staticcheck - l := libvirt.New(c) + _ = c.Close() + l := libvirt.NewWithDialer(&libvirtUnixDialer{uri: uri, timeout: 2 * time.Second}) adapter := &LibvirtAdapter{ client: &RealLibvirtClient{conn: l}, logger: logger, From 016fd3308acf2dc2819152cac7c8c607e5f770e7 Mon Sep 17 00:00:00 2001 From: jackthepunished Date: Tue, 14 Apr 2026 15:26:04 +0300 Subject: [PATCH 2/4] ci(benchmarks): disable gh-pages auto-push on fork PRs --- .github/workflows/benchmarks.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 6028dba78..4decdfdca 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -35,7 +35,8 @@ jobs: output-file-path: bench.txt # Access token to push results to gh-pages branch github-token: ${{ secrets.GITHUB_TOKEN }} - auto-push: true + # Fork PRs cannot push to upstream gh-pages with GITHUB_TOKEN. + auto-push: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} # Fail if performance drops by more than 50% alert-threshold: '200%' comment-on-alert: true From 5d84594b9fac27b91cc77fafb7b8ad9fdd178b25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Poyraz=20K=C3=BC=C3=A7=C3=BCkarslan?= <83272398+PoyrazK@users.noreply.github.com> Date: Sun, 26 Apr 2026 18:43:16 +0300 Subject: [PATCH 3/4] test(libvirt): add unit tests for libvirtUnixDialer and injectable SocketDialer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add SocketDialer interface for dependency injection - Add WithSocketDialer functional option for testing - Add unit tests for libvirtUnixDialer (interface compliance, invalid path, timeout) - Add test for WithSocketDialer option application Co-authored-by: Poyraz Küçükarslan --- internal/repositories/libvirt/adapter.go | 75 +++++++++++++----- .../repositories/libvirt/adapter_unit_test.go | 76 +++++++++++++++++++ 2 files changed, 130 insertions(+), 21 deletions(-) diff --git a/internal/repositories/libvirt/adapter.go b/internal/repositories/libvirt/adapter.go index 4495ecbd1..4be947501 100644 --- a/internal/repositories/libvirt/adapter.go +++ b/internal/repositories/libvirt/adapter.go @@ -38,6 +38,22 @@ func (d *libvirtUnixDialer) Dial() (net.Conn, error) { return net.DialTimeout("unix", d.uri, d.timeout) } +// SocketDialer is the interface for creating network connections. +// It allows dependency injection for testing. +type SocketDialer interface { + Dial() (net.Conn, error) +} + +// DialerOption configures NewLibvirtAdapter. +type DialerOption func(*LibvirtAdapter) + +// WithSocketDialer injects a custom socket dialer for testing. +func WithSocketDialer(d SocketDialer) DialerOption { + return func(a *LibvirtAdapter) { + a.socketDialer = d + } +} + const ( defaultPoolName = "default" userDataFileName = "user-data" @@ -78,6 +94,9 @@ type LibvirtAdapter struct { execCommandContext func(ctx context.Context, name string, arg ...string) *exec.Cmd lookPath func(file string) (string, error) osOpen func(name string) (*os.File, error) + + // Socket dialer for testability + socketDialer SocketDialer } func (a *LibvirtAdapter) recordPortMapping(name string, hPortStr string, cPort string) error { @@ -97,7 +116,7 @@ func (a *LibvirtAdapter) recordPortMapping(name string, hPortStr string, cPort s } // NewLibvirtAdapter creates a LibvirtAdapter connected to the provided URI. -func NewLibvirtAdapter(logger *slog.Logger, uri string) (*LibvirtAdapter, error) { +func NewLibvirtAdapter(logger *slog.Logger, uri string, opts ...DialerOption) (*LibvirtAdapter, error) { if uri == "" { uri = os.Getenv("LIBVIRT_URI") } @@ -105,27 +124,8 @@ func NewLibvirtAdapter(logger *slog.Logger, uri string) (*LibvirtAdapter, error) uri = "/var/run/libvirt/libvirt-sock" } - // Connect to libvirt socket - c, err := net.DialTimeout("unix", uri, 2*time.Second) - if err != nil { - // Fallback to session mode if system socket fails - if !strings.Contains(uri, "session") { - sessionUri := filepath.Join(os.Getenv("HOME"), ".cache/libvirt/libvirt-sock") - if c2, err2 := net.DialTimeout("unix", sessionUri, 2*time.Second); err2 == nil { - c = c2 - uri = sessionUri - } else { - return nil, fmt.Errorf("failed to dial libvirt (system and session): %w", err) - } - } else { - return nil, fmt.Errorf("failed to dial libvirt: %w", err) - } - } - - _ = c.Close() - l := libvirt.NewWithDialer(&libvirtUnixDialer{uri: uri, timeout: 2 * time.Second}) + // Create adapter with default dialer first (needed for option application) adapter := &LibvirtAdapter{ - client: &RealLibvirtClient{conn: l}, logger: logger, uri: uri, portMappings: make(map[string]map[string]int), @@ -140,6 +140,39 @@ func NewLibvirtAdapter(logger *slog.Logger, uri string) (*LibvirtAdapter, error) osOpen: os.Open, } + // Apply options (e.g., inject mock dialer for testing) + for _, opt := range opts { + opt(adapter) + } + + // Determine dialer to use + var dialer SocketDialer + if adapter.socketDialer != nil { + dialer = adapter.socketDialer + } else { + dialer = &libvirtUnixDialer{uri: uri, timeout: 2 * time.Second} + } + + // Verify connectivity + if _, err := dialer.Dial(); err != nil { + // Fallback to session mode if system socket fails + if !strings.Contains(uri, "session") { + sessionUri := filepath.Join(os.Getenv("HOME"), ".cache/libvirt/libvirt-sock") + sessionDialer := &libvirtUnixDialer{uri: sessionUri, timeout: 2 * time.Second} + if _, err2 := sessionDialer.Dial(); err2 == nil { + dialer = sessionDialer + adapter.uri = sessionUri + } else { + return nil, fmt.Errorf("failed to dial libvirt (system and session): %w", err) + } + } else { + return nil, fmt.Errorf("failed to dial libvirt: %w", err) + } + } + + l := libvirt.NewWithDialer(dialer) + adapter.client = &RealLibvirtClient{conn: l} + connectCtx, connectCancel := context.WithTimeout(context.Background(), 10*time.Second) defer connectCancel() diff --git a/internal/repositories/libvirt/adapter_unit_test.go b/internal/repositories/libvirt/adapter_unit_test.go index 6b8f87f9b..e95eed5b7 100644 --- a/internal/repositories/libvirt/adapter_unit_test.go +++ b/internal/repositories/libvirt/adapter_unit_test.go @@ -829,3 +829,79 @@ func TestLibvirtAdapter_IsNotFound(t *testing.T) { assert.False(t, a.isNotFound(err)) }) } + +func TestLibvirtUnixDialer(t *testing.T) { + t.Parallel() + + t.Run("ImplementsSocketDialerInterface", func(t *testing.T) { + t.Parallel() + var dialer SocketDialer = &libvirtUnixDialer{uri: "/var/run/libvirt/libvirt-sock", timeout: 2 * time.Second} + assert.NotNil(t, dialer) + }) + + t.Run("DialTimeoutReturnsConn", func(t *testing.T) { + t.Parallel() + // This test requires a real Unix socket to exist. + // If /var/run/libvirt/libvirt-sock doesn't exist, this will fail. + // In CI without libvirt, we expect this to fail gracefully. + dialer := &libvirtUnixDialer{uri: "/var/run/libvirt/libvirt-sock", timeout: 100 * time.Millisecond} + conn, err := dialer.Dial() + if err != nil { + // Expected if no libvirt socket exists (e.g., in CI without libvirt) + assert.NotNil(t, err) + return + } + assert.NotNil(t, conn) + conn.Close() + }) + + t.Run("DialInvalidPathReturnsError", func(t *testing.T) { + t.Parallel() + dialer := &libvirtUnixDialer{uri: "/nonexistent/path/to/socket", timeout: 100 * time.Millisecond} + conn, err := dialer.Dial() + assert.Error(t, err) + assert.Nil(t, conn) + }) + + t.Run("DialWithVeryShortTimeout", func(t *testing.T) { + t.Parallel() + dialer := &libvirtUnixDialer{uri: "/var/run/libvirt/libvirt-sock", timeout: 1 * time.Millisecond} + conn, err := dialer.Dial() + // Should either succeed quickly or timeout/fail + if err != nil { + assert.NotNil(t, err) + } else { + assert.NotNil(t, conn) + conn.Close() + } + }) +} + +func TestWithSocketDialerOption(t *testing.T) { + t.Parallel() + + t.Run("SetsSocketDialerField", func(t *testing.T) { + t.Parallel() + logger := slog.New(slog.NewTextHandler(io.Discard, nil)) + + // Create a mock dialer + mockDialer := &mockSocketDialer{err: fmt.Errorf("test error")} + + // Create adapter with the option + a := &LibvirtAdapter{logger: logger} + WithSocketDialer(mockDialer)(a) + + // Verify the dialer was set + assert.Equal(t, mockDialer, a.socketDialer) + }) +} + +// mockSocketDialer is a test double for SocketDialer. +type mockSocketDialer struct { + conn net.Conn + err error +} + +func (m *mockSocketDialer) Dial() (net.Conn, error) { + return m.conn, m.err +} From 534ea70e52f106cfac041363089e506a196e5351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Poyraz=20K=C3=BC=C3=A7=C3=BCkarslan?= <83272398+PoyrazK@users.noreply.github.com> Date: Sun, 26 Apr 2026 19:00:02 +0300 Subject: [PATCH 4/4] fix(lint): address testifylint issues in dialer tests - Use require.Error instead of assert.Error for error checks - Remove unnecessary assert calls in conditional branches - Use require.NotNil for connection checks --- internal/repositories/libvirt/adapter_unit_test.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/internal/repositories/libvirt/adapter_unit_test.go b/internal/repositories/libvirt/adapter_unit_test.go index e95eed5b7..52801d0b2 100644 --- a/internal/repositories/libvirt/adapter_unit_test.go +++ b/internal/repositories/libvirt/adapter_unit_test.go @@ -848,10 +848,9 @@ func TestLibvirtUnixDialer(t *testing.T) { conn, err := dialer.Dial() if err != nil { // Expected if no libvirt socket exists (e.g., in CI without libvirt) - assert.NotNil(t, err) return } - assert.NotNil(t, conn) + require.NotNil(t, conn) conn.Close() }) @@ -859,7 +858,7 @@ func TestLibvirtUnixDialer(t *testing.T) { t.Parallel() dialer := &libvirtUnixDialer{uri: "/nonexistent/path/to/socket", timeout: 100 * time.Millisecond} conn, err := dialer.Dial() - assert.Error(t, err) + require.Error(t, err) assert.Nil(t, conn) }) @@ -869,11 +868,11 @@ func TestLibvirtUnixDialer(t *testing.T) { conn, err := dialer.Dial() // Should either succeed quickly or timeout/fail if err != nil { - assert.NotNil(t, err) - } else { - assert.NotNil(t, conn) - conn.Close() + // Expected - socket unreachable or timeout + return } + require.NotNil(t, conn) + conn.Close() }) }