mirror of
https://github.com/coder/coder.git
synced 2025-07-18 14:17:22 +00:00
feat: add support for coder_script
(#9584)
* Add basic migrations * Improve schema * Refactor agent scripts into it's own package * Support legacy start and stop script format * Pipe the scripts! * Finish the piping * Fix context usage * It works! * Fix sql query * Fix SQL query * Rename `LogSourceID` -> `SourceID` * Fix the FE * fmt * Rename migrations * Fix log tests * Fix lint err * Fix gen * Fix story type * Rename source to script * Fix schema jank * Uncomment test * Rename proto to TimeoutSeconds * Fix comments * Fix comments * Fix legacy endpoint without specified log_source * Fix non-blocking by default in agent * Fix resources tests * Fix dbfake * Fix resources * Fix linting I think * Add fixtures * fmt * Fix startup script behavior * Fix comments * Fix context * Fix cancel * Fix SQL tests * Fix e2e tests * Interrupt on Windows * Fix agent leaking script process * Fix migrations * Fix stories * Fix duplicate logs appearing * Gen * Fix log location * Fix tests * Fix tests * Fix log output * Show display name in output * Fix print * Return timeout on start context * Gen * Fix fixture * Fix the agent status * Fix startup timeout msg * Fix command using shared context * Fix timeout draining * Change signal type * Add deterministic colors to startup script logs --------- Co-authored-by: Muhammad Atif Ali <atif@coder.com>
This commit is contained in:
@ -50,7 +50,6 @@ import (
|
||||
"github.com/coder/coder/v2/agent/agentproc/agentproctest"
|
||||
"github.com/coder/coder/v2/agent/agentssh"
|
||||
"github.com/coder/coder/v2/agent/agenttest"
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/codersdk/agentsdk"
|
||||
"github.com/coder/coder/v2/pty"
|
||||
@ -1060,84 +1059,6 @@ func TestAgent_SSHConnectionEnvVars(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgent_StartupScript(t *testing.T) {
|
||||
t.Parallel()
|
||||
output := "something"
|
||||
command := "sh -c 'echo " + output + "'"
|
||||
if runtime.GOOS == "windows" {
|
||||
command = "cmd.exe /c echo " + output
|
||||
}
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||
client := agenttest.NewClient(t,
|
||||
logger,
|
||||
uuid.New(),
|
||||
agentsdk.Manifest{
|
||||
StartupScript: command,
|
||||
DERPMap: &tailcfg.DERPMap{},
|
||||
},
|
||||
make(chan *agentsdk.Stats),
|
||||
tailnet.NewCoordinator(logger),
|
||||
)
|
||||
closer := agent.New(agent.Options{
|
||||
Client: client,
|
||||
Filesystem: afero.NewMemMapFs(),
|
||||
Logger: logger.Named("agent"),
|
||||
ReconnectingPTYTimeout: 0,
|
||||
})
|
||||
t.Cleanup(func() {
|
||||
_ = closer.Close()
|
||||
})
|
||||
assert.Eventually(t, func() bool {
|
||||
got := client.GetLifecycleStates()
|
||||
return len(got) > 0 && got[len(got)-1] == codersdk.WorkspaceAgentLifecycleReady
|
||||
}, testutil.WaitShort, testutil.IntervalMedium)
|
||||
|
||||
require.Len(t, client.GetStartupLogs(), 1)
|
||||
require.Equal(t, output, client.GetStartupLogs()[0].Output)
|
||||
})
|
||||
// This ensures that even when coderd sends back that the startup
|
||||
// script has written too many lines it will still succeed!
|
||||
t.Run("OverflowsAndSkips", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||
client := agenttest.NewClient(t,
|
||||
logger,
|
||||
uuid.New(),
|
||||
agentsdk.Manifest{
|
||||
StartupScript: command,
|
||||
DERPMap: &tailcfg.DERPMap{},
|
||||
},
|
||||
make(chan *agentsdk.Stats, 50),
|
||||
tailnet.NewCoordinator(logger),
|
||||
)
|
||||
client.PatchWorkspaceLogs = func() error {
|
||||
resp := httptest.NewRecorder()
|
||||
httpapi.Write(context.Background(), resp, http.StatusRequestEntityTooLarge, codersdk.Response{
|
||||
Message: "Too many lines!",
|
||||
})
|
||||
res := resp.Result()
|
||||
defer res.Body.Close()
|
||||
return codersdk.ReadBodyAsError(res)
|
||||
}
|
||||
closer := agent.New(agent.Options{
|
||||
Client: client,
|
||||
Filesystem: afero.NewMemMapFs(),
|
||||
Logger: logger.Named("agent"),
|
||||
ReconnectingPTYTimeout: 0,
|
||||
})
|
||||
t.Cleanup(func() {
|
||||
_ = closer.Close()
|
||||
})
|
||||
assert.Eventually(t, func() bool {
|
||||
got := client.GetLifecycleStates()
|
||||
return len(got) > 0 && got[len(got)-1] == codersdk.WorkspaceAgentLifecycleReady
|
||||
}, testutil.WaitShort, testutil.IntervalMedium)
|
||||
require.Len(t, client.GetStartupLogs(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAgent_Metadata(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@ -1292,8 +1213,11 @@ func TestAgent_Lifecycle(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, client, _, _, _ := setupAgent(t, agentsdk.Manifest{
|
||||
StartupScript: "sleep 3",
|
||||
StartupScriptTimeout: time.Nanosecond,
|
||||
Scripts: []codersdk.WorkspaceAgentScript{{
|
||||
Script: "sleep 3",
|
||||
Timeout: time.Millisecond,
|
||||
RunOnStart: true,
|
||||
}},
|
||||
}, 0)
|
||||
|
||||
want := []codersdk.WorkspaceAgentLifecycle{
|
||||
@ -1314,8 +1238,11 @@ func TestAgent_Lifecycle(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, client, _, _, _ := setupAgent(t, agentsdk.Manifest{
|
||||
StartupScript: "false",
|
||||
StartupScriptTimeout: 30 * time.Second,
|
||||
Scripts: []codersdk.WorkspaceAgentScript{{
|
||||
Script: "false",
|
||||
Timeout: 30 * time.Second,
|
||||
RunOnStart: true,
|
||||
}},
|
||||
}, 0)
|
||||
|
||||
want := []codersdk.WorkspaceAgentLifecycle{
|
||||
@ -1336,8 +1263,11 @@ func TestAgent_Lifecycle(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, client, _, _, _ := setupAgent(t, agentsdk.Manifest{
|
||||
StartupScript: "true",
|
||||
StartupScriptTimeout: 30 * time.Second,
|
||||
Scripts: []codersdk.WorkspaceAgentScript{{
|
||||
Script: "true",
|
||||
Timeout: 30 * time.Second,
|
||||
RunOnStart: true,
|
||||
}},
|
||||
}, 0)
|
||||
|
||||
want := []codersdk.WorkspaceAgentLifecycle{
|
||||
@ -1358,8 +1288,11 @@ func TestAgent_Lifecycle(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, client, _, _, closer := setupAgent(t, agentsdk.Manifest{
|
||||
ShutdownScript: "sleep 3",
|
||||
StartupScriptTimeout: 30 * time.Second,
|
||||
Scripts: []codersdk.WorkspaceAgentScript{{
|
||||
Script: "sleep 3",
|
||||
Timeout: 30 * time.Second,
|
||||
RunOnStop: true,
|
||||
}},
|
||||
}, 0)
|
||||
|
||||
assert.Eventually(t, func() bool {
|
||||
@ -1396,8 +1329,11 @@ func TestAgent_Lifecycle(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, client, _, _, closer := setupAgent(t, agentsdk.Manifest{
|
||||
ShutdownScript: "sleep 3",
|
||||
ShutdownScriptTimeout: time.Nanosecond,
|
||||
Scripts: []codersdk.WorkspaceAgentScript{{
|
||||
Script: "sleep 3",
|
||||
Timeout: time.Millisecond,
|
||||
RunOnStop: true,
|
||||
}},
|
||||
}, 0)
|
||||
|
||||
assert.Eventually(t, func() bool {
|
||||
@ -1435,8 +1371,11 @@ func TestAgent_Lifecycle(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, client, _, _, closer := setupAgent(t, agentsdk.Manifest{
|
||||
ShutdownScript: "false",
|
||||
ShutdownScriptTimeout: 30 * time.Second,
|
||||
Scripts: []codersdk.WorkspaceAgentScript{{
|
||||
Script: "false",
|
||||
Timeout: 30 * time.Second,
|
||||
RunOnStop: true,
|
||||
}},
|
||||
}, 0)
|
||||
|
||||
assert.Eventually(t, func() bool {
|
||||
@ -1480,9 +1419,16 @@ func TestAgent_Lifecycle(t *testing.T) {
|
||||
logger,
|
||||
uuid.New(),
|
||||
agentsdk.Manifest{
|
||||
DERPMap: derpMap,
|
||||
StartupScript: "echo 1",
|
||||
ShutdownScript: "echo " + expected,
|
||||
DERPMap: derpMap,
|
||||
Scripts: []codersdk.WorkspaceAgentScript{{
|
||||
LogPath: "coder-startup-script.log",
|
||||
Script: "echo 1",
|
||||
RunOnStart: true,
|
||||
}, {
|
||||
LogPath: "coder-shutdown-script.log",
|
||||
Script: "echo " + expected,
|
||||
RunOnStop: true,
|
||||
}},
|
||||
},
|
||||
make(chan *agentsdk.Stats, 50),
|
||||
tailnet.NewCoordinator(logger),
|
||||
@ -1533,9 +1479,7 @@ func TestAgent_Startup(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, client, _, _, _ := setupAgent(t, agentsdk.Manifest{
|
||||
StartupScript: "true",
|
||||
StartupScriptTimeout: 30 * time.Second,
|
||||
Directory: "",
|
||||
Directory: "",
|
||||
}, 0)
|
||||
assert.Eventually(t, func() bool {
|
||||
return client.GetStartup().Version != ""
|
||||
@ -1547,9 +1491,7 @@ func TestAgent_Startup(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, client, _, _, _ := setupAgent(t, agentsdk.Manifest{
|
||||
StartupScript: "true",
|
||||
StartupScriptTimeout: 30 * time.Second,
|
||||
Directory: "~",
|
||||
Directory: "~",
|
||||
}, 0)
|
||||
assert.Eventually(t, func() bool {
|
||||
return client.GetStartup().Version != ""
|
||||
@ -1563,9 +1505,7 @@ func TestAgent_Startup(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, client, _, _, _ := setupAgent(t, agentsdk.Manifest{
|
||||
StartupScript: "true",
|
||||
StartupScriptTimeout: 30 * time.Second,
|
||||
Directory: "coder/coder",
|
||||
Directory: "coder/coder",
|
||||
}, 0)
|
||||
assert.Eventually(t, func() bool {
|
||||
return client.GetStartup().Version != ""
|
||||
@ -1579,9 +1519,7 @@ func TestAgent_Startup(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, client, _, _, _ := setupAgent(t, agentsdk.Manifest{
|
||||
StartupScript: "true",
|
||||
StartupScriptTimeout: 30 * time.Second,
|
||||
Directory: "$HOME",
|
||||
Directory: "$HOME",
|
||||
}, 0)
|
||||
assert.Eventually(t, func() bool {
|
||||
return client.GetStartup().Version != ""
|
||||
|
Reference in New Issue
Block a user