mirror of
https://github.com/coder/coder.git
synced 2026-06-04 13:38:21 +00:00
1069ce6e19
relates to #21335 Adds support for the agentsock and thus `coder exp sync` commands on Windows. This support was initially missing.
355 lines
10 KiB
Go
355 lines
10 KiB
Go
package agentsocket_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"cdr.dev/slog/v3"
|
|
"github.com/coder/coder/v2/agent/agentsocket"
|
|
"github.com/coder/coder/v2/agent/unit"
|
|
"github.com/coder/coder/v2/testutil"
|
|
)
|
|
|
|
// newSocketClient creates a DRPC client connected to the Unix socket at the given path.
|
|
func newSocketClient(ctx context.Context, t *testing.T, socketPath string) *agentsocket.Client {
|
|
t.Helper()
|
|
|
|
client, err := agentsocket.NewClient(ctx, agentsocket.WithPath(socketPath))
|
|
t.Cleanup(func() {
|
|
_ = client.Close()
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
return client
|
|
}
|
|
|
|
func TestDRPCAgentSocketService(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("Ping", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
socketPath := testutil.AgentSocketPath(t)
|
|
ctx := testutil.Context(t, testutil.WaitShort)
|
|
server, err := agentsocket.NewServer(
|
|
slog.Make().Leveled(slog.LevelDebug),
|
|
agentsocket.WithPath(socketPath),
|
|
)
|
|
require.NoError(t, err)
|
|
defer server.Close()
|
|
|
|
client := newSocketClient(ctx, t, socketPath)
|
|
|
|
err = client.Ping(ctx)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("SyncStart", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("NewUnit", func(t *testing.T) {
|
|
t.Parallel()
|
|
socketPath := testutil.AgentSocketPath(t)
|
|
ctx := testutil.Context(t, testutil.WaitShort)
|
|
server, err := agentsocket.NewServer(
|
|
slog.Make().Leveled(slog.LevelDebug),
|
|
agentsocket.WithPath(socketPath),
|
|
)
|
|
require.NoError(t, err)
|
|
defer server.Close()
|
|
|
|
client := newSocketClient(ctx, t, socketPath)
|
|
|
|
err = client.SyncStart(ctx, "test-unit")
|
|
require.NoError(t, err)
|
|
|
|
status, err := client.SyncStatus(ctx, "test-unit")
|
|
require.NoError(t, err)
|
|
require.Equal(t, unit.StatusStarted, status.Status)
|
|
})
|
|
|
|
t.Run("UnitAlreadyStarted", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
socketPath := testutil.AgentSocketPath(t)
|
|
ctx := testutil.Context(t, testutil.WaitShort)
|
|
server, err := agentsocket.NewServer(
|
|
slog.Make().Leveled(slog.LevelDebug),
|
|
agentsocket.WithPath(socketPath),
|
|
)
|
|
require.NoError(t, err)
|
|
defer server.Close()
|
|
|
|
client := newSocketClient(ctx, t, socketPath)
|
|
|
|
// First Start
|
|
err = client.SyncStart(ctx, "test-unit")
|
|
require.NoError(t, err)
|
|
status, err := client.SyncStatus(ctx, "test-unit")
|
|
require.NoError(t, err)
|
|
require.Equal(t, unit.StatusStarted, status.Status)
|
|
|
|
// Second Start
|
|
err = client.SyncStart(ctx, "test-unit")
|
|
require.ErrorContains(t, err, unit.ErrSameStatusAlreadySet.Error())
|
|
|
|
status, err = client.SyncStatus(ctx, "test-unit")
|
|
require.NoError(t, err)
|
|
require.Equal(t, unit.StatusStarted, status.Status)
|
|
})
|
|
|
|
t.Run("UnitAlreadyCompleted", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
socketPath := testutil.AgentSocketPath(t)
|
|
ctx := testutil.Context(t, testutil.WaitShort)
|
|
server, err := agentsocket.NewServer(
|
|
slog.Make().Leveled(slog.LevelDebug),
|
|
agentsocket.WithPath(socketPath),
|
|
)
|
|
require.NoError(t, err)
|
|
defer server.Close()
|
|
|
|
client := newSocketClient(ctx, t, socketPath)
|
|
|
|
// First start
|
|
err = client.SyncStart(ctx, "test-unit")
|
|
require.NoError(t, err)
|
|
|
|
status, err := client.SyncStatus(ctx, "test-unit")
|
|
require.NoError(t, err)
|
|
require.Equal(t, unit.StatusStarted, status.Status)
|
|
|
|
// Complete the unit
|
|
err = client.SyncComplete(ctx, "test-unit")
|
|
require.NoError(t, err)
|
|
|
|
status, err = client.SyncStatus(ctx, "test-unit")
|
|
require.NoError(t, err)
|
|
require.Equal(t, unit.StatusComplete, status.Status)
|
|
|
|
// Second start
|
|
err = client.SyncStart(ctx, "test-unit")
|
|
require.NoError(t, err)
|
|
|
|
status, err = client.SyncStatus(ctx, "test-unit")
|
|
require.NoError(t, err)
|
|
require.Equal(t, unit.StatusStarted, status.Status)
|
|
})
|
|
|
|
t.Run("UnitNotReady", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
socketPath := testutil.AgentSocketPath(t)
|
|
ctx := testutil.Context(t, testutil.WaitShort)
|
|
server, err := agentsocket.NewServer(
|
|
slog.Make().Leveled(slog.LevelDebug),
|
|
agentsocket.WithPath(socketPath),
|
|
)
|
|
require.NoError(t, err)
|
|
defer server.Close()
|
|
|
|
client := newSocketClient(ctx, t, socketPath)
|
|
|
|
err = client.SyncWant(ctx, "test-unit", "dependency-unit")
|
|
require.NoError(t, err)
|
|
|
|
err = client.SyncStart(ctx, "test-unit")
|
|
require.ErrorContains(t, err, "unit not ready")
|
|
|
|
status, err := client.SyncStatus(ctx, "test-unit")
|
|
require.NoError(t, err)
|
|
require.Equal(t, unit.StatusPending, status.Status)
|
|
require.False(t, status.IsReady)
|
|
})
|
|
})
|
|
|
|
t.Run("SyncWant", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("NewUnits", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
socketPath := testutil.AgentSocketPath(t)
|
|
ctx := testutil.Context(t, testutil.WaitShort)
|
|
server, err := agentsocket.NewServer(
|
|
slog.Make().Leveled(slog.LevelDebug),
|
|
agentsocket.WithPath(socketPath),
|
|
)
|
|
require.NoError(t, err)
|
|
defer server.Close()
|
|
|
|
client := newSocketClient(ctx, t, socketPath)
|
|
|
|
// If dependency units are not registered, they are registered automatically
|
|
err = client.SyncWant(ctx, "test-unit", "dependency-unit")
|
|
require.NoError(t, err)
|
|
|
|
status, err := client.SyncStatus(ctx, "test-unit")
|
|
require.NoError(t, err)
|
|
require.Len(t, status.Dependencies, 1)
|
|
require.Equal(t, unit.ID("dependency-unit"), status.Dependencies[0].DependsOn)
|
|
require.Equal(t, unit.StatusComplete, status.Dependencies[0].RequiredStatus)
|
|
})
|
|
|
|
t.Run("DependencyAlreadyRegistered", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
socketPath := testutil.AgentSocketPath(t)
|
|
ctx := testutil.Context(t, testutil.WaitShort)
|
|
server, err := agentsocket.NewServer(
|
|
slog.Make().Leveled(slog.LevelDebug),
|
|
agentsocket.WithPath(socketPath),
|
|
)
|
|
require.NoError(t, err)
|
|
defer server.Close()
|
|
|
|
client := newSocketClient(ctx, t, socketPath)
|
|
|
|
// Start the dependency unit
|
|
err = client.SyncStart(ctx, "dependency-unit")
|
|
require.NoError(t, err)
|
|
|
|
status, err := client.SyncStatus(ctx, "dependency-unit")
|
|
require.NoError(t, err)
|
|
require.Equal(t, unit.StatusStarted, status.Status)
|
|
|
|
// Add the dependency after the dependency unit has already started
|
|
err = client.SyncWant(ctx, "test-unit", "dependency-unit")
|
|
|
|
// Dependencies can be added even if the dependency unit has already started
|
|
require.NoError(t, err)
|
|
|
|
// The dependency is now reflected in the test unit's status
|
|
status, err = client.SyncStatus(ctx, "test-unit")
|
|
require.NoError(t, err)
|
|
require.Equal(t, unit.ID("dependency-unit"), status.Dependencies[0].DependsOn)
|
|
require.Equal(t, unit.StatusComplete, status.Dependencies[0].RequiredStatus)
|
|
})
|
|
|
|
t.Run("DependencyAddedAfterDependentStarted", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
socketPath := testutil.AgentSocketPath(t)
|
|
ctx := testutil.Context(t, testutil.WaitShort)
|
|
server, err := agentsocket.NewServer(
|
|
slog.Make().Leveled(slog.LevelDebug),
|
|
agentsocket.WithPath(socketPath),
|
|
)
|
|
require.NoError(t, err)
|
|
defer server.Close()
|
|
|
|
client := newSocketClient(ctx, t, socketPath)
|
|
|
|
// Start the dependent unit
|
|
err = client.SyncStart(ctx, "test-unit")
|
|
require.NoError(t, err)
|
|
|
|
status, err := client.SyncStatus(ctx, "test-unit")
|
|
require.NoError(t, err)
|
|
require.Equal(t, unit.StatusStarted, status.Status)
|
|
|
|
// Add the dependency after the dependency unit has already started
|
|
err = client.SyncWant(ctx, "test-unit", "dependency-unit")
|
|
|
|
// Dependencies can be added even if the dependent unit has already started.
|
|
// The dependency applies the next time a unit is started. The current status is not updated.
|
|
// This is to allow flexible dependency management. It does mean that users of this API should
|
|
// take care to add dependencies before they start their dependent units.
|
|
require.NoError(t, err)
|
|
|
|
// The dependency is now reflected in the test unit's status
|
|
status, err = client.SyncStatus(ctx, "test-unit")
|
|
require.NoError(t, err)
|
|
require.Equal(t, unit.ID("dependency-unit"), status.Dependencies[0].DependsOn)
|
|
require.Equal(t, unit.StatusComplete, status.Dependencies[0].RequiredStatus)
|
|
})
|
|
})
|
|
|
|
t.Run("SyncReady", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("UnregisteredUnit", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
socketPath := testutil.AgentSocketPath(t)
|
|
ctx := testutil.Context(t, testutil.WaitShort)
|
|
server, err := agentsocket.NewServer(
|
|
slog.Make().Leveled(slog.LevelDebug),
|
|
agentsocket.WithPath(socketPath),
|
|
)
|
|
require.NoError(t, err)
|
|
defer server.Close()
|
|
|
|
client := newSocketClient(ctx, t, socketPath)
|
|
|
|
ready, err := client.SyncReady(ctx, "unregistered-unit")
|
|
require.NoError(t, err)
|
|
require.True(t, ready)
|
|
})
|
|
|
|
t.Run("UnitNotReady", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
socketPath := testutil.AgentSocketPath(t)
|
|
ctx := testutil.Context(t, testutil.WaitShort)
|
|
server, err := agentsocket.NewServer(
|
|
slog.Make().Leveled(slog.LevelDebug),
|
|
agentsocket.WithPath(socketPath),
|
|
)
|
|
require.NoError(t, err)
|
|
defer server.Close()
|
|
|
|
client := newSocketClient(ctx, t, socketPath)
|
|
|
|
// Register a unit with an unsatisfied dependency
|
|
err = client.SyncWant(ctx, "test-unit", "dependency-unit")
|
|
require.NoError(t, err)
|
|
|
|
// Check readiness - should be false because dependency is not satisfied
|
|
ready, err := client.SyncReady(ctx, "test-unit")
|
|
require.NoError(t, err)
|
|
require.False(t, ready)
|
|
})
|
|
|
|
t.Run("UnitReady", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
socketPath := testutil.AgentSocketPath(t)
|
|
ctx := testutil.Context(t, testutil.WaitShort)
|
|
server, err := agentsocket.NewServer(
|
|
slog.Make().Leveled(slog.LevelDebug),
|
|
agentsocket.WithPath(socketPath),
|
|
)
|
|
require.NoError(t, err)
|
|
defer server.Close()
|
|
|
|
client := newSocketClient(ctx, t, socketPath)
|
|
|
|
// Register a unit with no dependencies - should be ready immediately
|
|
err = client.SyncStart(ctx, "test-unit")
|
|
require.NoError(t, err)
|
|
|
|
// Check readiness - should be true
|
|
ready, err := client.SyncReady(ctx, "test-unit")
|
|
require.NoError(t, err)
|
|
require.True(t, ready)
|
|
|
|
// Also test a unit with satisfied dependencies
|
|
err = client.SyncWant(ctx, "dependent-unit", "test-unit")
|
|
require.NoError(t, err)
|
|
|
|
// Complete the dependency
|
|
err = client.SyncComplete(ctx, "test-unit")
|
|
require.NoError(t, err)
|
|
|
|
// Now dependent-unit should be ready
|
|
ready, err = client.SyncReady(ctx, "dependent-unit")
|
|
require.NoError(t, err)
|
|
require.True(t, ready)
|
|
})
|
|
})
|
|
}
|