mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
bddb808b25
Fixes all our Go file imports to match the preferred spec that we've _mostly_ been using. For example: ``` import ( "context" "time" "github.com/prometheus/client_golang/prometheus" "golang.org/x/xerrors" "gopkg.in/natefinch/lumberjack.v2" "cdr.dev/slog/v3" "github.com/coder/coder/v2/codersdk/agentsdk" "github.com/coder/serpent" ) ``` 3 groups: standard library, 3rd partly libs, Coder libs. This PR makes the change across the codebase. The PR in the stack above modifies our formatting to maintain this state of affairs, and is a separate PR so it's possible to review that one in detail.
361 lines
11 KiB
Go
361 lines
11 KiB
Go
package agentsocket_test
|
|
|
|
import (
|
|
"context"
|
|
"path/filepath"
|
|
"runtime"
|
|
"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()
|
|
|
|
if runtime.GOOS == "windows" {
|
|
t.Skip("agentsocket is not supported on Windows")
|
|
}
|
|
|
|
t.Run("Ping", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
socketPath := filepath.Join(testutil.TempDirUnixSocket(t), "test.sock")
|
|
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 := filepath.Join(testutil.TempDirUnixSocket(t), "test.sock")
|
|
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 := filepath.Join(testutil.TempDirUnixSocket(t), "test.sock")
|
|
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 := filepath.Join(testutil.TempDirUnixSocket(t), "test.sock")
|
|
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 := filepath.Join(testutil.TempDirUnixSocket(t), "test.sock")
|
|
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 := filepath.Join(testutil.TempDirUnixSocket(t), "test.sock")
|
|
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 := filepath.Join(testutil.TempDirUnixSocket(t), "test.sock")
|
|
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 := filepath.Join(testutil.TempDirUnixSocket(t), "test.sock")
|
|
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 := filepath.Join(testutil.TempDirUnixSocket(t), "test.sock")
|
|
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 := filepath.Join(testutil.TempDirUnixSocket(t), "test.sock")
|
|
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 := filepath.Join(testutil.TempDirUnixSocket(t), "test.sock")
|
|
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)
|
|
})
|
|
})
|
|
}
|