mirror of https://github.com/coder/coder.git
chore: rename startup logs to agent logs (#8649)
* chore: rename startup logs to agent logs This also adds a `source` property to every agent log. It should allow us to group logs and display them nicer in the UI as they stream in. * Fix migration order * Fix naming * Rename the frontend * Fix tests * Fix down migration * Match enums for workspace agent logs * Fix inserting log source * Fix migration order * Fix logs tests * Fix psql insert
This commit is contained in:
parent
78b06397a6
commit
bd944e0d21
|
@ -79,7 +79,7 @@ type Client interface {
|
|||
PostAppHealth(ctx context.Context, req agentsdk.PostAppHealthsRequest) error
|
||||
PostStartup(ctx context.Context, req agentsdk.PostStartupRequest) error
|
||||
PostMetadata(ctx context.Context, key string, req agentsdk.PostMetadataRequest) error
|
||||
PatchStartupLogs(ctx context.Context, req agentsdk.PatchStartupLogs) error
|
||||
PatchLogs(ctx context.Context, req agentsdk.PatchLogs) error
|
||||
GetServiceBanner(ctx context.Context) (codersdk.ServiceBannerConfig, error)
|
||||
}
|
||||
|
||||
|
@ -1006,7 +1006,7 @@ func (a *agent) runScript(ctx context.Context, lifecycle, script string) (err er
|
|||
|
||||
var stdout, stderr io.Writer = fileWriter, fileWriter
|
||||
if lifecycle == "startup" {
|
||||
send, flushAndClose := agentsdk.StartupLogsSender(a.client.PatchStartupLogs, logger)
|
||||
send, flushAndClose := agentsdk.LogsSender(a.client.PatchLogs, logger)
|
||||
// If ctx is canceled here (or in a writer below), we may be
|
||||
// discarding logs, but that's okay because we're shutting down
|
||||
// anyway. We could consider creating a new context here if we
|
||||
|
@ -1017,9 +1017,9 @@ func (a *agent) runScript(ctx context.Context, lifecycle, script string) (err er
|
|||
}
|
||||
}()
|
||||
|
||||
infoW := agentsdk.StartupLogsWriter(ctx, send, codersdk.LogLevelInfo)
|
||||
infoW := agentsdk.StartupLogsWriter(ctx, send, codersdk.WorkspaceAgentLogSourceStartupScript, codersdk.LogLevelInfo)
|
||||
defer infoW.Close()
|
||||
errW := agentsdk.StartupLogsWriter(ctx, send, codersdk.LogLevelError)
|
||||
errW := agentsdk.StartupLogsWriter(ctx, send, codersdk.WorkspaceAgentLogSourceStartupScript, codersdk.LogLevelError)
|
||||
defer errW.Close()
|
||||
|
||||
stdout = io.MultiWriter(fileWriter, infoW)
|
||||
|
|
|
@ -55,7 +55,7 @@ type Client struct {
|
|||
mu sync.Mutex // Protects following.
|
||||
lifecycleStates []codersdk.WorkspaceAgentLifecycle
|
||||
startup agentsdk.PostStartupRequest
|
||||
logs []agentsdk.StartupLog
|
||||
logs []agentsdk.Log
|
||||
derpMapUpdates chan agentsdk.DERPMapUpdate
|
||||
}
|
||||
|
||||
|
@ -161,13 +161,13 @@ func (c *Client) PostStartup(ctx context.Context, startup agentsdk.PostStartupRe
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) GetStartupLogs() []agentsdk.StartupLog {
|
||||
func (c *Client) GetStartupLogs() []agentsdk.Log {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
return c.logs
|
||||
}
|
||||
|
||||
func (c *Client) PatchStartupLogs(ctx context.Context, logs agentsdk.PatchStartupLogs) error {
|
||||
func (c *Client) PatchLogs(ctx context.Context, logs agentsdk.PatchLogs) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if c.PatchWorkspaceLogs != nil {
|
||||
|
|
|
@ -16,7 +16,7 @@ var errAgentShuttingDown = xerrors.New("agent is shutting down")
|
|||
type AgentOptions struct {
|
||||
FetchInterval time.Duration
|
||||
Fetch func(ctx context.Context, agentID uuid.UUID) (codersdk.WorkspaceAgent, error)
|
||||
FetchLogs func(ctx context.Context, agentID uuid.UUID, after int64, follow bool) (<-chan []codersdk.WorkspaceAgentStartupLog, io.Closer, error)
|
||||
FetchLogs func(ctx context.Context, agentID uuid.UUID, after int64, follow bool) (<-chan []codersdk.WorkspaceAgentLog, io.Closer, error)
|
||||
Wait bool // If true, wait for the agent to be ready (startup script).
|
||||
}
|
||||
|
||||
|
@ -29,8 +29,8 @@ func Agent(ctx context.Context, writer io.Writer, agentID uuid.UUID, opts AgentO
|
|||
opts.FetchInterval = 500 * time.Millisecond
|
||||
}
|
||||
if opts.FetchLogs == nil {
|
||||
opts.FetchLogs = func(_ context.Context, _ uuid.UUID, _ int64, _ bool) (<-chan []codersdk.WorkspaceAgentStartupLog, io.Closer, error) {
|
||||
c := make(chan []codersdk.WorkspaceAgentStartupLog)
|
||||
opts.FetchLogs = func(_ context.Context, _ uuid.UUID, _ int64, _ bool) (<-chan []codersdk.WorkspaceAgentLog, io.Closer, error) {
|
||||
c := make(chan []codersdk.WorkspaceAgentLog)
|
||||
close(c)
|
||||
return c, closeFunc(func() error { return nil }), nil
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ func Agent(ctx context.Context, writer io.Writer, agentID uuid.UUID, opts AgentO
|
|||
}
|
||||
defer logsCloser.Close()
|
||||
|
||||
var lastLog codersdk.WorkspaceAgentStartupLog
|
||||
var lastLog codersdk.WorkspaceAgentLog
|
||||
fetchedAgentWhileFollowing := fetchedAgent
|
||||
if !follow {
|
||||
fetchedAgentWhileFollowing = nil
|
||||
|
|
|
@ -27,8 +27,8 @@ func TestAgent(t *testing.T) {
|
|||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
iter []func(context.Context, *codersdk.WorkspaceAgent, chan []codersdk.WorkspaceAgentStartupLog) error
|
||||
logs chan []codersdk.WorkspaceAgentStartupLog
|
||||
iter []func(context.Context, *codersdk.WorkspaceAgent, chan []codersdk.WorkspaceAgentLog) error
|
||||
logs chan []codersdk.WorkspaceAgentLog
|
||||
opts cliui.AgentOptions
|
||||
want []string
|
||||
wantErr bool
|
||||
|
@ -38,12 +38,12 @@ func TestAgent(t *testing.T) {
|
|||
opts: cliui.AgentOptions{
|
||||
FetchInterval: time.Millisecond,
|
||||
},
|
||||
iter: []func(context.Context, *codersdk.WorkspaceAgent, chan []codersdk.WorkspaceAgentStartupLog) error{
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, _ chan []codersdk.WorkspaceAgentStartupLog) error {
|
||||
iter: []func(context.Context, *codersdk.WorkspaceAgent, chan []codersdk.WorkspaceAgentLog) error{
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, _ chan []codersdk.WorkspaceAgentLog) error {
|
||||
agent.Status = codersdk.WorkspaceAgentConnecting
|
||||
return nil
|
||||
},
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, logs chan []codersdk.WorkspaceAgentStartupLog) error {
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, logs chan []codersdk.WorkspaceAgentLog) error {
|
||||
agent.Status = codersdk.WorkspaceAgentConnected
|
||||
agent.FirstConnectedAt = ptr.Ref(time.Now())
|
||||
return nil
|
||||
|
@ -62,18 +62,18 @@ func TestAgent(t *testing.T) {
|
|||
opts: cliui.AgentOptions{
|
||||
FetchInterval: 1 * time.Millisecond,
|
||||
},
|
||||
iter: []func(context.Context, *codersdk.WorkspaceAgent, chan []codersdk.WorkspaceAgentStartupLog) error{
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, _ chan []codersdk.WorkspaceAgentStartupLog) error {
|
||||
iter: []func(context.Context, *codersdk.WorkspaceAgent, chan []codersdk.WorkspaceAgentLog) error{
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, _ chan []codersdk.WorkspaceAgentLog) error {
|
||||
agent.Status = codersdk.WorkspaceAgentConnecting
|
||||
agent.LifecycleState = codersdk.WorkspaceAgentLifecycleStarting
|
||||
agent.StartedAt = ptr.Ref(time.Now())
|
||||
return nil
|
||||
},
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, _ chan []codersdk.WorkspaceAgentStartupLog) error {
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, _ chan []codersdk.WorkspaceAgentLog) error {
|
||||
agent.Status = codersdk.WorkspaceAgentTimeout
|
||||
return nil
|
||||
},
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, logs chan []codersdk.WorkspaceAgentStartupLog) error {
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, logs chan []codersdk.WorkspaceAgentLog) error {
|
||||
agent.Status = codersdk.WorkspaceAgentConnected
|
||||
agent.FirstConnectedAt = ptr.Ref(time.Now())
|
||||
agent.LifecycleState = codersdk.WorkspaceAgentLifecycleReady
|
||||
|
@ -95,8 +95,8 @@ func TestAgent(t *testing.T) {
|
|||
opts: cliui.AgentOptions{
|
||||
FetchInterval: 1 * time.Millisecond,
|
||||
},
|
||||
iter: []func(context.Context, *codersdk.WorkspaceAgent, chan []codersdk.WorkspaceAgentStartupLog) error{
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, _ chan []codersdk.WorkspaceAgentStartupLog) error {
|
||||
iter: []func(context.Context, *codersdk.WorkspaceAgent, chan []codersdk.WorkspaceAgentLog) error{
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, _ chan []codersdk.WorkspaceAgentLog) error {
|
||||
agent.Status = codersdk.WorkspaceAgentDisconnected
|
||||
agent.FirstConnectedAt = ptr.Ref(time.Now().Add(-1 * time.Minute))
|
||||
agent.LastConnectedAt = ptr.Ref(time.Now().Add(-1 * time.Minute))
|
||||
|
@ -106,7 +106,7 @@ func TestAgent(t *testing.T) {
|
|||
agent.ReadyAt = ptr.Ref(time.Now())
|
||||
return nil
|
||||
},
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, _ chan []codersdk.WorkspaceAgentStartupLog) error {
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, _ chan []codersdk.WorkspaceAgentLog) error {
|
||||
agent.Status = codersdk.WorkspaceAgentConnected
|
||||
agent.LastConnectedAt = ptr.Ref(time.Now())
|
||||
return nil
|
||||
|
@ -125,13 +125,13 @@ func TestAgent(t *testing.T) {
|
|||
FetchInterval: time.Millisecond,
|
||||
Wait: true,
|
||||
},
|
||||
iter: []func(context.Context, *codersdk.WorkspaceAgent, chan []codersdk.WorkspaceAgentStartupLog) error{
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, logs chan []codersdk.WorkspaceAgentStartupLog) error {
|
||||
iter: []func(context.Context, *codersdk.WorkspaceAgent, chan []codersdk.WorkspaceAgentLog) error{
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, logs chan []codersdk.WorkspaceAgentLog) error {
|
||||
agent.Status = codersdk.WorkspaceAgentConnected
|
||||
agent.FirstConnectedAt = ptr.Ref(time.Now())
|
||||
agent.LifecycleState = codersdk.WorkspaceAgentLifecycleStarting
|
||||
agent.StartedAt = ptr.Ref(time.Now())
|
||||
logs <- []codersdk.WorkspaceAgentStartupLog{
|
||||
logs <- []codersdk.WorkspaceAgentLog{
|
||||
{
|
||||
CreatedAt: time.Now(),
|
||||
Output: "Hello world",
|
||||
|
@ -139,10 +139,10 @@ func TestAgent(t *testing.T) {
|
|||
}
|
||||
return nil
|
||||
},
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, logs chan []codersdk.WorkspaceAgentStartupLog) error {
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, logs chan []codersdk.WorkspaceAgentLog) error {
|
||||
agent.LifecycleState = codersdk.WorkspaceAgentLifecycleReady
|
||||
agent.ReadyAt = ptr.Ref(time.Now())
|
||||
logs <- []codersdk.WorkspaceAgentStartupLog{
|
||||
logs <- []codersdk.WorkspaceAgentLog{
|
||||
{
|
||||
CreatedAt: time.Now(),
|
||||
Output: "Bye now",
|
||||
|
@ -164,14 +164,14 @@ func TestAgent(t *testing.T) {
|
|||
FetchInterval: time.Millisecond,
|
||||
Wait: true,
|
||||
},
|
||||
iter: []func(context.Context, *codersdk.WorkspaceAgent, chan []codersdk.WorkspaceAgentStartupLog) error{
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, logs chan []codersdk.WorkspaceAgentStartupLog) error {
|
||||
iter: []func(context.Context, *codersdk.WorkspaceAgent, chan []codersdk.WorkspaceAgentLog) error{
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, logs chan []codersdk.WorkspaceAgentLog) error {
|
||||
agent.Status = codersdk.WorkspaceAgentConnected
|
||||
agent.FirstConnectedAt = ptr.Ref(time.Now())
|
||||
agent.StartedAt = ptr.Ref(time.Now())
|
||||
agent.LifecycleState = codersdk.WorkspaceAgentLifecycleStartError
|
||||
agent.ReadyAt = ptr.Ref(time.Now())
|
||||
logs <- []codersdk.WorkspaceAgentStartupLog{
|
||||
logs <- []codersdk.WorkspaceAgentLog{
|
||||
{
|
||||
CreatedAt: time.Now(),
|
||||
Output: "Hello world",
|
||||
|
@ -193,8 +193,8 @@ func TestAgent(t *testing.T) {
|
|||
opts: cliui.AgentOptions{
|
||||
FetchInterval: time.Millisecond,
|
||||
},
|
||||
iter: []func(context.Context, *codersdk.WorkspaceAgent, chan []codersdk.WorkspaceAgentStartupLog) error{
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, logs chan []codersdk.WorkspaceAgentStartupLog) error {
|
||||
iter: []func(context.Context, *codersdk.WorkspaceAgent, chan []codersdk.WorkspaceAgentLog) error{
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, logs chan []codersdk.WorkspaceAgentLog) error {
|
||||
agent.Status = codersdk.WorkspaceAgentDisconnected
|
||||
agent.LifecycleState = codersdk.WorkspaceAgentLifecycleOff
|
||||
return nil
|
||||
|
@ -208,13 +208,13 @@ func TestAgent(t *testing.T) {
|
|||
FetchInterval: time.Millisecond,
|
||||
Wait: true,
|
||||
},
|
||||
iter: []func(context.Context, *codersdk.WorkspaceAgent, chan []codersdk.WorkspaceAgentStartupLog) error{
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, logs chan []codersdk.WorkspaceAgentStartupLog) error {
|
||||
iter: []func(context.Context, *codersdk.WorkspaceAgent, chan []codersdk.WorkspaceAgentLog) error{
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, logs chan []codersdk.WorkspaceAgentLog) error {
|
||||
agent.Status = codersdk.WorkspaceAgentConnected
|
||||
agent.FirstConnectedAt = ptr.Ref(time.Now())
|
||||
agent.LifecycleState = codersdk.WorkspaceAgentLifecycleStarting
|
||||
agent.StartedAt = ptr.Ref(time.Now())
|
||||
logs <- []codersdk.WorkspaceAgentStartupLog{
|
||||
logs <- []codersdk.WorkspaceAgentLog{
|
||||
{
|
||||
CreatedAt: time.Now(),
|
||||
Output: "Hello world",
|
||||
|
@ -222,7 +222,7 @@ func TestAgent(t *testing.T) {
|
|||
}
|
||||
return nil
|
||||
},
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, logs chan []codersdk.WorkspaceAgentStartupLog) error {
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, logs chan []codersdk.WorkspaceAgentLog) error {
|
||||
agent.ReadyAt = ptr.Ref(time.Now())
|
||||
agent.LifecycleState = codersdk.WorkspaceAgentLifecycleShuttingDown
|
||||
return nil
|
||||
|
@ -241,12 +241,12 @@ func TestAgent(t *testing.T) {
|
|||
FetchInterval: time.Millisecond,
|
||||
Wait: true,
|
||||
},
|
||||
iter: []func(context.Context, *codersdk.WorkspaceAgent, chan []codersdk.WorkspaceAgentStartupLog) error{
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, _ chan []codersdk.WorkspaceAgentStartupLog) error {
|
||||
iter: []func(context.Context, *codersdk.WorkspaceAgent, chan []codersdk.WorkspaceAgentLog) error{
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, _ chan []codersdk.WorkspaceAgentLog) error {
|
||||
agent.Status = codersdk.WorkspaceAgentConnecting
|
||||
return nil
|
||||
},
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, _ chan []codersdk.WorkspaceAgentStartupLog) error {
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, _ chan []codersdk.WorkspaceAgentLog) error {
|
||||
return xerrors.New("bad")
|
||||
},
|
||||
},
|
||||
|
@ -261,13 +261,13 @@ func TestAgent(t *testing.T) {
|
|||
FetchInterval: time.Millisecond,
|
||||
Wait: true,
|
||||
},
|
||||
iter: []func(context.Context, *codersdk.WorkspaceAgent, chan []codersdk.WorkspaceAgentStartupLog) error{
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, _ chan []codersdk.WorkspaceAgentStartupLog) error {
|
||||
iter: []func(context.Context, *codersdk.WorkspaceAgent, chan []codersdk.WorkspaceAgentLog) error{
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, _ chan []codersdk.WorkspaceAgentLog) error {
|
||||
agent.Status = codersdk.WorkspaceAgentTimeout
|
||||
agent.TroubleshootingURL = "https://troubleshoot"
|
||||
return nil
|
||||
},
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, _ chan []codersdk.WorkspaceAgentStartupLog) error {
|
||||
func(_ context.Context, agent *codersdk.WorkspaceAgent, _ chan []codersdk.WorkspaceAgentLog) error {
|
||||
return xerrors.New("bad")
|
||||
},
|
||||
},
|
||||
|
@ -294,7 +294,7 @@ func TestAgent(t *testing.T) {
|
|||
CreatedAt: time.Now(),
|
||||
LifecycleState: codersdk.WorkspaceAgentLifecycleCreated,
|
||||
}
|
||||
logs := make(chan []codersdk.WorkspaceAgentStartupLog, 1)
|
||||
logs := make(chan []codersdk.WorkspaceAgentLog, 1)
|
||||
|
||||
cmd := &clibase.Cmd{
|
||||
Handler: func(inv *clibase.Invocation) error {
|
||||
|
@ -306,12 +306,12 @@ func TestAgent(t *testing.T) {
|
|||
}
|
||||
return agent, err
|
||||
}
|
||||
tc.opts.FetchLogs = func(ctx context.Context, _ uuid.UUID, _ int64, follow bool) (<-chan []codersdk.WorkspaceAgentStartupLog, io.Closer, error) {
|
||||
tc.opts.FetchLogs = func(ctx context.Context, _ uuid.UUID, _ int64, follow bool) (<-chan []codersdk.WorkspaceAgentLog, io.Closer, error) {
|
||||
if follow {
|
||||
return logs, closeFunc(func() error { return nil }), nil
|
||||
}
|
||||
|
||||
fetchLogs := make(chan []codersdk.WorkspaceAgentStartupLog, 1)
|
||||
fetchLogs := make(chan []codersdk.WorkspaceAgentLog, 1)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, nil, ctx.Err()
|
||||
|
|
|
@ -186,7 +186,7 @@ func (r *RootCmd) ssh() *clibase.Cmd {
|
|||
// This is required in "stdio" mode so a connecting indicator can be displayed.
|
||||
err = cliui.Agent(ctx, inv.Stderr, workspaceAgent.ID, cliui.AgentOptions{
|
||||
Fetch: client.WorkspaceAgent,
|
||||
FetchLogs: client.WorkspaceAgentStartupLogsAfter,
|
||||
FetchLogs: client.WorkspaceAgentLogsAfter,
|
||||
Wait: wait,
|
||||
})
|
||||
if err != nil {
|
||||
|
|
|
@ -167,7 +167,7 @@ func main() {
|
|||
Use: "agent",
|
||||
Handler: func(inv *clibase.Invocation) error {
|
||||
var agent codersdk.WorkspaceAgent
|
||||
var logs []codersdk.WorkspaceAgentStartupLog
|
||||
var logs []codersdk.WorkspaceAgentLog
|
||||
|
||||
fetchSteps := []func(){
|
||||
func() {
|
||||
|
@ -191,7 +191,7 @@ func main() {
|
|||
if rand.Float64() > 0.75 { //nolint:gosec
|
||||
level = codersdk.LogLevelError
|
||||
}
|
||||
logs = append(logs, codersdk.WorkspaceAgentStartupLog{
|
||||
logs = append(logs, codersdk.WorkspaceAgentLog{
|
||||
CreatedAt: time.Now().Add(-time.Duration(10-i) * 144 * time.Millisecond),
|
||||
Output: fmt.Sprintf("Some log %d", i),
|
||||
Level: level,
|
||||
|
@ -226,13 +226,13 @@ func main() {
|
|||
step()
|
||||
return agent, nil
|
||||
},
|
||||
FetchLogs: func(_ context.Context, _ uuid.UUID, _ int64, follow bool) (<-chan []codersdk.WorkspaceAgentStartupLog, io.Closer, error) {
|
||||
logsC := make(chan []codersdk.WorkspaceAgentStartupLog, len(logs))
|
||||
FetchLogs: func(_ context.Context, _ uuid.UUID, _ int64, follow bool) (<-chan []codersdk.WorkspaceAgentLog, io.Closer, error) {
|
||||
logsC := make(chan []codersdk.WorkspaceAgentLog, len(logs))
|
||||
if follow {
|
||||
go func() {
|
||||
defer close(logsC)
|
||||
for _, log := range logs {
|
||||
logsC <- []codersdk.WorkspaceAgentStartupLog{log}
|
||||
logsC <- []codersdk.WorkspaceAgentLog{log}
|
||||
time.Sleep(144 * time.Millisecond)
|
||||
}
|
||||
agent.LifecycleState = codersdk.WorkspaceAgentLifecycleReady
|
||||
|
|
|
@ -4565,6 +4565,45 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/logs": {
|
||||
"patch": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Agents"
|
||||
],
|
||||
"summary": "Patch workspace agent logs",
|
||||
"operationId": "patch-workspace-agent-logs",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "logs",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.PatchLogs"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/manifest": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
@ -4764,16 +4803,16 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Agents"
|
||||
],
|
||||
"summary": "Patch workspace agent startup logs",
|
||||
"operationId": "patch-workspace-agent-startup-logs",
|
||||
"summary": "Removed: Patch workspace agent logs",
|
||||
"operationId": "removed-patch-workspace-agent-logs",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Startup logs",
|
||||
"description": "logs",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.PatchStartupLogs"
|
||||
"$ref": "#/definitions/agentsdk.PatchLogs"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -4959,6 +4998,68 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/{workspaceagent}/logs": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Agents"
|
||||
],
|
||||
"summary": "Get logs by workspace agent",
|
||||
"operationId": "get-logs-by-workspace-agent",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Workspace agent ID",
|
||||
"name": "workspaceagent",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Before log id",
|
||||
"name": "before",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "After log id",
|
||||
"name": "after",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"description": "Follow log stream",
|
||||
"name": "follow",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"description": "Disable compression for WebSocket connection",
|
||||
"name": "no_compression",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentLog"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/{workspaceagent}/pty": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
@ -5001,8 +5102,8 @@ const docTemplate = `{
|
|||
"tags": [
|
||||
"Agents"
|
||||
],
|
||||
"summary": "Get startup logs by workspace agent",
|
||||
"operationId": "get-startup-logs-by-workspace-agent",
|
||||
"summary": "Removed: Get logs by workspace agent",
|
||||
"operationId": "removed-get-logs-by-workspace-agent",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -5043,7 +5144,7 @@ const docTemplate = `{
|
|||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentStartupLog"
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentLog"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6213,6 +6314,23 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.Log": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"level": {
|
||||
"$ref": "#/definitions/codersdk.LogLevel"
|
||||
},
|
||||
"output": {
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentLogSource"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.Manifest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -6270,13 +6388,13 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PatchStartupLogs": {
|
||||
"agentsdk.PatchLogs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"logs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/agentsdk.StartupLog"
|
||||
"$ref": "#/definitions/agentsdk.Log"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6337,20 +6455,6 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.StartupLog": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"level": {
|
||||
"$ref": "#/definitions/codersdk.LogLevel"
|
||||
},
|
||||
"output": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.Stats": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -10325,6 +10429,12 @@ const docTemplate = `{
|
|||
"description": "Deprecated: Use StartupScriptBehavior instead.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"logs_length": {
|
||||
"type": "integer"
|
||||
},
|
||||
"logs_overflowed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -10349,12 +10459,6 @@ const docTemplate = `{
|
|||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"startup_logs_length": {
|
||||
"type": "integer"
|
||||
},
|
||||
"startup_logs_overflowed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"startup_script": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -10462,6 +10566,43 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceAgentLog": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"level": {
|
||||
"$ref": "#/definitions/codersdk.LogLevel"
|
||||
},
|
||||
"output": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceAgentLogSource": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"startup_script",
|
||||
"shutdown_script",
|
||||
"kubernetes",
|
||||
"envbox",
|
||||
"envbuilder",
|
||||
"external"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"WorkspaceAgentLogSourceStartupScript",
|
||||
"WorkspaceAgentLogSourceShutdownScript",
|
||||
"WorkspaceAgentLogSourceKubernetes",
|
||||
"WorkspaceAgentLogSourceEnvbox",
|
||||
"WorkspaceAgentLogSourceEnvbuilder",
|
||||
"WorkspaceAgentLogSourceExternal"
|
||||
]
|
||||
},
|
||||
"codersdk.WorkspaceAgentMetadataDescription": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -10482,24 +10623,6 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceAgentStartupLog": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"level": {
|
||||
"$ref": "#/definitions/codersdk.LogLevel"
|
||||
},
|
||||
"output": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceAgentStartupScriptBehavior": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
|
|
@ -4017,6 +4017,39 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/logs": {
|
||||
"patch": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": ["application/json"],
|
||||
"produces": ["application/json"],
|
||||
"tags": ["Agents"],
|
||||
"summary": "Patch workspace agent logs",
|
||||
"operationId": "patch-workspace-agent-logs",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "logs",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.PatchLogs"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/manifest": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
@ -4186,16 +4219,16 @@
|
|||
"consumes": ["application/json"],
|
||||
"produces": ["application/json"],
|
||||
"tags": ["Agents"],
|
||||
"summary": "Patch workspace agent startup logs",
|
||||
"operationId": "patch-workspace-agent-startup-logs",
|
||||
"summary": "Removed: Patch workspace agent logs",
|
||||
"operationId": "removed-patch-workspace-agent-logs",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Startup logs",
|
||||
"description": "logs",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.PatchStartupLogs"
|
||||
"$ref": "#/definitions/agentsdk.PatchLogs"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -4363,34 +4396,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/{workspaceagent}/pty": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"tags": ["Agents"],
|
||||
"summary": "Open PTY to workspace agent",
|
||||
"operationId": "open-pty-to-workspace-agent",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Workspace agent ID",
|
||||
"name": "workspaceagent",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"101": {
|
||||
"description": "Switching Protocols"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/{workspaceagent}/startup-logs": {
|
||||
"/workspaceagents/{workspaceagent}/logs": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
|
@ -4399,8 +4405,8 @@
|
|||
],
|
||||
"produces": ["application/json"],
|
||||
"tags": ["Agents"],
|
||||
"summary": "Get startup logs by workspace agent",
|
||||
"operationId": "get-startup-logs-by-workspace-agent",
|
||||
"summary": "Get logs by workspace agent",
|
||||
"operationId": "get-logs-by-workspace-agent",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
|
@ -4441,7 +4447,92 @@
|
|||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentStartupLog"
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentLog"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/{workspaceagent}/pty": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"tags": ["Agents"],
|
||||
"summary": "Open PTY to workspace agent",
|
||||
"operationId": "open-pty-to-workspace-agent",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Workspace agent ID",
|
||||
"name": "workspaceagent",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"101": {
|
||||
"description": "Switching Protocols"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/{workspaceagent}/startup-logs": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"produces": ["application/json"],
|
||||
"tags": ["Agents"],
|
||||
"summary": "Removed: Get logs by workspace agent",
|
||||
"operationId": "removed-get-logs-by-workspace-agent",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Workspace agent ID",
|
||||
"name": "workspaceagent",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Before log id",
|
||||
"name": "before",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "After log id",
|
||||
"name": "after",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"description": "Follow log stream",
|
||||
"name": "follow",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"description": "Disable compression for WebSocket connection",
|
||||
"name": "no_compression",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentLog"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5473,6 +5564,23 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.Log": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"level": {
|
||||
"$ref": "#/definitions/codersdk.LogLevel"
|
||||
},
|
||||
"output": {
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentLogSource"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.Manifest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -5530,13 +5638,13 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PatchStartupLogs": {
|
||||
"agentsdk.PatchLogs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"logs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/agentsdk.StartupLog"
|
||||
"$ref": "#/definitions/agentsdk.Log"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5597,20 +5705,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.StartupLog": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"level": {
|
||||
"$ref": "#/definitions/codersdk.LogLevel"
|
||||
},
|
||||
"output": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.Stats": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -9354,6 +9448,12 @@
|
|||
"description": "Deprecated: Use StartupScriptBehavior instead.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"logs_length": {
|
||||
"type": "integer"
|
||||
},
|
||||
"logs_overflowed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -9378,12 +9478,6 @@
|
|||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"startup_logs_length": {
|
||||
"type": "integer"
|
||||
},
|
||||
"startup_logs_overflowed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"startup_script": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -9491,6 +9585,43 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceAgentLog": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"level": {
|
||||
"$ref": "#/definitions/codersdk.LogLevel"
|
||||
},
|
||||
"output": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceAgentLogSource": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"startup_script",
|
||||
"shutdown_script",
|
||||
"kubernetes",
|
||||
"envbox",
|
||||
"envbuilder",
|
||||
"external"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"WorkspaceAgentLogSourceStartupScript",
|
||||
"WorkspaceAgentLogSourceShutdownScript",
|
||||
"WorkspaceAgentLogSourceKubernetes",
|
||||
"WorkspaceAgentLogSourceEnvbox",
|
||||
"WorkspaceAgentLogSourceEnvbuilder",
|
||||
"WorkspaceAgentLogSourceExternal"
|
||||
]
|
||||
},
|
||||
"codersdk.WorkspaceAgentMetadataDescription": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -9511,24 +9642,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceAgentStartupLog": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"level": {
|
||||
"$ref": "#/definitions/codersdk.LogLevel"
|
||||
},
|
||||
"output": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceAgentStartupScriptBehavior": {
|
||||
"type": "string",
|
||||
"enum": ["blocking", "non-blocking"],
|
||||
|
|
|
@ -776,7 +776,8 @@ func New(options *Options) *API {
|
|||
// New agents will use /me/manifest instead.
|
||||
r.Get("/metadata", api.workspaceAgentManifest)
|
||||
r.Post("/startup", api.postWorkspaceAgentStartup)
|
||||
r.Patch("/startup-logs", api.patchWorkspaceAgentStartupLogs)
|
||||
r.Patch("/startup-logs", api.patchWorkspaceAgentLogsDeprecated)
|
||||
r.Patch("/logs", api.patchWorkspaceAgentLogs)
|
||||
r.Post("/app-health", api.postWorkspaceAppHealth)
|
||||
r.Get("/gitauth", api.workspaceAgentsGitAuth)
|
||||
r.Get("/gitsshkey", api.agentGitSSHKey)
|
||||
|
@ -800,7 +801,8 @@ func New(options *Options) *API {
|
|||
)
|
||||
r.Get("/", api.workspaceAgent)
|
||||
r.Get("/watch-metadata", api.watchWorkspaceAgentMetadata)
|
||||
r.Get("/startup-logs", api.workspaceAgentStartupLogs)
|
||||
r.Get("/startup-logs", api.workspaceAgentLogsDeprecated)
|
||||
r.Get("/logs", api.workspaceAgentLogs)
|
||||
r.Get("/listening-ports", api.workspaceAgentListeningPorts)
|
||||
r.Get("/connection", api.workspaceAgentConnection)
|
||||
r.Get("/coordinate", api.workspaceAgentClientCoordinate)
|
||||
|
|
|
@ -722,11 +722,11 @@ func (q *querier) DeleteLicense(ctx context.Context, id int32) (int32, error) {
|
|||
return id, nil
|
||||
}
|
||||
|
||||
func (q *querier) DeleteOldWorkspaceAgentStartupLogs(ctx context.Context) error {
|
||||
func (q *querier) DeleteOldWorkspaceAgentLogs(ctx context.Context) error {
|
||||
if err := q.authorizeContext(ctx, rbac.ActionDelete, rbac.ResourceSystem); err != nil {
|
||||
return err
|
||||
}
|
||||
return q.db.DeleteOldWorkspaceAgentStartupLogs(ctx)
|
||||
return q.db.DeleteOldWorkspaceAgentLogs(ctx)
|
||||
}
|
||||
|
||||
func (q *querier) DeleteOldWorkspaceAgentStats(ctx context.Context) error {
|
||||
|
@ -1444,6 +1444,14 @@ func (q *querier) GetWorkspaceAgentLifecycleStateByID(ctx context.Context, id uu
|
|||
return q.db.GetWorkspaceAgentLifecycleStateByID(ctx, id)
|
||||
}
|
||||
|
||||
func (q *querier) GetWorkspaceAgentLogsAfter(ctx context.Context, arg database.GetWorkspaceAgentLogsAfterParams) ([]database.WorkspaceAgentLog, error) {
|
||||
_, err := q.GetWorkspaceAgentByID(ctx, arg.AgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return q.db.GetWorkspaceAgentLogsAfter(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) GetWorkspaceAgentMetadata(ctx context.Context, workspaceAgentID uuid.UUID) ([]database.WorkspaceAgentMetadatum, error) {
|
||||
workspace, err := q.db.GetWorkspaceByAgentID(ctx, workspaceAgentID)
|
||||
if err != nil {
|
||||
|
@ -1458,14 +1466,6 @@ func (q *querier) GetWorkspaceAgentMetadata(ctx context.Context, workspaceAgentI
|
|||
return q.db.GetWorkspaceAgentMetadata(ctx, workspaceAgentID)
|
||||
}
|
||||
|
||||
func (q *querier) GetWorkspaceAgentStartupLogsAfter(ctx context.Context, arg database.GetWorkspaceAgentStartupLogsAfterParams) ([]database.WorkspaceAgentStartupLog, error) {
|
||||
_, err := q.GetWorkspaceAgentByID(ctx, arg.AgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return q.db.GetWorkspaceAgentStartupLogsAfter(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) GetWorkspaceAgentStats(ctx context.Context, createdAfter time.Time) ([]database.GetWorkspaceAgentStatsRow, error) {
|
||||
return q.db.GetWorkspaceAgentStats(ctx, createdAfter)
|
||||
}
|
||||
|
@ -1926,6 +1926,10 @@ func (q *querier) InsertWorkspaceAgent(ctx context.Context, arg database.InsertW
|
|||
return q.db.InsertWorkspaceAgent(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) InsertWorkspaceAgentLogs(ctx context.Context, arg database.InsertWorkspaceAgentLogsParams) ([]database.WorkspaceAgentLog, error) {
|
||||
return q.db.InsertWorkspaceAgentLogs(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) InsertWorkspaceAgentMetadata(ctx context.Context, arg database.InsertWorkspaceAgentMetadataParams) error {
|
||||
// We don't check for workspace ownership here since the agent metadata may
|
||||
// be associated with an orphaned agent used by a dry run build.
|
||||
|
@ -1936,10 +1940,6 @@ func (q *querier) InsertWorkspaceAgentMetadata(ctx context.Context, arg database
|
|||
return q.db.InsertWorkspaceAgentMetadata(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) InsertWorkspaceAgentStartupLogs(ctx context.Context, arg database.InsertWorkspaceAgentStartupLogsParams) ([]database.WorkspaceAgentStartupLog, error) {
|
||||
return q.db.InsertWorkspaceAgentStartupLogs(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) InsertWorkspaceAgentStat(ctx context.Context, arg database.InsertWorkspaceAgentStatParams) (database.WorkspaceAgentStat, error) {
|
||||
// TODO: This is a workspace agent operation. Should users be able to query this?
|
||||
// Not really sure what this is for.
|
||||
|
@ -2404,6 +2404,24 @@ func (q *querier) UpdateWorkspaceAgentLifecycleStateByID(ctx context.Context, ar
|
|||
return q.db.UpdateWorkspaceAgentLifecycleStateByID(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) UpdateWorkspaceAgentLogOverflowByID(ctx context.Context, arg database.UpdateWorkspaceAgentLogOverflowByIDParams) error {
|
||||
agent, err := q.db.GetWorkspaceAgentByID(ctx, arg.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
workspace, err := q.db.GetWorkspaceByAgentID(ctx, agent.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := q.authorizeContext(ctx, rbac.ActionUpdate, workspace); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return q.db.UpdateWorkspaceAgentLogOverflowByID(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) UpdateWorkspaceAgentMetadata(ctx context.Context, arg database.UpdateWorkspaceAgentMetadataParams) error {
|
||||
workspace, err := q.db.GetWorkspaceByAgentID(ctx, arg.WorkspaceAgentID)
|
||||
if err != nil {
|
||||
|
@ -2436,24 +2454,6 @@ func (q *querier) UpdateWorkspaceAgentStartupByID(ctx context.Context, arg datab
|
|||
return q.db.UpdateWorkspaceAgentStartupByID(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) UpdateWorkspaceAgentStartupLogOverflowByID(ctx context.Context, arg database.UpdateWorkspaceAgentStartupLogOverflowByIDParams) error {
|
||||
agent, err := q.db.GetWorkspaceAgentByID(ctx, arg.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
workspace, err := q.db.GetWorkspaceByAgentID(ctx, agent.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := q.authorizeContext(ctx, rbac.ActionUpdate, workspace); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return q.db.UpdateWorkspaceAgentStartupLogOverflowByID(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) UpdateWorkspaceAppHealthByID(ctx context.Context, arg database.UpdateWorkspaceAppHealthByIDParams) error {
|
||||
// TODO: This is a workspace agent operation. Should users be able to query this?
|
||||
workspace, err := q.db.GetWorkspaceByWorkspaceAppID(ctx, arg.ID)
|
||||
|
|
|
@ -1053,14 +1053,14 @@ func (s *MethodTestSuite) TestWorkspace() {
|
|||
LifecycleState: database.WorkspaceAgentLifecycleStateCreated,
|
||||
}).Asserts(ws, rbac.ActionUpdate).Returns()
|
||||
}))
|
||||
s.Run("UpdateWorkspaceAgentStartupLogOverflowByID", s.Subtest(func(db database.Store, check *expects) {
|
||||
s.Run("UpdateWorkspaceAgentLogOverflowByID", s.Subtest(func(db database.Store, check *expects) {
|
||||
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
|
||||
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
|
||||
res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID})
|
||||
agt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: res.ID})
|
||||
check.Args(database.UpdateWorkspaceAgentStartupLogOverflowByIDParams{
|
||||
ID: agt.ID,
|
||||
StartupLogsOverflowed: true,
|
||||
check.Args(database.UpdateWorkspaceAgentLogOverflowByIDParams{
|
||||
ID: agt.ID,
|
||||
LogsOverflowed: true,
|
||||
}).Asserts(ws, rbac.ActionUpdate).Returns()
|
||||
}))
|
||||
s.Run("UpdateWorkspaceAgentStartupByID", s.Subtest(func(db database.Store, check *expects) {
|
||||
|
@ -1073,14 +1073,14 @@ func (s *MethodTestSuite) TestWorkspace() {
|
|||
Subsystem: database.WorkspaceAgentSubsystemNone,
|
||||
}).Asserts(ws, rbac.ActionUpdate).Returns()
|
||||
}))
|
||||
s.Run("GetWorkspaceAgentStartupLogsAfter", s.Subtest(func(db database.Store, check *expects) {
|
||||
s.Run("GetWorkspaceAgentLogsAfter", s.Subtest(func(db database.Store, check *expects) {
|
||||
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
|
||||
build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()})
|
||||
res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID})
|
||||
agt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: res.ID})
|
||||
check.Args(database.GetWorkspaceAgentStartupLogsAfterParams{
|
||||
check.Args(database.GetWorkspaceAgentLogsAfterParams{
|
||||
AgentID: agt.ID,
|
||||
}).Asserts(ws, rbac.ActionRead).Returns([]database.WorkspaceAgentStartupLog{})
|
||||
}).Asserts(ws, rbac.ActionRead).Returns([]database.WorkspaceAgentLog{})
|
||||
}))
|
||||
s.Run("GetWorkspaceAppByAgentIDAndSlug", s.Subtest(func(db database.Store, check *expects) {
|
||||
ws := dbgen.Workspace(s.T(), db, database.Workspace{})
|
||||
|
|
|
@ -60,7 +60,7 @@ func New() database.Store {
|
|||
templateVersions: make([]database.TemplateVersionTable, 0),
|
||||
templates: make([]database.TemplateTable, 0),
|
||||
workspaceAgentStats: make([]database.WorkspaceAgentStat, 0),
|
||||
workspaceAgentLogs: make([]database.WorkspaceAgentStartupLog, 0),
|
||||
workspaceAgentLogs: make([]database.WorkspaceAgentLog, 0),
|
||||
workspaceBuilds: make([]database.WorkspaceBuildTable, 0),
|
||||
workspaceApps: make([]database.WorkspaceApp, 0),
|
||||
workspaces: make([]database.Workspace, 0),
|
||||
|
@ -133,7 +133,7 @@ type data struct {
|
|||
templates []database.TemplateTable
|
||||
workspaceAgents []database.WorkspaceAgent
|
||||
workspaceAgentMetadata []database.WorkspaceAgentMetadatum
|
||||
workspaceAgentLogs []database.WorkspaceAgentStartupLog
|
||||
workspaceAgentLogs []database.WorkspaceAgentLog
|
||||
workspaceApps []database.WorkspaceApp
|
||||
workspaceBuilds []database.WorkspaceBuildTable
|
||||
workspaceBuildParameters []database.WorkspaceBuildParameter
|
||||
|
@ -789,7 +789,7 @@ func (q *FakeQuerier) DeleteLicense(_ context.Context, id int32) (int32, error)
|
|||
return 0, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (*FakeQuerier) DeleteOldWorkspaceAgentStartupLogs(_ context.Context) error {
|
||||
func (*FakeQuerier) DeleteOldWorkspaceAgentLogs(_ context.Context) error {
|
||||
// noop
|
||||
return nil
|
||||
}
|
||||
|
@ -2640,6 +2640,27 @@ func (q *FakeQuerier) GetWorkspaceAgentLifecycleStateByID(ctx context.Context, i
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetWorkspaceAgentLogsAfter(_ context.Context, arg database.GetWorkspaceAgentLogsAfterParams) ([]database.WorkspaceAgentLog, error) {
|
||||
if err := validateDatabaseType(arg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
logs := []database.WorkspaceAgentLog{}
|
||||
for _, log := range q.workspaceAgentLogs {
|
||||
if log.AgentID != arg.AgentID {
|
||||
continue
|
||||
}
|
||||
if arg.CreatedAfter != 0 && log.ID <= arg.CreatedAfter {
|
||||
continue
|
||||
}
|
||||
logs = append(logs, log)
|
||||
}
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetWorkspaceAgentMetadata(_ context.Context, workspaceAgentID uuid.UUID) ([]database.WorkspaceAgentMetadatum, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
@ -2653,27 +2674,6 @@ func (q *FakeQuerier) GetWorkspaceAgentMetadata(_ context.Context, workspaceAgen
|
|||
return metadata, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetWorkspaceAgentStartupLogsAfter(_ context.Context, arg database.GetWorkspaceAgentStartupLogsAfterParams) ([]database.WorkspaceAgentStartupLog, error) {
|
||||
if err := validateDatabaseType(arg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
logs := []database.WorkspaceAgentStartupLog{}
|
||||
for _, log := range q.workspaceAgentLogs {
|
||||
if log.AgentID != arg.AgentID {
|
||||
continue
|
||||
}
|
||||
if arg.CreatedAfter != 0 && log.ID <= arg.CreatedAfter {
|
||||
continue
|
||||
}
|
||||
logs = append(logs, log)
|
||||
}
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetWorkspaceAgentStats(_ context.Context, createdAfter time.Time) ([]database.GetWorkspaceAgentStatsRow, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
@ -3964,6 +3964,51 @@ func (q *FakeQuerier) InsertWorkspaceAgent(_ context.Context, arg database.Inser
|
|||
return agent, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) InsertWorkspaceAgentLogs(_ context.Context, arg database.InsertWorkspaceAgentLogsParams) ([]database.WorkspaceAgentLog, error) {
|
||||
if err := validateDatabaseType(arg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
logs := []database.WorkspaceAgentLog{}
|
||||
id := int64(0)
|
||||
if len(q.workspaceAgentLogs) > 0 {
|
||||
id = q.workspaceAgentLogs[len(q.workspaceAgentLogs)-1].ID
|
||||
}
|
||||
outputLength := int32(0)
|
||||
for index, output := range arg.Output {
|
||||
id++
|
||||
logs = append(logs, database.WorkspaceAgentLog{
|
||||
ID: id,
|
||||
AgentID: arg.AgentID,
|
||||
CreatedAt: arg.CreatedAt[index],
|
||||
Level: arg.Level[index],
|
||||
Source: arg.Source[index],
|
||||
Output: output,
|
||||
})
|
||||
outputLength += int32(len(output))
|
||||
}
|
||||
for index, agent := range q.workspaceAgents {
|
||||
if agent.ID != arg.AgentID {
|
||||
continue
|
||||
}
|
||||
// Greater than 1MB, same as the PostgreSQL constraint!
|
||||
if agent.LogsLength+outputLength > (1 << 20) {
|
||||
return nil, &pq.Error{
|
||||
Constraint: "max_logs_length",
|
||||
Table: "workspace_agents",
|
||||
}
|
||||
}
|
||||
agent.LogsLength += outputLength
|
||||
q.workspaceAgents[index] = agent
|
||||
break
|
||||
}
|
||||
q.workspaceAgentLogs = append(q.workspaceAgentLogs, logs...)
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) InsertWorkspaceAgentMetadata(_ context.Context, arg database.InsertWorkspaceAgentMetadataParams) error {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
@ -3982,50 +4027,6 @@ func (q *FakeQuerier) InsertWorkspaceAgentMetadata(_ context.Context, arg databa
|
|||
return nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) InsertWorkspaceAgentStartupLogs(_ context.Context, arg database.InsertWorkspaceAgentStartupLogsParams) ([]database.WorkspaceAgentStartupLog, error) {
|
||||
if err := validateDatabaseType(arg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
logs := []database.WorkspaceAgentStartupLog{}
|
||||
id := int64(0)
|
||||
if len(q.workspaceAgentLogs) > 0 {
|
||||
id = q.workspaceAgentLogs[len(q.workspaceAgentLogs)-1].ID
|
||||
}
|
||||
outputLength := int32(0)
|
||||
for index, output := range arg.Output {
|
||||
id++
|
||||
logs = append(logs, database.WorkspaceAgentStartupLog{
|
||||
ID: id,
|
||||
AgentID: arg.AgentID,
|
||||
CreatedAt: arg.CreatedAt[index],
|
||||
Level: arg.Level[index],
|
||||
Output: output,
|
||||
})
|
||||
outputLength += int32(len(output))
|
||||
}
|
||||
for index, agent := range q.workspaceAgents {
|
||||
if agent.ID != arg.AgentID {
|
||||
continue
|
||||
}
|
||||
// Greater than 1MB, same as the PostgreSQL constraint!
|
||||
if agent.StartupLogsLength+outputLength > (1 << 20) {
|
||||
return nil, &pq.Error{
|
||||
Constraint: "max_startup_logs_length",
|
||||
Table: "workspace_agents",
|
||||
}
|
||||
}
|
||||
agent.StartupLogsLength += outputLength
|
||||
q.workspaceAgents[index] = agent
|
||||
break
|
||||
}
|
||||
q.workspaceAgentLogs = append(q.workspaceAgentLogs, logs...)
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) InsertWorkspaceAgentStat(_ context.Context, p database.InsertWorkspaceAgentStatParams) (database.WorkspaceAgentStat, error) {
|
||||
if err := validateDatabaseType(p); err != nil {
|
||||
return database.WorkspaceAgentStat{}, err
|
||||
|
@ -4923,6 +4924,23 @@ func (q *FakeQuerier) UpdateWorkspaceAgentLifecycleStateByID(_ context.Context,
|
|||
return sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) UpdateWorkspaceAgentLogOverflowByID(_ context.Context, arg database.UpdateWorkspaceAgentLogOverflowByIDParams) error {
|
||||
if err := validateDatabaseType(arg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
for i, agent := range q.workspaceAgents {
|
||||
if agent.ID == arg.ID {
|
||||
agent.LogsOverflowed = arg.LogsOverflowed
|
||||
q.workspaceAgents[i] = agent
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) UpdateWorkspaceAgentMetadata(_ context.Context, arg database.UpdateWorkspaceAgentMetadataParams) error {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
@ -4968,23 +4986,6 @@ func (q *FakeQuerier) UpdateWorkspaceAgentStartupByID(_ context.Context, arg dat
|
|||
return sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) UpdateWorkspaceAgentStartupLogOverflowByID(_ context.Context, arg database.UpdateWorkspaceAgentStartupLogOverflowByIDParams) error {
|
||||
if err := validateDatabaseType(arg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
for i, agent := range q.workspaceAgents {
|
||||
if agent.ID == arg.ID {
|
||||
agent.StartupLogsOverflowed = arg.StartupLogsOverflowed
|
||||
q.workspaceAgents[i] = agent
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) UpdateWorkspaceAppHealthByID(_ context.Context, arg database.UpdateWorkspaceAppHealthByIDParams) error {
|
||||
if err := validateDatabaseType(arg); err != nil {
|
||||
return err
|
||||
|
|
|
@ -162,11 +162,11 @@ func (m metricsStore) DeleteLicense(ctx context.Context, id int32) (int32, error
|
|||
return licenseID, err
|
||||
}
|
||||
|
||||
func (m metricsStore) DeleteOldWorkspaceAgentStartupLogs(ctx context.Context) error {
|
||||
func (m metricsStore) DeleteOldWorkspaceAgentLogs(ctx context.Context) error {
|
||||
start := time.Now()
|
||||
err := m.s.DeleteOldWorkspaceAgentStartupLogs(ctx)
|
||||
m.queryLatencies.WithLabelValues("DeleteOldWorkspaceAgentStartupLogs").Observe(time.Since(start).Seconds())
|
||||
return err
|
||||
r0 := m.s.DeleteOldWorkspaceAgentLogs(ctx)
|
||||
m.queryLatencies.WithLabelValues("DeleteOldWorkspaceAgentLogs").Observe(time.Since(start).Seconds())
|
||||
return r0
|
||||
}
|
||||
|
||||
func (m metricsStore) DeleteOldWorkspaceAgentStats(ctx context.Context) error {
|
||||
|
@ -781,6 +781,13 @@ func (m metricsStore) GetWorkspaceAgentLifecycleStateByID(ctx context.Context, i
|
|||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) GetWorkspaceAgentLogsAfter(ctx context.Context, arg database.GetWorkspaceAgentLogsAfterParams) ([]database.WorkspaceAgentLog, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetWorkspaceAgentLogsAfter(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("GetWorkspaceAgentLogsAfter").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) GetWorkspaceAgentMetadata(ctx context.Context, workspaceAgentID uuid.UUID) ([]database.WorkspaceAgentMetadatum, error) {
|
||||
start := time.Now()
|
||||
metadata, err := m.s.GetWorkspaceAgentMetadata(ctx, workspaceAgentID)
|
||||
|
@ -788,13 +795,6 @@ func (m metricsStore) GetWorkspaceAgentMetadata(ctx context.Context, workspaceAg
|
|||
return metadata, err
|
||||
}
|
||||
|
||||
func (m metricsStore) GetWorkspaceAgentStartupLogsAfter(ctx context.Context, arg database.GetWorkspaceAgentStartupLogsAfterParams) ([]database.WorkspaceAgentStartupLog, error) {
|
||||
start := time.Now()
|
||||
logs, err := m.s.GetWorkspaceAgentStartupLogsAfter(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("GetWorkspaceAgentStartupLogsAfter").Observe(time.Since(start).Seconds())
|
||||
return logs, err
|
||||
}
|
||||
|
||||
func (m metricsStore) GetWorkspaceAgentStats(ctx context.Context, createdAt time.Time) ([]database.GetWorkspaceAgentStatsRow, error) {
|
||||
start := time.Now()
|
||||
stats, err := m.s.GetWorkspaceAgentStats(ctx, createdAt)
|
||||
|
@ -1194,6 +1194,13 @@ func (m metricsStore) InsertWorkspaceAgent(ctx context.Context, arg database.Ins
|
|||
return agent, err
|
||||
}
|
||||
|
||||
func (m metricsStore) InsertWorkspaceAgentLogs(ctx context.Context, arg database.InsertWorkspaceAgentLogsParams) ([]database.WorkspaceAgentLog, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.InsertWorkspaceAgentLogs(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("InsertWorkspaceAgentLogs").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) InsertWorkspaceAgentMetadata(ctx context.Context, arg database.InsertWorkspaceAgentMetadataParams) error {
|
||||
start := time.Now()
|
||||
err := m.s.InsertWorkspaceAgentMetadata(ctx, arg)
|
||||
|
@ -1201,13 +1208,6 @@ func (m metricsStore) InsertWorkspaceAgentMetadata(ctx context.Context, arg data
|
|||
return err
|
||||
}
|
||||
|
||||
func (m metricsStore) InsertWorkspaceAgentStartupLogs(ctx context.Context, arg database.InsertWorkspaceAgentStartupLogsParams) ([]database.WorkspaceAgentStartupLog, error) {
|
||||
start := time.Now()
|
||||
logs, err := m.s.InsertWorkspaceAgentStartupLogs(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("InsertWorkspaceAgentStartupLogs").Observe(time.Since(start).Seconds())
|
||||
return logs, err
|
||||
}
|
||||
|
||||
func (m metricsStore) InsertWorkspaceAgentStat(ctx context.Context, arg database.InsertWorkspaceAgentStatParams) (database.WorkspaceAgentStat, error) {
|
||||
start := time.Now()
|
||||
stat, err := m.s.InsertWorkspaceAgentStat(ctx, arg)
|
||||
|
@ -1481,6 +1481,13 @@ func (m metricsStore) UpdateWorkspaceAgentLifecycleStateByID(ctx context.Context
|
|||
return r0
|
||||
}
|
||||
|
||||
func (m metricsStore) UpdateWorkspaceAgentLogOverflowByID(ctx context.Context, arg database.UpdateWorkspaceAgentLogOverflowByIDParams) error {
|
||||
start := time.Now()
|
||||
r0 := m.s.UpdateWorkspaceAgentLogOverflowByID(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("UpdateWorkspaceAgentLogOverflowByID").Observe(time.Since(start).Seconds())
|
||||
return r0
|
||||
}
|
||||
|
||||
func (m metricsStore) UpdateWorkspaceAgentMetadata(ctx context.Context, arg database.UpdateWorkspaceAgentMetadataParams) error {
|
||||
start := time.Now()
|
||||
err := m.s.UpdateWorkspaceAgentMetadata(ctx, arg)
|
||||
|
@ -1495,13 +1502,6 @@ func (m metricsStore) UpdateWorkspaceAgentStartupByID(ctx context.Context, arg d
|
|||
return err
|
||||
}
|
||||
|
||||
func (m metricsStore) UpdateWorkspaceAgentStartupLogOverflowByID(ctx context.Context, arg database.UpdateWorkspaceAgentStartupLogOverflowByIDParams) error {
|
||||
start := time.Now()
|
||||
err := m.s.UpdateWorkspaceAgentStartupLogOverflowByID(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("UpdateWorkspaceAgentStartupLogOverflowByID").Observe(time.Since(start).Seconds())
|
||||
return err
|
||||
}
|
||||
|
||||
func (m metricsStore) UpdateWorkspaceAppHealthByID(ctx context.Context, arg database.UpdateWorkspaceAppHealthByIDParams) error {
|
||||
start := time.Now()
|
||||
err := m.s.UpdateWorkspaceAppHealthByID(ctx, arg)
|
||||
|
|
|
@ -209,18 +209,18 @@ func (mr *MockStoreMockRecorder) DeleteLicense(arg0, arg1 interface{}) *gomock.C
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteLicense", reflect.TypeOf((*MockStore)(nil).DeleteLicense), arg0, arg1)
|
||||
}
|
||||
|
||||
// DeleteOldWorkspaceAgentStartupLogs mocks base method.
|
||||
func (m *MockStore) DeleteOldWorkspaceAgentStartupLogs(arg0 context.Context) error {
|
||||
// DeleteOldWorkspaceAgentLogs mocks base method.
|
||||
func (m *MockStore) DeleteOldWorkspaceAgentLogs(arg0 context.Context) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DeleteOldWorkspaceAgentStartupLogs", arg0)
|
||||
ret := m.ctrl.Call(m, "DeleteOldWorkspaceAgentLogs", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// DeleteOldWorkspaceAgentStartupLogs indicates an expected call of DeleteOldWorkspaceAgentStartupLogs.
|
||||
func (mr *MockStoreMockRecorder) DeleteOldWorkspaceAgentStartupLogs(arg0 interface{}) *gomock.Call {
|
||||
// DeleteOldWorkspaceAgentLogs indicates an expected call of DeleteOldWorkspaceAgentLogs.
|
||||
func (mr *MockStoreMockRecorder) DeleteOldWorkspaceAgentLogs(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOldWorkspaceAgentStartupLogs", reflect.TypeOf((*MockStore)(nil).DeleteOldWorkspaceAgentStartupLogs), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOldWorkspaceAgentLogs", reflect.TypeOf((*MockStore)(nil).DeleteOldWorkspaceAgentLogs), arg0)
|
||||
}
|
||||
|
||||
// DeleteOldWorkspaceAgentStats mocks base method.
|
||||
|
@ -1616,6 +1616,21 @@ func (mr *MockStoreMockRecorder) GetWorkspaceAgentLifecycleStateByID(arg0, arg1
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspaceAgentLifecycleStateByID", reflect.TypeOf((*MockStore)(nil).GetWorkspaceAgentLifecycleStateByID), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetWorkspaceAgentLogsAfter mocks base method.
|
||||
func (m *MockStore) GetWorkspaceAgentLogsAfter(arg0 context.Context, arg1 database.GetWorkspaceAgentLogsAfterParams) ([]database.WorkspaceAgentLog, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetWorkspaceAgentLogsAfter", arg0, arg1)
|
||||
ret0, _ := ret[0].([]database.WorkspaceAgentLog)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetWorkspaceAgentLogsAfter indicates an expected call of GetWorkspaceAgentLogsAfter.
|
||||
func (mr *MockStoreMockRecorder) GetWorkspaceAgentLogsAfter(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspaceAgentLogsAfter", reflect.TypeOf((*MockStore)(nil).GetWorkspaceAgentLogsAfter), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetWorkspaceAgentMetadata mocks base method.
|
||||
func (m *MockStore) GetWorkspaceAgentMetadata(arg0 context.Context, arg1 uuid.UUID) ([]database.WorkspaceAgentMetadatum, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -1631,21 +1646,6 @@ func (mr *MockStoreMockRecorder) GetWorkspaceAgentMetadata(arg0, arg1 interface{
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspaceAgentMetadata", reflect.TypeOf((*MockStore)(nil).GetWorkspaceAgentMetadata), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetWorkspaceAgentStartupLogsAfter mocks base method.
|
||||
func (m *MockStore) GetWorkspaceAgentStartupLogsAfter(arg0 context.Context, arg1 database.GetWorkspaceAgentStartupLogsAfterParams) ([]database.WorkspaceAgentStartupLog, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetWorkspaceAgentStartupLogsAfter", arg0, arg1)
|
||||
ret0, _ := ret[0].([]database.WorkspaceAgentStartupLog)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetWorkspaceAgentStartupLogsAfter indicates an expected call of GetWorkspaceAgentStartupLogsAfter.
|
||||
func (mr *MockStoreMockRecorder) GetWorkspaceAgentStartupLogsAfter(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspaceAgentStartupLogsAfter", reflect.TypeOf((*MockStore)(nil).GetWorkspaceAgentStartupLogsAfter), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetWorkspaceAgentStats mocks base method.
|
||||
func (m *MockStore) GetWorkspaceAgentStats(arg0 context.Context, arg1 time.Time) ([]database.GetWorkspaceAgentStatsRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -2509,6 +2509,21 @@ func (mr *MockStoreMockRecorder) InsertWorkspaceAgent(arg0, arg1 interface{}) *g
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertWorkspaceAgent", reflect.TypeOf((*MockStore)(nil).InsertWorkspaceAgent), arg0, arg1)
|
||||
}
|
||||
|
||||
// InsertWorkspaceAgentLogs mocks base method.
|
||||
func (m *MockStore) InsertWorkspaceAgentLogs(arg0 context.Context, arg1 database.InsertWorkspaceAgentLogsParams) ([]database.WorkspaceAgentLog, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "InsertWorkspaceAgentLogs", arg0, arg1)
|
||||
ret0, _ := ret[0].([]database.WorkspaceAgentLog)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// InsertWorkspaceAgentLogs indicates an expected call of InsertWorkspaceAgentLogs.
|
||||
func (mr *MockStoreMockRecorder) InsertWorkspaceAgentLogs(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertWorkspaceAgentLogs", reflect.TypeOf((*MockStore)(nil).InsertWorkspaceAgentLogs), arg0, arg1)
|
||||
}
|
||||
|
||||
// InsertWorkspaceAgentMetadata mocks base method.
|
||||
func (m *MockStore) InsertWorkspaceAgentMetadata(arg0 context.Context, arg1 database.InsertWorkspaceAgentMetadataParams) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -2523,21 +2538,6 @@ func (mr *MockStoreMockRecorder) InsertWorkspaceAgentMetadata(arg0, arg1 interfa
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertWorkspaceAgentMetadata", reflect.TypeOf((*MockStore)(nil).InsertWorkspaceAgentMetadata), arg0, arg1)
|
||||
}
|
||||
|
||||
// InsertWorkspaceAgentStartupLogs mocks base method.
|
||||
func (m *MockStore) InsertWorkspaceAgentStartupLogs(arg0 context.Context, arg1 database.InsertWorkspaceAgentStartupLogsParams) ([]database.WorkspaceAgentStartupLog, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "InsertWorkspaceAgentStartupLogs", arg0, arg1)
|
||||
ret0, _ := ret[0].([]database.WorkspaceAgentStartupLog)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// InsertWorkspaceAgentStartupLogs indicates an expected call of InsertWorkspaceAgentStartupLogs.
|
||||
func (mr *MockStoreMockRecorder) InsertWorkspaceAgentStartupLogs(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertWorkspaceAgentStartupLogs", reflect.TypeOf((*MockStore)(nil).InsertWorkspaceAgentStartupLogs), arg0, arg1)
|
||||
}
|
||||
|
||||
// InsertWorkspaceAgentStat mocks base method.
|
||||
func (m *MockStore) InsertWorkspaceAgentStat(arg0 context.Context, arg1 database.InsertWorkspaceAgentStatParams) (database.WorkspaceAgentStat, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -3120,6 +3120,20 @@ func (mr *MockStoreMockRecorder) UpdateWorkspaceAgentLifecycleStateByID(arg0, ar
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkspaceAgentLifecycleStateByID", reflect.TypeOf((*MockStore)(nil).UpdateWorkspaceAgentLifecycleStateByID), arg0, arg1)
|
||||
}
|
||||
|
||||
// UpdateWorkspaceAgentLogOverflowByID mocks base method.
|
||||
func (m *MockStore) UpdateWorkspaceAgentLogOverflowByID(arg0 context.Context, arg1 database.UpdateWorkspaceAgentLogOverflowByIDParams) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpdateWorkspaceAgentLogOverflowByID", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// UpdateWorkspaceAgentLogOverflowByID indicates an expected call of UpdateWorkspaceAgentLogOverflowByID.
|
||||
func (mr *MockStoreMockRecorder) UpdateWorkspaceAgentLogOverflowByID(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkspaceAgentLogOverflowByID", reflect.TypeOf((*MockStore)(nil).UpdateWorkspaceAgentLogOverflowByID), arg0, arg1)
|
||||
}
|
||||
|
||||
// UpdateWorkspaceAgentMetadata mocks base method.
|
||||
func (m *MockStore) UpdateWorkspaceAgentMetadata(arg0 context.Context, arg1 database.UpdateWorkspaceAgentMetadataParams) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -3148,20 +3162,6 @@ func (mr *MockStoreMockRecorder) UpdateWorkspaceAgentStartupByID(arg0, arg1 inte
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkspaceAgentStartupByID", reflect.TypeOf((*MockStore)(nil).UpdateWorkspaceAgentStartupByID), arg0, arg1)
|
||||
}
|
||||
|
||||
// UpdateWorkspaceAgentStartupLogOverflowByID mocks base method.
|
||||
func (m *MockStore) UpdateWorkspaceAgentStartupLogOverflowByID(arg0 context.Context, arg1 database.UpdateWorkspaceAgentStartupLogOverflowByIDParams) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpdateWorkspaceAgentStartupLogOverflowByID", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// UpdateWorkspaceAgentStartupLogOverflowByID indicates an expected call of UpdateWorkspaceAgentStartupLogOverflowByID.
|
||||
func (mr *MockStoreMockRecorder) UpdateWorkspaceAgentStartupLogOverflowByID(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkspaceAgentStartupLogOverflowByID", reflect.TypeOf((*MockStore)(nil).UpdateWorkspaceAgentStartupLogOverflowByID), arg0, arg1)
|
||||
}
|
||||
|
||||
// UpdateWorkspaceAppHealthByID mocks base method.
|
||||
func (m *MockStore) UpdateWorkspaceAppHealthByID(arg0 context.Context, arg1 database.UpdateWorkspaceAppHealthByIDParams) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
|
|
@ -40,7 +40,7 @@ func New(ctx context.Context, logger slog.Logger, db database.Store) io.Closer {
|
|||
|
||||
var eg errgroup.Group
|
||||
eg.Go(func() error {
|
||||
return db.DeleteOldWorkspaceAgentStartupLogs(ctx)
|
||||
return db.DeleteOldWorkspaceAgentLogs(ctx)
|
||||
})
|
||||
eg.Go(func() error {
|
||||
return db.DeleteOldWorkspaceAgentStats(ctx)
|
||||
|
|
|
@ -128,6 +128,15 @@ CREATE TYPE workspace_agent_lifecycle_state AS ENUM (
|
|||
'off'
|
||||
);
|
||||
|
||||
CREATE TYPE workspace_agent_log_source AS ENUM (
|
||||
'startup_script',
|
||||
'shutdown_script',
|
||||
'kubernetes_logs',
|
||||
'envbox',
|
||||
'envbuilder',
|
||||
'external'
|
||||
);
|
||||
|
||||
CREATE TYPE workspace_agent_subsystem AS ENUM (
|
||||
'envbuilder',
|
||||
'envbox',
|
||||
|
@ -672,6 +681,15 @@ CREATE TABLE user_links (
|
|||
oauth_expiry timestamp with time zone DEFAULT '0001-01-01 00:00:00+00'::timestamp with time zone NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE workspace_agent_logs (
|
||||
agent_id uuid NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
output character varying(1024) NOT NULL,
|
||||
id bigint NOT NULL,
|
||||
level log_level DEFAULT 'info'::log_level NOT NULL,
|
||||
source workspace_agent_log_source DEFAULT 'startup_script'::workspace_agent_log_source NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNLOGGED TABLE workspace_agent_metadata (
|
||||
workspace_agent_id uuid NOT NULL,
|
||||
display_name character varying(127) NOT NULL,
|
||||
|
@ -684,14 +702,6 @@ CREATE UNLOGGED TABLE workspace_agent_metadata (
|
|||
collected_at timestamp with time zone DEFAULT '0001-01-01 00:00:00+00'::timestamp with time zone NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE workspace_agent_startup_logs (
|
||||
agent_id uuid NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
output character varying(1024) NOT NULL,
|
||||
id bigint NOT NULL,
|
||||
level log_level DEFAULT 'info'::log_level NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE workspace_agent_startup_logs_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
|
@ -699,7 +709,7 @@ CREATE SEQUENCE workspace_agent_startup_logs_id_seq
|
|||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
ALTER SEQUENCE workspace_agent_startup_logs_id_seq OWNED BY workspace_agent_startup_logs.id;
|
||||
ALTER SEQUENCE workspace_agent_startup_logs_id_seq OWNED BY workspace_agent_logs.id;
|
||||
|
||||
CREATE TABLE workspace_agent_stats (
|
||||
id uuid NOT NULL,
|
||||
|
@ -749,13 +759,13 @@ CREATE TABLE workspace_agents (
|
|||
expanded_directory character varying(4096) DEFAULT ''::character varying NOT NULL,
|
||||
shutdown_script character varying(65534),
|
||||
shutdown_script_timeout_seconds integer DEFAULT 0 NOT NULL,
|
||||
startup_logs_length integer DEFAULT 0 NOT NULL,
|
||||
startup_logs_overflowed boolean DEFAULT false NOT NULL,
|
||||
logs_length integer DEFAULT 0 NOT NULL,
|
||||
logs_overflowed boolean DEFAULT false NOT NULL,
|
||||
subsystem workspace_agent_subsystem DEFAULT 'none'::workspace_agent_subsystem NOT NULL,
|
||||
startup_script_behavior startup_script_behavior DEFAULT 'non-blocking'::startup_script_behavior NOT NULL,
|
||||
started_at timestamp with time zone,
|
||||
ready_at timestamp with time zone,
|
||||
CONSTRAINT max_startup_logs_length CHECK ((startup_logs_length <= 1048576))
|
||||
CONSTRAINT max_logs_length CHECK ((logs_length <= 1048576))
|
||||
);
|
||||
|
||||
COMMENT ON COLUMN workspace_agents.version IS 'Version tracks the version of the currently running workspace agent. Workspace agents register their version upon start.';
|
||||
|
@ -776,9 +786,9 @@ COMMENT ON COLUMN workspace_agents.shutdown_script IS 'Script that is executed b
|
|||
|
||||
COMMENT ON COLUMN workspace_agents.shutdown_script_timeout_seconds IS 'The number of seconds to wait for the shutdown script to complete. If the script does not complete within this time, the agent lifecycle will be marked as shutdown_timeout.';
|
||||
|
||||
COMMENT ON COLUMN workspace_agents.startup_logs_length IS 'Total length of startup logs';
|
||||
COMMENT ON COLUMN workspace_agents.logs_length IS 'Total length of startup logs';
|
||||
|
||||
COMMENT ON COLUMN workspace_agents.startup_logs_overflowed IS 'Whether the startup logs overflowed in length';
|
||||
COMMENT ON COLUMN workspace_agents.logs_overflowed IS 'Whether the startup logs overflowed in length';
|
||||
|
||||
COMMENT ON COLUMN workspace_agents.startup_script_behavior IS 'When startup script behavior is non-blocking, the workspace will be ready and accessible upon agent connection, when it is blocking, workspace will wait for the startup script to complete before becoming ready and accessible.';
|
||||
|
||||
|
@ -938,7 +948,7 @@ ALTER TABLE ONLY licenses ALTER COLUMN id SET DEFAULT nextval('licenses_id_seq':
|
|||
|
||||
ALTER TABLE ONLY provisioner_job_logs ALTER COLUMN id SET DEFAULT nextval('provisioner_job_logs_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY workspace_agent_startup_logs ALTER COLUMN id SET DEFAULT nextval('workspace_agent_startup_logs_id_seq'::regclass);
|
||||
ALTER TABLE ONLY workspace_agent_logs ALTER COLUMN id SET DEFAULT nextval('workspace_agent_startup_logs_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY workspace_proxies ALTER COLUMN region_id SET DEFAULT nextval('workspace_proxies_region_id_seq'::regclass);
|
||||
|
||||
|
@ -1046,7 +1056,7 @@ ALTER TABLE ONLY users
|
|||
ALTER TABLE ONLY workspace_agent_metadata
|
||||
ADD CONSTRAINT workspace_agent_metadata_pkey PRIMARY KEY (workspace_agent_id, key);
|
||||
|
||||
ALTER TABLE ONLY workspace_agent_startup_logs
|
||||
ALTER TABLE ONLY workspace_agent_logs
|
||||
ADD CONSTRAINT workspace_agent_startup_logs_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY workspace_agents
|
||||
|
@ -1132,7 +1142,7 @@ CREATE UNIQUE INDEX users_email_lower_idx ON users USING btree (lower(email)) WH
|
|||
|
||||
CREATE UNIQUE INDEX users_username_lower_idx ON users USING btree (lower(username)) WHERE (deleted = false);
|
||||
|
||||
CREATE INDEX workspace_agent_startup_logs_id_agent_id_idx ON workspace_agent_startup_logs USING btree (agent_id, id);
|
||||
CREATE INDEX workspace_agent_startup_logs_id_agent_id_idx ON workspace_agent_logs USING btree (agent_id, id);
|
||||
|
||||
CREATE INDEX workspace_agents_auth_token_idx ON workspace_agents USING btree (auth_token);
|
||||
|
||||
|
@ -1217,7 +1227,7 @@ ALTER TABLE ONLY user_links
|
|||
ALTER TABLE ONLY workspace_agent_metadata
|
||||
ADD CONSTRAINT workspace_agent_metadata_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY workspace_agent_startup_logs
|
||||
ALTER TABLE ONLY workspace_agent_logs
|
||||
ADD CONSTRAINT workspace_agent_startup_logs_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY workspace_agents
|
||||
|
|
|
@ -49,10 +49,10 @@ func IsQueryCanceledError(err error) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func IsStartupLogsLimitError(err error) bool {
|
||||
func IsWorkspaceAgentLogsLimitError(err error) bool {
|
||||
var pqErr *pq.Error
|
||||
if errors.As(err, &pqErr) {
|
||||
return pqErr.Constraint == "max_startup_logs_length" && pqErr.Table == "workspace_agents"
|
||||
return pqErr.Constraint == "max_logs_length" && pqErr.Table == "workspace_agents"
|
||||
}
|
||||
|
||||
return false
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
BEGIN;
|
||||
ALTER TABLE workspace_agent_logs RENAME TO workspace_agent_startup_logs;
|
||||
ALTER TABLE workspace_agent_startup_logs DROP COLUMN source;
|
||||
DROP TYPE workspace_agent_log_source;
|
||||
ALTER TABLE workspace_agents RENAME COLUMN logs_overflowed TO startup_logs_overflowed;
|
||||
ALTER TABLE workspace_agents RENAME COLUMN logs_length TO startup_logs_length;
|
||||
ALTER TABLE workspace_agents RENAME CONSTRAINT max_logs_length TO max_startup_logs_length;
|
||||
COMMIT;
|
|
@ -0,0 +1,8 @@
|
|||
BEGIN;
|
||||
CREATE TYPE workspace_agent_log_source AS ENUM ('startup_script', 'shutdown_script', 'kubernetes_logs', 'envbox', 'envbuilder', 'external');
|
||||
ALTER TABLE workspace_agent_startup_logs RENAME TO workspace_agent_logs;
|
||||
ALTER TABLE workspace_agent_logs ADD COLUMN source workspace_agent_log_source NOT NULL DEFAULT 'startup_script';
|
||||
ALTER TABLE workspace_agents RENAME COLUMN startup_logs_overflowed TO logs_overflowed;
|
||||
ALTER TABLE workspace_agents RENAME COLUMN startup_logs_length TO logs_length;
|
||||
ALTER TABLE workspace_agents RENAME CONSTRAINT max_startup_logs_length TO max_logs_length;
|
||||
COMMIT;
|
|
@ -1169,6 +1169,76 @@ func AllWorkspaceAgentLifecycleStateValues() []WorkspaceAgentLifecycleState {
|
|||
}
|
||||
}
|
||||
|
||||
type WorkspaceAgentLogSource string
|
||||
|
||||
const (
|
||||
WorkspaceAgentLogSourceStartupScript WorkspaceAgentLogSource = "startup_script"
|
||||
WorkspaceAgentLogSourceShutdownScript WorkspaceAgentLogSource = "shutdown_script"
|
||||
WorkspaceAgentLogSourceKubernetesLogs WorkspaceAgentLogSource = "kubernetes_logs"
|
||||
WorkspaceAgentLogSourceEnvbox WorkspaceAgentLogSource = "envbox"
|
||||
WorkspaceAgentLogSourceEnvbuilder WorkspaceAgentLogSource = "envbuilder"
|
||||
WorkspaceAgentLogSourceExternal WorkspaceAgentLogSource = "external"
|
||||
)
|
||||
|
||||
func (e *WorkspaceAgentLogSource) Scan(src interface{}) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = WorkspaceAgentLogSource(s)
|
||||
case string:
|
||||
*e = WorkspaceAgentLogSource(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for WorkspaceAgentLogSource: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullWorkspaceAgentLogSource struct {
|
||||
WorkspaceAgentLogSource WorkspaceAgentLogSource `json:"workspace_agent_log_source"`
|
||||
Valid bool `json:"valid"` // Valid is true if WorkspaceAgentLogSource is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullWorkspaceAgentLogSource) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
ns.WorkspaceAgentLogSource, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.WorkspaceAgentLogSource.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullWorkspaceAgentLogSource) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.WorkspaceAgentLogSource), nil
|
||||
}
|
||||
|
||||
func (e WorkspaceAgentLogSource) Valid() bool {
|
||||
switch e {
|
||||
case WorkspaceAgentLogSourceStartupScript,
|
||||
WorkspaceAgentLogSourceShutdownScript,
|
||||
WorkspaceAgentLogSourceKubernetesLogs,
|
||||
WorkspaceAgentLogSourceEnvbox,
|
||||
WorkspaceAgentLogSourceEnvbuilder,
|
||||
WorkspaceAgentLogSourceExternal:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func AllWorkspaceAgentLogSourceValues() []WorkspaceAgentLogSource {
|
||||
return []WorkspaceAgentLogSource{
|
||||
WorkspaceAgentLogSourceStartupScript,
|
||||
WorkspaceAgentLogSourceShutdownScript,
|
||||
WorkspaceAgentLogSourceKubernetesLogs,
|
||||
WorkspaceAgentLogSourceEnvbox,
|
||||
WorkspaceAgentLogSourceEnvbuilder,
|
||||
WorkspaceAgentLogSourceExternal,
|
||||
}
|
||||
}
|
||||
|
||||
type WorkspaceAgentSubsystem string
|
||||
|
||||
const (
|
||||
|
@ -1806,10 +1876,10 @@ type WorkspaceAgent struct {
|
|||
// The number of seconds to wait for the shutdown script to complete. If the script does not complete within this time, the agent lifecycle will be marked as shutdown_timeout.
|
||||
ShutdownScriptTimeoutSeconds int32 `db:"shutdown_script_timeout_seconds" json:"shutdown_script_timeout_seconds"`
|
||||
// Total length of startup logs
|
||||
StartupLogsLength int32 `db:"startup_logs_length" json:"startup_logs_length"`
|
||||
LogsLength int32 `db:"logs_length" json:"logs_length"`
|
||||
// Whether the startup logs overflowed in length
|
||||
StartupLogsOverflowed bool `db:"startup_logs_overflowed" json:"startup_logs_overflowed"`
|
||||
Subsystem WorkspaceAgentSubsystem `db:"subsystem" json:"subsystem"`
|
||||
LogsOverflowed bool `db:"logs_overflowed" json:"logs_overflowed"`
|
||||
Subsystem WorkspaceAgentSubsystem `db:"subsystem" json:"subsystem"`
|
||||
// When startup script behavior is non-blocking, the workspace will be ready and accessible upon agent connection, when it is blocking, workspace will wait for the startup script to complete before becoming ready and accessible.
|
||||
StartupScriptBehavior StartupScriptBehavior `db:"startup_script_behavior" json:"startup_script_behavior"`
|
||||
// The time the agent entered the starting lifecycle state
|
||||
|
@ -1818,6 +1888,15 @@ type WorkspaceAgent struct {
|
|||
ReadyAt sql.NullTime `db:"ready_at" json:"ready_at"`
|
||||
}
|
||||
|
||||
type WorkspaceAgentLog struct {
|
||||
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
Output string `db:"output" json:"output"`
|
||||
ID int64 `db:"id" json:"id"`
|
||||
Level LogLevel `db:"level" json:"level"`
|
||||
Source WorkspaceAgentLogSource `db:"source" json:"source"`
|
||||
}
|
||||
|
||||
type WorkspaceAgentMetadatum struct {
|
||||
WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"`
|
||||
DisplayName string `db:"display_name" json:"display_name"`
|
||||
|
@ -1830,14 +1909,6 @@ type WorkspaceAgentMetadatum struct {
|
|||
CollectedAt time.Time `db:"collected_at" json:"collected_at"`
|
||||
}
|
||||
|
||||
type WorkspaceAgentStartupLog struct {
|
||||
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
Output string `db:"output" json:"output"`
|
||||
ID int64 `db:"id" json:"id"`
|
||||
Level LogLevel `db:"level" json:"level"`
|
||||
}
|
||||
|
||||
type WorkspaceAgentStat struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
|
|
|
@ -36,7 +36,7 @@ type sqlcQuerier interface {
|
|||
DeleteLicense(ctx context.Context, id int32) (int32, error)
|
||||
// If an agent hasn't connected in the last 7 days, we purge it's logs.
|
||||
// Logs can take up a lot of space, so it's important we clean up frequently.
|
||||
DeleteOldWorkspaceAgentStartupLogs(ctx context.Context) error
|
||||
DeleteOldWorkspaceAgentLogs(ctx context.Context) error
|
||||
DeleteOldWorkspaceAgentStats(ctx context.Context) error
|
||||
DeleteReplicasUpdatedBefore(ctx context.Context, updatedAt time.Time) error
|
||||
DeleteTailnetAgent(ctx context.Context, arg DeleteTailnetAgentParams) (DeleteTailnetAgentRow, error)
|
||||
|
@ -145,8 +145,8 @@ type sqlcQuerier interface {
|
|||
GetWorkspaceAgentByID(ctx context.Context, id uuid.UUID) (WorkspaceAgent, error)
|
||||
GetWorkspaceAgentByInstanceID(ctx context.Context, authInstanceID string) (WorkspaceAgent, error)
|
||||
GetWorkspaceAgentLifecycleStateByID(ctx context.Context, id uuid.UUID) (GetWorkspaceAgentLifecycleStateByIDRow, error)
|
||||
GetWorkspaceAgentLogsAfter(ctx context.Context, arg GetWorkspaceAgentLogsAfterParams) ([]WorkspaceAgentLog, error)
|
||||
GetWorkspaceAgentMetadata(ctx context.Context, workspaceAgentID uuid.UUID) ([]WorkspaceAgentMetadatum, error)
|
||||
GetWorkspaceAgentStartupLogsAfter(ctx context.Context, arg GetWorkspaceAgentStartupLogsAfterParams) ([]WorkspaceAgentStartupLog, error)
|
||||
GetWorkspaceAgentStats(ctx context.Context, createdAt time.Time) ([]GetWorkspaceAgentStatsRow, error)
|
||||
GetWorkspaceAgentStatsAndLabels(ctx context.Context, createdAt time.Time) ([]GetWorkspaceAgentStatsAndLabelsRow, error)
|
||||
GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgent, error)
|
||||
|
@ -215,8 +215,8 @@ type sqlcQuerier interface {
|
|||
InsertUserLink(ctx context.Context, arg InsertUserLinkParams) (UserLink, error)
|
||||
InsertWorkspace(ctx context.Context, arg InsertWorkspaceParams) (Workspace, error)
|
||||
InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspaceAgentParams) (WorkspaceAgent, error)
|
||||
InsertWorkspaceAgentLogs(ctx context.Context, arg InsertWorkspaceAgentLogsParams) ([]WorkspaceAgentLog, error)
|
||||
InsertWorkspaceAgentMetadata(ctx context.Context, arg InsertWorkspaceAgentMetadataParams) error
|
||||
InsertWorkspaceAgentStartupLogs(ctx context.Context, arg InsertWorkspaceAgentStartupLogsParams) ([]WorkspaceAgentStartupLog, error)
|
||||
InsertWorkspaceAgentStat(ctx context.Context, arg InsertWorkspaceAgentStatParams) (WorkspaceAgentStat, error)
|
||||
InsertWorkspaceApp(ctx context.Context, arg InsertWorkspaceAppParams) (WorkspaceApp, error)
|
||||
InsertWorkspaceBuild(ctx context.Context, arg InsertWorkspaceBuildParams) error
|
||||
|
@ -260,9 +260,9 @@ type sqlcQuerier interface {
|
|||
UpdateWorkspace(ctx context.Context, arg UpdateWorkspaceParams) (Workspace, error)
|
||||
UpdateWorkspaceAgentConnectionByID(ctx context.Context, arg UpdateWorkspaceAgentConnectionByIDParams) error
|
||||
UpdateWorkspaceAgentLifecycleStateByID(ctx context.Context, arg UpdateWorkspaceAgentLifecycleStateByIDParams) error
|
||||
UpdateWorkspaceAgentLogOverflowByID(ctx context.Context, arg UpdateWorkspaceAgentLogOverflowByIDParams) error
|
||||
UpdateWorkspaceAgentMetadata(ctx context.Context, arg UpdateWorkspaceAgentMetadataParams) error
|
||||
UpdateWorkspaceAgentStartupByID(ctx context.Context, arg UpdateWorkspaceAgentStartupByIDParams) error
|
||||
UpdateWorkspaceAgentStartupLogOverflowByID(ctx context.Context, arg UpdateWorkspaceAgentStartupLogOverflowByIDParams) error
|
||||
UpdateWorkspaceAppHealthByID(ctx context.Context, arg UpdateWorkspaceAppHealthByIDParams) error
|
||||
UpdateWorkspaceAutostart(ctx context.Context, arg UpdateWorkspaceAutostartParams) error
|
||||
UpdateWorkspaceBuildByID(ctx context.Context, arg UpdateWorkspaceBuildByIDParams) error
|
||||
|
|
|
@ -91,7 +91,7 @@ func TestGetDeploymentWorkspaceAgentStats(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestInsertWorkspaceAgentStartupLogs(t *testing.T) {
|
||||
func TestInsertWorkspaceAgentLogs(t *testing.T) {
|
||||
t.Parallel()
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
|
@ -111,25 +111,27 @@ func TestInsertWorkspaceAgentStartupLogs(t *testing.T) {
|
|||
agent := dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{
|
||||
ResourceID: resource.ID,
|
||||
})
|
||||
logs, err := db.InsertWorkspaceAgentStartupLogs(ctx, database.InsertWorkspaceAgentStartupLogsParams{
|
||||
logs, err := db.InsertWorkspaceAgentLogs(ctx, database.InsertWorkspaceAgentLogsParams{
|
||||
AgentID: agent.ID,
|
||||
CreatedAt: []time.Time{database.Now()},
|
||||
Output: []string{"first"},
|
||||
Level: []database.LogLevel{database.LogLevelInfo},
|
||||
Source: []database.WorkspaceAgentLogSource{database.WorkspaceAgentLogSourceExternal},
|
||||
// 1 MB is the max
|
||||
OutputLength: 1 << 20,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(1), logs[0].ID)
|
||||
|
||||
_, err = db.InsertWorkspaceAgentStartupLogs(ctx, database.InsertWorkspaceAgentStartupLogsParams{
|
||||
_, err = db.InsertWorkspaceAgentLogs(ctx, database.InsertWorkspaceAgentLogsParams{
|
||||
AgentID: agent.ID,
|
||||
CreatedAt: []time.Time{database.Now()},
|
||||
Output: []string{"second"},
|
||||
Level: []database.LogLevel{database.LogLevelInfo},
|
||||
Source: []database.WorkspaceAgentLogSource{database.WorkspaceAgentLogSourceExternal},
|
||||
OutputLength: 1,
|
||||
})
|
||||
require.True(t, database.IsStartupLogsLimitError(err))
|
||||
require.True(t, database.IsWorkspaceAgentLogsLimitError(err))
|
||||
}
|
||||
|
||||
func TestProxyByHostname(t *testing.T) {
|
||||
|
|
|
@ -5914,22 +5914,22 @@ func (q *sqlQuerier) UpdateUserStatus(ctx context.Context, arg UpdateUserStatusP
|
|||
return i, err
|
||||
}
|
||||
|
||||
const deleteOldWorkspaceAgentStartupLogs = `-- name: DeleteOldWorkspaceAgentStartupLogs :exec
|
||||
DELETE FROM workspace_agent_startup_logs WHERE agent_id IN
|
||||
const deleteOldWorkspaceAgentLogs = `-- name: DeleteOldWorkspaceAgentLogs :exec
|
||||
DELETE FROM workspace_agent_logs WHERE agent_id IN
|
||||
(SELECT id FROM workspace_agents WHERE last_connected_at IS NOT NULL
|
||||
AND last_connected_at < NOW() - INTERVAL '7 day')
|
||||
`
|
||||
|
||||
// If an agent hasn't connected in the last 7 days, we purge it's logs.
|
||||
// Logs can take up a lot of space, so it's important we clean up frequently.
|
||||
func (q *sqlQuerier) DeleteOldWorkspaceAgentStartupLogs(ctx context.Context) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteOldWorkspaceAgentStartupLogs)
|
||||
func (q *sqlQuerier) DeleteOldWorkspaceAgentLogs(ctx context.Context) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteOldWorkspaceAgentLogs)
|
||||
return err
|
||||
}
|
||||
|
||||
const getWorkspaceAgentByAuthToken = `-- name: GetWorkspaceAgentByAuthToken :one
|
||||
SELECT
|
||||
id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, startup_logs_length, startup_logs_overflowed, subsystem, startup_script_behavior, started_at, ready_at
|
||||
id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, subsystem, startup_script_behavior, started_at, ready_at
|
||||
FROM
|
||||
workspace_agents
|
||||
WHERE
|
||||
|
@ -5969,8 +5969,8 @@ func (q *sqlQuerier) GetWorkspaceAgentByAuthToken(ctx context.Context, authToken
|
|||
&i.ExpandedDirectory,
|
||||
&i.ShutdownScript,
|
||||
&i.ShutdownScriptTimeoutSeconds,
|
||||
&i.StartupLogsLength,
|
||||
&i.StartupLogsOverflowed,
|
||||
&i.LogsLength,
|
||||
&i.LogsOverflowed,
|
||||
&i.Subsystem,
|
||||
&i.StartupScriptBehavior,
|
||||
&i.StartedAt,
|
||||
|
@ -5981,7 +5981,7 @@ func (q *sqlQuerier) GetWorkspaceAgentByAuthToken(ctx context.Context, authToken
|
|||
|
||||
const getWorkspaceAgentByID = `-- name: GetWorkspaceAgentByID :one
|
||||
SELECT
|
||||
id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, startup_logs_length, startup_logs_overflowed, subsystem, startup_script_behavior, started_at, ready_at
|
||||
id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, subsystem, startup_script_behavior, started_at, ready_at
|
||||
FROM
|
||||
workspace_agents
|
||||
WHERE
|
||||
|
@ -6019,8 +6019,8 @@ func (q *sqlQuerier) GetWorkspaceAgentByID(ctx context.Context, id uuid.UUID) (W
|
|||
&i.ExpandedDirectory,
|
||||
&i.ShutdownScript,
|
||||
&i.ShutdownScriptTimeoutSeconds,
|
||||
&i.StartupLogsLength,
|
||||
&i.StartupLogsOverflowed,
|
||||
&i.LogsLength,
|
||||
&i.LogsOverflowed,
|
||||
&i.Subsystem,
|
||||
&i.StartupScriptBehavior,
|
||||
&i.StartedAt,
|
||||
|
@ -6031,7 +6031,7 @@ func (q *sqlQuerier) GetWorkspaceAgentByID(ctx context.Context, id uuid.UUID) (W
|
|||
|
||||
const getWorkspaceAgentByInstanceID = `-- name: GetWorkspaceAgentByInstanceID :one
|
||||
SELECT
|
||||
id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, startup_logs_length, startup_logs_overflowed, subsystem, startup_script_behavior, started_at, ready_at
|
||||
id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, subsystem, startup_script_behavior, started_at, ready_at
|
||||
FROM
|
||||
workspace_agents
|
||||
WHERE
|
||||
|
@ -6071,8 +6071,8 @@ func (q *sqlQuerier) GetWorkspaceAgentByInstanceID(ctx context.Context, authInst
|
|||
&i.ExpandedDirectory,
|
||||
&i.ShutdownScript,
|
||||
&i.ShutdownScriptTimeoutSeconds,
|
||||
&i.StartupLogsLength,
|
||||
&i.StartupLogsOverflowed,
|
||||
&i.LogsLength,
|
||||
&i.LogsOverflowed,
|
||||
&i.Subsystem,
|
||||
&i.StartupScriptBehavior,
|
||||
&i.StartedAt,
|
||||
|
@ -6105,6 +6105,53 @@ func (q *sqlQuerier) GetWorkspaceAgentLifecycleStateByID(ctx context.Context, id
|
|||
return i, err
|
||||
}
|
||||
|
||||
const getWorkspaceAgentLogsAfter = `-- name: GetWorkspaceAgentLogsAfter :many
|
||||
SELECT
|
||||
agent_id, created_at, output, id, level, source
|
||||
FROM
|
||||
workspace_agent_logs
|
||||
WHERE
|
||||
agent_id = $1
|
||||
AND (
|
||||
id > $2
|
||||
) ORDER BY id ASC
|
||||
`
|
||||
|
||||
type GetWorkspaceAgentLogsAfterParams struct {
|
||||
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
|
||||
CreatedAfter int64 `db:"created_after" json:"created_after"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) GetWorkspaceAgentLogsAfter(ctx context.Context, arg GetWorkspaceAgentLogsAfterParams) ([]WorkspaceAgentLog, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getWorkspaceAgentLogsAfter, arg.AgentID, arg.CreatedAfter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []WorkspaceAgentLog
|
||||
for rows.Next() {
|
||||
var i WorkspaceAgentLog
|
||||
if err := rows.Scan(
|
||||
&i.AgentID,
|
||||
&i.CreatedAt,
|
||||
&i.Output,
|
||||
&i.ID,
|
||||
&i.Level,
|
||||
&i.Source,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getWorkspaceAgentMetadata = `-- name: GetWorkspaceAgentMetadata :many
|
||||
SELECT
|
||||
workspace_agent_id, display_name, key, script, value, error, timeout, interval, collected_at
|
||||
|
@ -6147,55 +6194,9 @@ func (q *sqlQuerier) GetWorkspaceAgentMetadata(ctx context.Context, workspaceAge
|
|||
return items, nil
|
||||
}
|
||||
|
||||
const getWorkspaceAgentStartupLogsAfter = `-- name: GetWorkspaceAgentStartupLogsAfter :many
|
||||
SELECT
|
||||
agent_id, created_at, output, id, level
|
||||
FROM
|
||||
workspace_agent_startup_logs
|
||||
WHERE
|
||||
agent_id = $1
|
||||
AND (
|
||||
id > $2
|
||||
) ORDER BY id ASC
|
||||
`
|
||||
|
||||
type GetWorkspaceAgentStartupLogsAfterParams struct {
|
||||
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
|
||||
CreatedAfter int64 `db:"created_after" json:"created_after"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) GetWorkspaceAgentStartupLogsAfter(ctx context.Context, arg GetWorkspaceAgentStartupLogsAfterParams) ([]WorkspaceAgentStartupLog, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getWorkspaceAgentStartupLogsAfter, arg.AgentID, arg.CreatedAfter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []WorkspaceAgentStartupLog
|
||||
for rows.Next() {
|
||||
var i WorkspaceAgentStartupLog
|
||||
if err := rows.Scan(
|
||||
&i.AgentID,
|
||||
&i.CreatedAt,
|
||||
&i.Output,
|
||||
&i.ID,
|
||||
&i.Level,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getWorkspaceAgentsByResourceIDs = `-- name: GetWorkspaceAgentsByResourceIDs :many
|
||||
SELECT
|
||||
id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, startup_logs_length, startup_logs_overflowed, subsystem, startup_script_behavior, started_at, ready_at
|
||||
id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, subsystem, startup_script_behavior, started_at, ready_at
|
||||
FROM
|
||||
workspace_agents
|
||||
WHERE
|
||||
|
@ -6239,8 +6240,8 @@ func (q *sqlQuerier) GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids []
|
|||
&i.ExpandedDirectory,
|
||||
&i.ShutdownScript,
|
||||
&i.ShutdownScriptTimeoutSeconds,
|
||||
&i.StartupLogsLength,
|
||||
&i.StartupLogsOverflowed,
|
||||
&i.LogsLength,
|
||||
&i.LogsOverflowed,
|
||||
&i.Subsystem,
|
||||
&i.StartupScriptBehavior,
|
||||
&i.StartedAt,
|
||||
|
@ -6260,7 +6261,7 @@ func (q *sqlQuerier) GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids []
|
|||
}
|
||||
|
||||
const getWorkspaceAgentsCreatedAfter = `-- name: GetWorkspaceAgentsCreatedAfter :many
|
||||
SELECT id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, startup_logs_length, startup_logs_overflowed, subsystem, startup_script_behavior, started_at, ready_at FROM workspace_agents WHERE created_at > $1
|
||||
SELECT id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, subsystem, startup_script_behavior, started_at, ready_at FROM workspace_agents WHERE created_at > $1
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceAgent, error) {
|
||||
|
@ -6300,8 +6301,8 @@ func (q *sqlQuerier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, created
|
|||
&i.ExpandedDirectory,
|
||||
&i.ShutdownScript,
|
||||
&i.ShutdownScriptTimeoutSeconds,
|
||||
&i.StartupLogsLength,
|
||||
&i.StartupLogsOverflowed,
|
||||
&i.LogsLength,
|
||||
&i.LogsOverflowed,
|
||||
&i.Subsystem,
|
||||
&i.StartupScriptBehavior,
|
||||
&i.StartedAt,
|
||||
|
@ -6322,7 +6323,7 @@ func (q *sqlQuerier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, created
|
|||
|
||||
const getWorkspaceAgentsInLatestBuildByWorkspaceID = `-- name: GetWorkspaceAgentsInLatestBuildByWorkspaceID :many
|
||||
SELECT
|
||||
workspace_agents.id, workspace_agents.created_at, workspace_agents.updated_at, workspace_agents.name, workspace_agents.first_connected_at, workspace_agents.last_connected_at, workspace_agents.disconnected_at, workspace_agents.resource_id, workspace_agents.auth_token, workspace_agents.auth_instance_id, workspace_agents.architecture, workspace_agents.environment_variables, workspace_agents.operating_system, workspace_agents.startup_script, workspace_agents.instance_metadata, workspace_agents.resource_metadata, workspace_agents.directory, workspace_agents.version, workspace_agents.last_connected_replica_id, workspace_agents.connection_timeout_seconds, workspace_agents.troubleshooting_url, workspace_agents.motd_file, workspace_agents.lifecycle_state, workspace_agents.startup_script_timeout_seconds, workspace_agents.expanded_directory, workspace_agents.shutdown_script, workspace_agents.shutdown_script_timeout_seconds, workspace_agents.startup_logs_length, workspace_agents.startup_logs_overflowed, workspace_agents.subsystem, workspace_agents.startup_script_behavior, workspace_agents.started_at, workspace_agents.ready_at
|
||||
workspace_agents.id, workspace_agents.created_at, workspace_agents.updated_at, workspace_agents.name, workspace_agents.first_connected_at, workspace_agents.last_connected_at, workspace_agents.disconnected_at, workspace_agents.resource_id, workspace_agents.auth_token, workspace_agents.auth_instance_id, workspace_agents.architecture, workspace_agents.environment_variables, workspace_agents.operating_system, workspace_agents.startup_script, workspace_agents.instance_metadata, workspace_agents.resource_metadata, workspace_agents.directory, workspace_agents.version, workspace_agents.last_connected_replica_id, workspace_agents.connection_timeout_seconds, workspace_agents.troubleshooting_url, workspace_agents.motd_file, workspace_agents.lifecycle_state, workspace_agents.startup_script_timeout_seconds, workspace_agents.expanded_directory, workspace_agents.shutdown_script, workspace_agents.shutdown_script_timeout_seconds, workspace_agents.logs_length, workspace_agents.logs_overflowed, workspace_agents.subsystem, workspace_agents.startup_script_behavior, workspace_agents.started_at, workspace_agents.ready_at
|
||||
FROM
|
||||
workspace_agents
|
||||
JOIN
|
||||
|
@ -6378,8 +6379,8 @@ func (q *sqlQuerier) GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx context.Co
|
|||
&i.ExpandedDirectory,
|
||||
&i.ShutdownScript,
|
||||
&i.ShutdownScriptTimeoutSeconds,
|
||||
&i.StartupLogsLength,
|
||||
&i.StartupLogsOverflowed,
|
||||
&i.LogsLength,
|
||||
&i.LogsOverflowed,
|
||||
&i.Subsystem,
|
||||
&i.StartupScriptBehavior,
|
||||
&i.StartedAt,
|
||||
|
@ -6424,7 +6425,7 @@ INSERT INTO
|
|||
shutdown_script_timeout_seconds
|
||||
)
|
||||
VALUES
|
||||
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21) RETURNING id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, startup_logs_length, startup_logs_overflowed, subsystem, startup_script_behavior, started_at, ready_at
|
||||
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21) RETURNING id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, subsystem, startup_script_behavior, started_at, ready_at
|
||||
`
|
||||
|
||||
type InsertWorkspaceAgentParams struct {
|
||||
|
@ -6504,8 +6505,8 @@ func (q *sqlQuerier) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspa
|
|||
&i.ExpandedDirectory,
|
||||
&i.ShutdownScript,
|
||||
&i.ShutdownScriptTimeoutSeconds,
|
||||
&i.StartupLogsLength,
|
||||
&i.StartupLogsOverflowed,
|
||||
&i.LogsLength,
|
||||
&i.LogsOverflowed,
|
||||
&i.Subsystem,
|
||||
&i.StartupScriptBehavior,
|
||||
&i.StartedAt,
|
||||
|
@ -6514,6 +6515,68 @@ func (q *sqlQuerier) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspa
|
|||
return i, err
|
||||
}
|
||||
|
||||
const insertWorkspaceAgentLogs = `-- name: InsertWorkspaceAgentLogs :many
|
||||
WITH new_length AS (
|
||||
UPDATE workspace_agents SET
|
||||
logs_length = logs_length + $6 WHERE workspace_agents.id = $1
|
||||
)
|
||||
INSERT INTO
|
||||
workspace_agent_logs (agent_id, created_at, output, level, source)
|
||||
SELECT
|
||||
$1 :: uuid AS agent_id,
|
||||
unnest($2 :: timestamptz [ ]) AS created_at,
|
||||
unnest($3 :: VARCHAR(1024) [ ]) AS output,
|
||||
unnest($4 :: log_level [ ]) AS level,
|
||||
unnest($5 :: workspace_agent_log_source [ ]) AS source
|
||||
RETURNING workspace_agent_logs.agent_id, workspace_agent_logs.created_at, workspace_agent_logs.output, workspace_agent_logs.id, workspace_agent_logs.level, workspace_agent_logs.source
|
||||
`
|
||||
|
||||
type InsertWorkspaceAgentLogsParams struct {
|
||||
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
|
||||
CreatedAt []time.Time `db:"created_at" json:"created_at"`
|
||||
Output []string `db:"output" json:"output"`
|
||||
Level []LogLevel `db:"level" json:"level"`
|
||||
Source []WorkspaceAgentLogSource `db:"source" json:"source"`
|
||||
OutputLength int32 `db:"output_length" json:"output_length"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) InsertWorkspaceAgentLogs(ctx context.Context, arg InsertWorkspaceAgentLogsParams) ([]WorkspaceAgentLog, error) {
|
||||
rows, err := q.db.QueryContext(ctx, insertWorkspaceAgentLogs,
|
||||
arg.AgentID,
|
||||
pq.Array(arg.CreatedAt),
|
||||
pq.Array(arg.Output),
|
||||
pq.Array(arg.Level),
|
||||
pq.Array(arg.Source),
|
||||
arg.OutputLength,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []WorkspaceAgentLog
|
||||
for rows.Next() {
|
||||
var i WorkspaceAgentLog
|
||||
if err := rows.Scan(
|
||||
&i.AgentID,
|
||||
&i.CreatedAt,
|
||||
&i.Output,
|
||||
&i.ID,
|
||||
&i.Level,
|
||||
&i.Source,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const insertWorkspaceAgentMetadata = `-- name: InsertWorkspaceAgentMetadata :exec
|
||||
INSERT INTO
|
||||
workspace_agent_metadata (
|
||||
|
@ -6549,64 +6612,6 @@ func (q *sqlQuerier) InsertWorkspaceAgentMetadata(ctx context.Context, arg Inser
|
|||
return err
|
||||
}
|
||||
|
||||
const insertWorkspaceAgentStartupLogs = `-- name: InsertWorkspaceAgentStartupLogs :many
|
||||
WITH new_length AS (
|
||||
UPDATE workspace_agents SET
|
||||
startup_logs_length = startup_logs_length + $5 WHERE workspace_agents.id = $1
|
||||
)
|
||||
INSERT INTO
|
||||
workspace_agent_startup_logs (agent_id, created_at, output, level)
|
||||
SELECT
|
||||
$1 :: uuid AS agent_id,
|
||||
unnest($2 :: timestamptz [ ]) AS created_at,
|
||||
unnest($3 :: VARCHAR(1024) [ ]) AS output,
|
||||
unnest($4 :: log_level [ ]) AS level
|
||||
RETURNING workspace_agent_startup_logs.agent_id, workspace_agent_startup_logs.created_at, workspace_agent_startup_logs.output, workspace_agent_startup_logs.id, workspace_agent_startup_logs.level
|
||||
`
|
||||
|
||||
type InsertWorkspaceAgentStartupLogsParams struct {
|
||||
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
|
||||
CreatedAt []time.Time `db:"created_at" json:"created_at"`
|
||||
Output []string `db:"output" json:"output"`
|
||||
Level []LogLevel `db:"level" json:"level"`
|
||||
OutputLength int32 `db:"output_length" json:"output_length"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) InsertWorkspaceAgentStartupLogs(ctx context.Context, arg InsertWorkspaceAgentStartupLogsParams) ([]WorkspaceAgentStartupLog, error) {
|
||||
rows, err := q.db.QueryContext(ctx, insertWorkspaceAgentStartupLogs,
|
||||
arg.AgentID,
|
||||
pq.Array(arg.CreatedAt),
|
||||
pq.Array(arg.Output),
|
||||
pq.Array(arg.Level),
|
||||
arg.OutputLength,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []WorkspaceAgentStartupLog
|
||||
for rows.Next() {
|
||||
var i WorkspaceAgentStartupLog
|
||||
if err := rows.Scan(
|
||||
&i.AgentID,
|
||||
&i.CreatedAt,
|
||||
&i.Output,
|
||||
&i.ID,
|
||||
&i.Level,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updateWorkspaceAgentConnectionByID = `-- name: UpdateWorkspaceAgentConnectionByID :exec
|
||||
UPDATE
|
||||
workspace_agents
|
||||
|
@ -6669,6 +6674,25 @@ func (q *sqlQuerier) UpdateWorkspaceAgentLifecycleStateByID(ctx context.Context,
|
|||
return err
|
||||
}
|
||||
|
||||
const updateWorkspaceAgentLogOverflowByID = `-- name: UpdateWorkspaceAgentLogOverflowByID :exec
|
||||
UPDATE
|
||||
workspace_agents
|
||||
SET
|
||||
logs_overflowed = $2
|
||||
WHERE
|
||||
id = $1
|
||||
`
|
||||
|
||||
type UpdateWorkspaceAgentLogOverflowByIDParams struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
LogsOverflowed bool `db:"logs_overflowed" json:"logs_overflowed"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) UpdateWorkspaceAgentLogOverflowByID(ctx context.Context, arg UpdateWorkspaceAgentLogOverflowByIDParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateWorkspaceAgentLogOverflowByID, arg.ID, arg.LogsOverflowed)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateWorkspaceAgentMetadata = `-- name: UpdateWorkspaceAgentMetadata :exec
|
||||
UPDATE
|
||||
workspace_agent_metadata
|
||||
|
@ -6728,25 +6752,6 @@ func (q *sqlQuerier) UpdateWorkspaceAgentStartupByID(ctx context.Context, arg Up
|
|||
return err
|
||||
}
|
||||
|
||||
const updateWorkspaceAgentStartupLogOverflowByID = `-- name: UpdateWorkspaceAgentStartupLogOverflowByID :exec
|
||||
UPDATE
|
||||
workspace_agents
|
||||
SET
|
||||
startup_logs_overflowed = $2
|
||||
WHERE
|
||||
id = $1
|
||||
`
|
||||
|
||||
type UpdateWorkspaceAgentStartupLogOverflowByIDParams struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
StartupLogsOverflowed bool `db:"startup_logs_overflowed" json:"startup_logs_overflowed"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) UpdateWorkspaceAgentStartupLogOverflowByID(ctx context.Context, arg UpdateWorkspaceAgentStartupLogOverflowByIDParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateWorkspaceAgentStartupLogOverflowByID, arg.ID, arg.StartupLogsOverflowed)
|
||||
return err
|
||||
}
|
||||
|
||||
const deleteOldWorkspaceAgentStats = `-- name: DeleteOldWorkspaceAgentStats :exec
|
||||
DELETE FROM workspace_agent_stats WHERE created_at < NOW() - INTERVAL '30 days'
|
||||
`
|
||||
|
|
|
@ -140,43 +140,44 @@ FROM
|
|||
WHERE
|
||||
workspace_agent_id = $1;
|
||||
|
||||
-- name: UpdateWorkspaceAgentStartupLogOverflowByID :exec
|
||||
-- name: UpdateWorkspaceAgentLogOverflowByID :exec
|
||||
UPDATE
|
||||
workspace_agents
|
||||
SET
|
||||
startup_logs_overflowed = $2
|
||||
logs_overflowed = $2
|
||||
WHERE
|
||||
id = $1;
|
||||
|
||||
-- name: GetWorkspaceAgentStartupLogsAfter :many
|
||||
-- name: GetWorkspaceAgentLogsAfter :many
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
workspace_agent_startup_logs
|
||||
workspace_agent_logs
|
||||
WHERE
|
||||
agent_id = $1
|
||||
AND (
|
||||
id > @created_after
|
||||
) ORDER BY id ASC;
|
||||
|
||||
-- name: InsertWorkspaceAgentStartupLogs :many
|
||||
-- name: InsertWorkspaceAgentLogs :many
|
||||
WITH new_length AS (
|
||||
UPDATE workspace_agents SET
|
||||
startup_logs_length = startup_logs_length + @output_length WHERE workspace_agents.id = @agent_id
|
||||
logs_length = logs_length + @output_length WHERE workspace_agents.id = @agent_id
|
||||
)
|
||||
INSERT INTO
|
||||
workspace_agent_startup_logs (agent_id, created_at, output, level)
|
||||
workspace_agent_logs (agent_id, created_at, output, level, source)
|
||||
SELECT
|
||||
@agent_id :: uuid AS agent_id,
|
||||
unnest(@created_at :: timestamptz [ ]) AS created_at,
|
||||
unnest(@output :: VARCHAR(1024) [ ]) AS output,
|
||||
unnest(@level :: log_level [ ]) AS level
|
||||
RETURNING workspace_agent_startup_logs.*;
|
||||
unnest(@level :: log_level [ ]) AS level,
|
||||
unnest(@source :: workspace_agent_log_source [ ]) AS source
|
||||
RETURNING workspace_agent_logs.*;
|
||||
|
||||
-- If an agent hasn't connected in the last 7 days, we purge it's logs.
|
||||
-- Logs can take up a lot of space, so it's important we clean up frequently.
|
||||
-- name: DeleteOldWorkspaceAgentStartupLogs :exec
|
||||
DELETE FROM workspace_agent_startup_logs WHERE agent_id IN
|
||||
-- name: DeleteOldWorkspaceAgentLogs :exec
|
||||
DELETE FROM workspace_agent_logs WHERE agent_id IN
|
||||
(SELECT id FROM workspace_agents WHERE last_connected_at IS NOT NULL
|
||||
AND last_connected_at < NOW() - INTERVAL '7 day');
|
||||
|
||||
|
|
|
@ -27,3 +27,32 @@ func templateVersionParametersDeprecated(rw http.ResponseWriter, r *http.Request
|
|||
func templateVersionSchemaDeprecated(rw http.ResponseWriter, r *http.Request) {
|
||||
httpapi.Write(r.Context(), rw, http.StatusOK, []struct{}{})
|
||||
}
|
||||
|
||||
// @Summary Removed: Patch workspace agent logs
|
||||
// @ID removed-patch-workspace-agent-logs
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Agents
|
||||
// @Param request body agentsdk.PatchLogs true "logs"
|
||||
// @Success 200 {object} codersdk.Response
|
||||
// @Router /workspaceagents/me/startup-logs [patch]
|
||||
func (api *API) patchWorkspaceAgentLogsDeprecated(rw http.ResponseWriter, r *http.Request) {
|
||||
api.patchWorkspaceAgentLogs(rw, r)
|
||||
}
|
||||
|
||||
// @Summary Removed: Get logs by workspace agent
|
||||
// @ID removed-get-logs-by-workspace-agent
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Agents
|
||||
// @Param workspaceagent path string true "Workspace agent ID" format(uuid)
|
||||
// @Param before query int false "Before log id"
|
||||
// @Param after query int false "After log id"
|
||||
// @Param follow query bool false "Follow log stream"
|
||||
// @Param no_compression query bool false "Disable compression for WebSocket connection"
|
||||
// @Success 200 {array} codersdk.WorkspaceAgentLog
|
||||
// @Router /workspaceagents/{workspaceagent}/startup-logs [get]
|
||||
func (api *API) workspaceAgentLogsDeprecated(rw http.ResponseWriter, r *http.Request) {
|
||||
api.workspaceAgentLogs(rw, r)
|
||||
}
|
||||
|
|
|
@ -234,20 +234,20 @@ func (api *API) postWorkspaceAgentStartup(rw http.ResponseWriter, r *http.Reques
|
|||
httpapi.Write(ctx, rw, http.StatusOK, nil)
|
||||
}
|
||||
|
||||
// @Summary Patch workspace agent startup logs
|
||||
// @ID patch-workspace-agent-startup-logs
|
||||
// @Summary Patch workspace agent logs
|
||||
// @ID patch-workspace-agent-logs
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Agents
|
||||
// @Param request body agentsdk.PatchStartupLogs true "Startup logs"
|
||||
// @Param request body agentsdk.PatchLogs true "logs"
|
||||
// @Success 200 {object} codersdk.Response
|
||||
// @Router /workspaceagents/me/startup-logs [patch]
|
||||
func (api *API) patchWorkspaceAgentStartupLogs(rw http.ResponseWriter, r *http.Request) {
|
||||
// @Router /workspaceagents/me/logs [patch]
|
||||
func (api *API) patchWorkspaceAgentLogs(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
workspaceAgent := httpmw.WorkspaceAgent(r)
|
||||
|
||||
var req agentsdk.PatchStartupLogs
|
||||
var req agentsdk.PatchLogs
|
||||
if !httpapi.Read(ctx, rw, r, &req) {
|
||||
return
|
||||
}
|
||||
|
@ -260,6 +260,7 @@ func (api *API) patchWorkspaceAgentStartupLogs(rw http.ResponseWriter, r *http.R
|
|||
createdAt := make([]time.Time, 0)
|
||||
output := make([]string, 0)
|
||||
level := make([]database.LogLevel, 0)
|
||||
source := make([]database.WorkspaceAgentLogSource, 0)
|
||||
outputLength := 0
|
||||
for _, logEntry := range req.Logs {
|
||||
createdAt = append(createdAt, logEntry.CreatedAt)
|
||||
|
@ -278,39 +279,54 @@ func (api *API) patchWorkspaceAgentStartupLogs(rw http.ResponseWriter, r *http.R
|
|||
return
|
||||
}
|
||||
level = append(level, parsedLevel)
|
||||
|
||||
if logEntry.Source == "" {
|
||||
// Default to "startup_script" to support older agents that didn't have the source field.
|
||||
logEntry.Source = codersdk.WorkspaceAgentLogSourceStartupScript
|
||||
}
|
||||
parsedSource := database.WorkspaceAgentLogSource(logEntry.Source)
|
||||
if !parsedSource.Valid() {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Invalid log source provided.",
|
||||
Detail: fmt.Sprintf("invalid log source: %q", logEntry.Source),
|
||||
})
|
||||
return
|
||||
}
|
||||
source = append(source, parsedSource)
|
||||
}
|
||||
|
||||
logs, err := api.Database.InsertWorkspaceAgentStartupLogs(ctx, database.InsertWorkspaceAgentStartupLogsParams{
|
||||
logs, err := api.Database.InsertWorkspaceAgentLogs(ctx, database.InsertWorkspaceAgentLogsParams{
|
||||
AgentID: workspaceAgent.ID,
|
||||
CreatedAt: createdAt,
|
||||
Output: output,
|
||||
Level: level,
|
||||
Source: source,
|
||||
OutputLength: int32(outputLength),
|
||||
})
|
||||
if err != nil {
|
||||
if !database.IsStartupLogsLimitError(err) {
|
||||
if !database.IsWorkspaceAgentLogsLimitError(err) {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Failed to upload startup logs",
|
||||
Message: "Failed to upload logs",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
if workspaceAgent.StartupLogsOverflowed {
|
||||
if workspaceAgent.LogsOverflowed {
|
||||
httpapi.Write(ctx, rw, http.StatusRequestEntityTooLarge, codersdk.Response{
|
||||
Message: "Startup logs limit exceeded",
|
||||
Message: "Logs limit exceeded",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
err := api.Database.UpdateWorkspaceAgentStartupLogOverflowByID(ctx, database.UpdateWorkspaceAgentStartupLogOverflowByIDParams{
|
||||
ID: workspaceAgent.ID,
|
||||
StartupLogsOverflowed: true,
|
||||
err := api.Database.UpdateWorkspaceAgentLogOverflowByID(ctx, database.UpdateWorkspaceAgentLogOverflowByIDParams{
|
||||
ID: workspaceAgent.ID,
|
||||
LogsOverflowed: true,
|
||||
})
|
||||
if err != nil {
|
||||
// We don't want to return here, because the agent will retry
|
||||
// on failure and this isn't a huge deal. The overflow state
|
||||
// is just a hint to the user that the logs are incomplete.
|
||||
api.Logger.Warn(ctx, "failed to update workspace agent startup log overflow", slog.Error(err))
|
||||
api.Logger.Warn(ctx, "failed to update workspace agent log overflow", slog.Error(err))
|
||||
}
|
||||
|
||||
resource, err := api.Database.GetWorkspaceResourceByID(ctx, workspaceAgent.ResourceID)
|
||||
|
@ -334,7 +350,7 @@ func (api *API) patchWorkspaceAgentStartupLogs(rw http.ResponseWriter, r *http.R
|
|||
api.publishWorkspaceUpdate(ctx, build.WorkspaceID)
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusRequestEntityTooLarge, codersdk.Response{
|
||||
Message: "Startup logs limit exceeded",
|
||||
Message: "Logs limit exceeded",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
@ -343,11 +359,11 @@ func (api *API) patchWorkspaceAgentStartupLogs(rw http.ResponseWriter, r *http.R
|
|||
|
||||
// Publish by the lowest log ID inserted so the
|
||||
// log stream will fetch everything from that point.
|
||||
api.publishWorkspaceAgentStartupLogsUpdate(ctx, workspaceAgent.ID, agentsdk.StartupLogsNotifyMessage{
|
||||
api.publishWorkspaceAgentLogsUpdate(ctx, workspaceAgent.ID, agentsdk.LogsNotifyMessage{
|
||||
CreatedAfter: lowestLogID - 1,
|
||||
})
|
||||
|
||||
if workspaceAgent.StartupLogsLength == 0 {
|
||||
if workspaceAgent.LogsLength == 0 {
|
||||
// If these are the first logs being appended, we publish a UI update
|
||||
// to notify the UI that logs are now available.
|
||||
resource, err := api.Database.GetWorkspaceResourceByID(ctx, workspaceAgent.ResourceID)
|
||||
|
@ -374,11 +390,10 @@ func (api *API) patchWorkspaceAgentStartupLogs(rw http.ResponseWriter, r *http.R
|
|||
httpapi.Write(ctx, rw, http.StatusOK, nil)
|
||||
}
|
||||
|
||||
// workspaceAgentStartupLogs returns the logs sent from a workspace agent
|
||||
// during startup.
|
||||
// workspaceAgentLogs returns the logs associated with a workspace agent
|
||||
//
|
||||
// @Summary Get startup logs by workspace agent
|
||||
// @ID get-startup-logs-by-workspace-agent
|
||||
// @Summary Get logs by workspace agent
|
||||
// @ID get-logs-by-workspace-agent
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Agents
|
||||
|
@ -387,9 +402,9 @@ func (api *API) patchWorkspaceAgentStartupLogs(rw http.ResponseWriter, r *http.R
|
|||
// @Param after query int false "After log id"
|
||||
// @Param follow query bool false "Follow log stream"
|
||||
// @Param no_compression query bool false "Disable compression for WebSocket connection"
|
||||
// @Success 200 {array} codersdk.WorkspaceAgentStartupLog
|
||||
// @Router /workspaceagents/{workspaceagent}/startup-logs [get]
|
||||
func (api *API) workspaceAgentStartupLogs(rw http.ResponseWriter, r *http.Request) {
|
||||
// @Success 200 {array} codersdk.WorkspaceAgentLog
|
||||
// @Router /workspaceagents/{workspaceagent}/logs [get]
|
||||
func (api *API) workspaceAgentLogs(rw http.ResponseWriter, r *http.Request) {
|
||||
// This mostly copies how provisioner job logs are streamed!
|
||||
var (
|
||||
ctx = r.Context()
|
||||
|
@ -416,7 +431,7 @@ func (api *API) workspaceAgentStartupLogs(rw http.ResponseWriter, r *http.Reques
|
|||
}
|
||||
}
|
||||
|
||||
logs, err := api.Database.GetWorkspaceAgentStartupLogsAfter(ctx, database.GetWorkspaceAgentStartupLogsAfterParams{
|
||||
logs, err := api.Database.GetWorkspaceAgentLogsAfter(ctx, database.GetWorkspaceAgentLogsAfterParams{
|
||||
AgentID: workspaceAgent.ID,
|
||||
CreatedAfter: after,
|
||||
})
|
||||
|
@ -431,11 +446,11 @@ func (api *API) workspaceAgentStartupLogs(rw http.ResponseWriter, r *http.Reques
|
|||
return
|
||||
}
|
||||
if logs == nil {
|
||||
logs = []database.WorkspaceAgentStartupLog{}
|
||||
logs = []database.WorkspaceAgentLog{}
|
||||
}
|
||||
|
||||
if !follow {
|
||||
httpapi.Write(ctx, rw, http.StatusOK, convertWorkspaceAgentStartupLogs(logs))
|
||||
httpapi.Write(ctx, rw, http.StatusOK, convertWorkspaceAgentLogs(logs))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -472,7 +487,7 @@ func (api *API) workspaceAgentStartupLogs(rw http.ResponseWriter, r *http.Reques
|
|||
|
||||
// The Go stdlib JSON encoder appends a newline character after message write.
|
||||
encoder := json.NewEncoder(wsNetConn)
|
||||
err = encoder.Encode(convertWorkspaceAgentStartupLogs(logs))
|
||||
err = encoder.Encode(convertWorkspaceAgentLogs(logs))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -488,7 +503,7 @@ func (api *API) workspaceAgentStartupLogs(rw http.ResponseWriter, r *http.Reques
|
|||
notifyCh <- struct{}{}
|
||||
|
||||
// Subscribe early to prevent missing log events.
|
||||
closeSubscribe, err := api.Pubsub.Subscribe(agentsdk.StartupLogsNotifyChannel(workspaceAgent.ID), func(_ context.Context, _ []byte) {
|
||||
closeSubscribe, err := api.Pubsub.Subscribe(agentsdk.LogsNotifyChannel(workspaceAgent.ID), func(_ context.Context, _ []byte) {
|
||||
// The message is not important, we're tracking lastSentLogID manually.
|
||||
select {
|
||||
case notifyCh <- struct{}{}:
|
||||
|
@ -497,7 +512,7 @@ func (api *API) workspaceAgentStartupLogs(rw http.ResponseWriter, r *http.Reques
|
|||
})
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Failed to subscribe to startup logs.",
|
||||
Message: "Failed to subscribe to logs.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
|
@ -505,7 +520,7 @@ func (api *API) workspaceAgentStartupLogs(rw http.ResponseWriter, r *http.Reques
|
|||
defer closeSubscribe()
|
||||
|
||||
// Buffer size controls the log prefetch capacity.
|
||||
bufferedLogs := make(chan []database.WorkspaceAgentStartupLog, 8)
|
||||
bufferedLogs := make(chan []database.WorkspaceAgentLog, 8)
|
||||
// Check at least once per minute in case we didn't receive a pubsub message.
|
||||
recheckInterval := time.Minute
|
||||
t := time.NewTicker(recheckInterval)
|
||||
|
@ -523,7 +538,7 @@ func (api *API) workspaceAgentStartupLogs(rw http.ResponseWriter, r *http.Reques
|
|||
t.Reset(recheckInterval)
|
||||
}
|
||||
|
||||
logs, err := api.Database.GetWorkspaceAgentStartupLogsAfter(ctx, database.GetWorkspaceAgentStartupLogsAfterParams{
|
||||
logs, err := api.Database.GetWorkspaceAgentLogsAfter(ctx, database.GetWorkspaceAgentLogsAfterParams{
|
||||
AgentID: workspaceAgent.ID,
|
||||
CreatedAfter: lastSentLogID,
|
||||
})
|
||||
|
@ -531,7 +546,7 @@ func (api *API) workspaceAgentStartupLogs(rw http.ResponseWriter, r *http.Reques
|
|||
if xerrors.Is(err, context.Canceled) {
|
||||
return
|
||||
}
|
||||
logger.Warn(ctx, "failed to get workspace agent startup logs after", slog.Error(err))
|
||||
logger.Warn(ctx, "failed to get workspace agent logs after", slog.Error(err))
|
||||
continue
|
||||
}
|
||||
if len(logs) == 0 {
|
||||
|
@ -569,7 +584,7 @@ func (api *API) workspaceAgentStartupLogs(rw http.ResponseWriter, r *http.Reques
|
|||
}
|
||||
return
|
||||
}
|
||||
err = encoder.Encode(convertWorkspaceAgentStartupLogs(logs))
|
||||
err = encoder.Encode(convertWorkspaceAgentLogs(logs))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -1244,8 +1259,8 @@ func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordin
|
|||
StartupScript: dbAgent.StartupScript.String,
|
||||
StartupScriptBehavior: codersdk.WorkspaceAgentStartupScriptBehavior(dbAgent.StartupScriptBehavior),
|
||||
StartupScriptTimeoutSeconds: dbAgent.StartupScriptTimeoutSeconds,
|
||||
StartupLogsLength: dbAgent.StartupLogsLength,
|
||||
StartupLogsOverflowed: dbAgent.StartupLogsOverflowed,
|
||||
LogsLength: dbAgent.LogsLength,
|
||||
LogsOverflowed: dbAgent.LogsOverflowed,
|
||||
Version: dbAgent.Version,
|
||||
EnvironmentVariables: envs,
|
||||
Directory: dbAgent.Directory,
|
||||
|
@ -2076,16 +2091,16 @@ func websocketNetConn(ctx context.Context, conn *websocket.Conn, msgType websock
|
|||
}
|
||||
}
|
||||
|
||||
func convertWorkspaceAgentStartupLogs(logs []database.WorkspaceAgentStartupLog) []codersdk.WorkspaceAgentStartupLog {
|
||||
sdk := make([]codersdk.WorkspaceAgentStartupLog, 0, len(logs))
|
||||
func convertWorkspaceAgentLogs(logs []database.WorkspaceAgentLog) []codersdk.WorkspaceAgentLog {
|
||||
sdk := make([]codersdk.WorkspaceAgentLog, 0, len(logs))
|
||||
for _, logEntry := range logs {
|
||||
sdk = append(sdk, convertWorkspaceAgentStartupLog(logEntry))
|
||||
sdk = append(sdk, convertWorkspaceAgentLog(logEntry))
|
||||
}
|
||||
return sdk
|
||||
}
|
||||
|
||||
func convertWorkspaceAgentStartupLog(logEntry database.WorkspaceAgentStartupLog) codersdk.WorkspaceAgentStartupLog {
|
||||
return codersdk.WorkspaceAgentStartupLog{
|
||||
func convertWorkspaceAgentLog(logEntry database.WorkspaceAgentLog) codersdk.WorkspaceAgentLog {
|
||||
return codersdk.WorkspaceAgentLog{
|
||||
ID: logEntry.ID,
|
||||
CreatedAt: logEntry.CreatedAt,
|
||||
Output: logEntry.Output,
|
||||
|
|
|
@ -213,8 +213,8 @@ func TestWorkspaceAgentStartupLogs(t *testing.T) {
|
|||
|
||||
agentClient := agentsdk.New(client.URL)
|
||||
agentClient.SetSessionToken(authToken)
|
||||
err := agentClient.PatchStartupLogs(ctx, agentsdk.PatchStartupLogs{
|
||||
Logs: []agentsdk.StartupLog{
|
||||
err := agentClient.PatchLogs(ctx, agentsdk.PatchLogs{
|
||||
Logs: []agentsdk.Log{
|
||||
{
|
||||
CreatedAt: database.Now(),
|
||||
Output: "testing",
|
||||
|
@ -227,12 +227,12 @@ func TestWorkspaceAgentStartupLogs(t *testing.T) {
|
|||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
logs, closer, err := client.WorkspaceAgentStartupLogsAfter(ctx, build.Resources[0].Agents[0].ID, 0, true)
|
||||
logs, closer, err := client.WorkspaceAgentLogsAfter(ctx, build.Resources[0].Agents[0].ID, 0, true)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
_ = closer.Close()
|
||||
}()
|
||||
var logChunk []codersdk.WorkspaceAgentStartupLog
|
||||
var logChunk []codersdk.WorkspaceAgentLog
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case logChunk = <-logs:
|
||||
|
@ -280,8 +280,8 @@ func TestWorkspaceAgentStartupLogs(t *testing.T) {
|
|||
|
||||
agentClient := agentsdk.New(client.URL)
|
||||
agentClient.SetSessionToken(authToken)
|
||||
err = agentClient.PatchStartupLogs(ctx, agentsdk.PatchStartupLogs{
|
||||
Logs: []agentsdk.StartupLog{{
|
||||
err = agentClient.PatchLogs(ctx, agentsdk.PatchLogs{
|
||||
Logs: []agentsdk.Log{{
|
||||
CreatedAt: database.Now(),
|
||||
Output: strings.Repeat("a", (1<<20)+1),
|
||||
}},
|
||||
|
@ -299,7 +299,7 @@ func TestWorkspaceAgentStartupLogs(t *testing.T) {
|
|||
t.FailNow()
|
||||
case update = <-updates:
|
||||
}
|
||||
if update.LatestBuild.Resources[0].Agents[0].StartupLogsOverflowed {
|
||||
if update.LatestBuild.Resources[0].Agents[0].LogsOverflowed {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1251,13 +1251,13 @@ func (api *API) publishWorkspaceUpdate(ctx context.Context, workspaceID uuid.UUI
|
|||
}
|
||||
}
|
||||
|
||||
func (api *API) publishWorkspaceAgentStartupLogsUpdate(ctx context.Context, workspaceAgentID uuid.UUID, m agentsdk.StartupLogsNotifyMessage) {
|
||||
func (api *API) publishWorkspaceAgentLogsUpdate(ctx context.Context, workspaceAgentID uuid.UUID, m agentsdk.LogsNotifyMessage) {
|
||||
b, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
api.Logger.Warn(ctx, "failed to marshal startup logs notify message", slog.F("workspace_agent_id", workspaceAgentID), slog.Error(err))
|
||||
api.Logger.Warn(ctx, "failed to marshal logs notify message", slog.F("workspace_agent_id", workspaceAgentID), slog.Error(err))
|
||||
}
|
||||
err = api.Pubsub.Publish(agentsdk.StartupLogsNotifyChannel(workspaceAgentID), b)
|
||||
err = api.Pubsub.Publish(agentsdk.LogsNotifyChannel(workspaceAgentID), b)
|
||||
if err != nil {
|
||||
api.Logger.Warn(ctx, "failed to publish workspace agent startup logs update", slog.F("workspace_agent_id", workspaceAgentID), slog.Error(err))
|
||||
api.Logger.Warn(ctx, "failed to publish workspace agent logs update", slog.F("workspace_agent_id", workspaceAgentID), slog.Error(err))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -274,7 +274,7 @@ func (*client) PostStartup(_ context.Context, _ agentsdk.PostStartupRequest) err
|
|||
return nil
|
||||
}
|
||||
|
||||
func (*client) PatchStartupLogs(_ context.Context, _ agentsdk.PatchStartupLogs) error {
|
||||
func (*client) PatchLogs(_ context.Context, _ agentsdk.PatchLogs) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -621,20 +621,21 @@ func (c *Client) PostStartup(ctx context.Context, req PostStartupRequest) error
|
|||
return nil
|
||||
}
|
||||
|
||||
type StartupLog struct {
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Output string `json:"output"`
|
||||
Level codersdk.LogLevel `json:"level"`
|
||||
type Log struct {
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Output string `json:"output"`
|
||||
Level codersdk.LogLevel `json:"level"`
|
||||
Source codersdk.WorkspaceAgentLogSource `json:"source"`
|
||||
}
|
||||
|
||||
type PatchStartupLogs struct {
|
||||
Logs []StartupLog `json:"logs"`
|
||||
type PatchLogs struct {
|
||||
Logs []Log `json:"logs"`
|
||||
}
|
||||
|
||||
// PatchStartupLogs writes log messages to the agent startup script.
|
||||
// PatchLogs writes log messages to the agent startup script.
|
||||
// Log messages are limited to 1MB in total.
|
||||
func (c *Client) PatchStartupLogs(ctx context.Context, req PatchStartupLogs) error {
|
||||
res, err := c.SDK.Request(ctx, http.MethodPatch, "/api/v2/workspaceagents/me/startup-logs", req)
|
||||
func (c *Client) PatchLogs(ctx context.Context, req PatchLogs) error {
|
||||
res, err := c.SDK.Request(ctx, http.MethodPatch, "/api/v2/workspaceagents/me/logs", req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -737,13 +738,13 @@ func websocketNetConn(ctx context.Context, conn *websocket.Conn, msgType websock
|
|||
}
|
||||
}
|
||||
|
||||
// StartupLogsNotifyChannel returns the channel name responsible for notifying
|
||||
// of new startup logs.
|
||||
func StartupLogsNotifyChannel(agentID uuid.UUID) string {
|
||||
return fmt.Sprintf("startup-logs:%s", agentID)
|
||||
// LogsNotifyChannel returns the channel name responsible for notifying
|
||||
// of new logs.
|
||||
func LogsNotifyChannel(agentID uuid.UUID) string {
|
||||
return fmt.Sprintf("agent-logs:%s", agentID)
|
||||
}
|
||||
|
||||
type StartupLogsNotifyMessage struct {
|
||||
type LogsNotifyMessage struct {
|
||||
CreatedAfter int64 `json:"created_after"`
|
||||
}
|
||||
|
||||
|
|
|
@ -16,10 +16,11 @@ import (
|
|||
)
|
||||
|
||||
type startupLogsWriter struct {
|
||||
buf bytes.Buffer // Buffer to track partial lines.
|
||||
ctx context.Context
|
||||
send func(ctx context.Context, log ...StartupLog) error
|
||||
level codersdk.LogLevel
|
||||
buf bytes.Buffer // Buffer to track partial lines.
|
||||
ctx context.Context
|
||||
send func(ctx context.Context, log ...Log) error
|
||||
level codersdk.LogLevel
|
||||
source codersdk.WorkspaceAgentLogSource
|
||||
}
|
||||
|
||||
func (w *startupLogsWriter) Write(p []byte) (int, error) {
|
||||
|
@ -39,10 +40,11 @@ func (w *startupLogsWriter) Write(p []byte) (int, error) {
|
|||
partial = w.buf.Bytes()
|
||||
w.buf.Reset()
|
||||
}
|
||||
err := w.send(w.ctx, StartupLog{
|
||||
err := w.send(w.ctx, Log{
|
||||
CreatedAt: time.Now().UTC(), // UTC, like database.Now().
|
||||
Level: w.level,
|
||||
Output: string(partial) + string(p[:nl-cr]),
|
||||
Source: w.source,
|
||||
})
|
||||
if err != nil {
|
||||
return n - len(p), err
|
||||
|
@ -61,10 +63,11 @@ func (w *startupLogsWriter) Write(p []byte) (int, error) {
|
|||
func (w *startupLogsWriter) Close() error {
|
||||
if w.buf.Len() > 0 {
|
||||
defer w.buf.Reset()
|
||||
return w.send(w.ctx, StartupLog{
|
||||
return w.send(w.ctx, Log{
|
||||
CreatedAt: time.Now().UTC(), // UTC, like database.Now().
|
||||
Level: w.level,
|
||||
Output: w.buf.String(),
|
||||
Source: w.source,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
|
@ -78,20 +81,24 @@ func (w *startupLogsWriter) Close() error {
|
|||
//
|
||||
// Neither Write nor Close is safe for concurrent use and must be used
|
||||
// by a single goroutine.
|
||||
func StartupLogsWriter(ctx context.Context, sender func(ctx context.Context, log ...StartupLog) error, level codersdk.LogLevel) io.WriteCloser {
|
||||
func StartupLogsWriter(ctx context.Context, sender func(ctx context.Context, log ...Log) error, source codersdk.WorkspaceAgentLogSource, level codersdk.LogLevel) io.WriteCloser {
|
||||
if source == "" {
|
||||
source = codersdk.WorkspaceAgentLogSourceExternal
|
||||
}
|
||||
return &startupLogsWriter{
|
||||
ctx: ctx,
|
||||
send: sender,
|
||||
level: level,
|
||||
ctx: ctx,
|
||||
send: sender,
|
||||
level: level,
|
||||
source: source,
|
||||
}
|
||||
}
|
||||
|
||||
// SendStartupLogs will send agent startup logs to the server. Calls to
|
||||
// LogsSender will send agent startup logs to the server. Calls to
|
||||
// sendLog are non-blocking and will return an error if flushAndClose
|
||||
// has been called. Calling sendLog concurrently is not supported. If
|
||||
// the context passed to flushAndClose is canceled, any remaining logs
|
||||
// will be discarded.
|
||||
func StartupLogsSender(patchStartupLogs func(ctx context.Context, req PatchStartupLogs) error, logger slog.Logger) (sendLog func(ctx context.Context, log ...StartupLog) error, flushAndClose func(context.Context) error) {
|
||||
func LogsSender(patchLogs func(ctx context.Context, req PatchLogs) error, logger slog.Logger) (sendLog func(ctx context.Context, log ...Log) error, flushAndClose func(context.Context) error) {
|
||||
// The main context is used to close the sender goroutine and cancel
|
||||
// any outbound requests to the API. The shutdown context is used to
|
||||
// signal the sender goroutine to flush logs and then exit.
|
||||
|
@ -100,7 +107,7 @@ func StartupLogsSender(patchStartupLogs func(ctx context.Context, req PatchStart
|
|||
|
||||
// Synchronous sender, there can only be one outbound send at a time.
|
||||
sendDone := make(chan struct{})
|
||||
send := make(chan []StartupLog, 1)
|
||||
send := make(chan []Log, 1)
|
||||
go func() {
|
||||
// Set flushTimeout and backlogLimit so that logs are uploaded
|
||||
// once every 250ms or when 100 logs have been added to the
|
||||
|
@ -110,7 +117,7 @@ func StartupLogsSender(patchStartupLogs func(ctx context.Context, req PatchStart
|
|||
|
||||
flush := time.NewTicker(flushTimeout)
|
||||
|
||||
var backlog []StartupLog
|
||||
var backlog []Log
|
||||
defer func() {
|
||||
flush.Stop()
|
||||
if len(backlog) > 0 {
|
||||
|
@ -150,7 +157,7 @@ func StartupLogsSender(patchStartupLogs func(ctx context.Context, req PatchStart
|
|||
// meaning these requests won't be interrupted by
|
||||
// shutdown.
|
||||
for r := retry.New(time.Second, 5*time.Second); r.Wait(ctx); {
|
||||
err := patchStartupLogs(ctx, PatchStartupLogs{
|
||||
err := patchLogs(ctx, PatchLogs{
|
||||
Logs: backlog,
|
||||
})
|
||||
if err == nil {
|
||||
|
@ -185,8 +192,8 @@ func StartupLogsSender(patchStartupLogs func(ctx context.Context, req PatchStart
|
|||
}
|
||||
}()
|
||||
|
||||
var queue []StartupLog
|
||||
sendLog = func(callCtx context.Context, log ...StartupLog) error {
|
||||
var queue []Log
|
||||
sendLog = func(callCtx context.Context, log ...Log) error {
|
||||
select {
|
||||
case <-shutdownCtx.Done():
|
||||
return xerrors.Errorf("closed: %w", shutdownCtx.Err())
|
||||
|
|
|
@ -28,8 +28,9 @@ func TestStartupLogsWriter_Write(t *testing.T) {
|
|||
name string
|
||||
ctx context.Context
|
||||
level codersdk.LogLevel
|
||||
source codersdk.WorkspaceAgentLogSource
|
||||
writes []string
|
||||
want []agentsdk.StartupLog
|
||||
want []agentsdk.Log
|
||||
wantErr bool
|
||||
closeFirst bool
|
||||
}{
|
||||
|
@ -38,10 +39,12 @@ func TestStartupLogsWriter_Write(t *testing.T) {
|
|||
ctx: context.Background(),
|
||||
level: codersdk.LogLevelInfo,
|
||||
writes: []string{"hello world\n"},
|
||||
want: []agentsdk.StartupLog{
|
||||
source: codersdk.WorkspaceAgentLogSourceShutdownScript,
|
||||
want: []agentsdk.Log{
|
||||
{
|
||||
Level: codersdk.LogLevelInfo,
|
||||
Output: "hello world",
|
||||
Source: codersdk.WorkspaceAgentLogSourceShutdownScript,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -50,14 +53,16 @@ func TestStartupLogsWriter_Write(t *testing.T) {
|
|||
ctx: context.Background(),
|
||||
level: codersdk.LogLevelInfo,
|
||||
writes: []string{"hello world\n", "goodbye world\n"},
|
||||
want: []agentsdk.StartupLog{
|
||||
want: []agentsdk.Log{
|
||||
{
|
||||
Level: codersdk.LogLevelInfo,
|
||||
Output: "hello world",
|
||||
Source: codersdk.WorkspaceAgentLogSourceExternal,
|
||||
},
|
||||
{
|
||||
Level: codersdk.LogLevelInfo,
|
||||
Output: "goodbye world",
|
||||
Source: codersdk.WorkspaceAgentLogSourceExternal,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -66,30 +71,36 @@ func TestStartupLogsWriter_Write(t *testing.T) {
|
|||
ctx: context.Background(),
|
||||
level: codersdk.LogLevelInfo,
|
||||
writes: []string{"\n\n", "hello world\n\n\n", "goodbye world\n"},
|
||||
want: []agentsdk.StartupLog{
|
||||
want: []agentsdk.Log{
|
||||
{
|
||||
Level: codersdk.LogLevelInfo,
|
||||
Output: "",
|
||||
Source: codersdk.WorkspaceAgentLogSourceExternal,
|
||||
},
|
||||
{
|
||||
Level: codersdk.LogLevelInfo,
|
||||
Output: "",
|
||||
Source: codersdk.WorkspaceAgentLogSourceExternal,
|
||||
},
|
||||
{
|
||||
Level: codersdk.LogLevelInfo,
|
||||
Output: "hello world",
|
||||
Source: codersdk.WorkspaceAgentLogSourceExternal,
|
||||
},
|
||||
{
|
||||
Level: codersdk.LogLevelInfo,
|
||||
Output: "",
|
||||
Source: codersdk.WorkspaceAgentLogSourceExternal,
|
||||
},
|
||||
{
|
||||
Level: codersdk.LogLevelInfo,
|
||||
Output: "",
|
||||
Source: codersdk.WorkspaceAgentLogSourceExternal,
|
||||
},
|
||||
{
|
||||
Level: codersdk.LogLevelInfo,
|
||||
Output: "goodbye world",
|
||||
Source: codersdk.WorkspaceAgentLogSourceExternal,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -98,10 +109,11 @@ func TestStartupLogsWriter_Write(t *testing.T) {
|
|||
ctx: context.Background(),
|
||||
level: codersdk.LogLevelInfo,
|
||||
writes: []string{"hello world\n", "goodbye world"},
|
||||
want: []agentsdk.StartupLog{
|
||||
want: []agentsdk.Log{
|
||||
{
|
||||
Level: codersdk.LogLevelInfo,
|
||||
Output: "hello world",
|
||||
Source: codersdk.WorkspaceAgentLogSourceExternal,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -111,14 +123,16 @@ func TestStartupLogsWriter_Write(t *testing.T) {
|
|||
level: codersdk.LogLevelInfo,
|
||||
writes: []string{"hello world\n", "goodbye world"},
|
||||
closeFirst: true,
|
||||
want: []agentsdk.StartupLog{
|
||||
want: []agentsdk.Log{
|
||||
{
|
||||
Level: codersdk.LogLevelInfo,
|
||||
Output: "hello world",
|
||||
Source: codersdk.WorkspaceAgentLogSourceExternal,
|
||||
},
|
||||
{
|
||||
Level: codersdk.LogLevelInfo,
|
||||
Output: "goodbye world",
|
||||
Source: codersdk.WorkspaceAgentLogSourceExternal,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -127,14 +141,16 @@ func TestStartupLogsWriter_Write(t *testing.T) {
|
|||
ctx: context.Background(),
|
||||
level: codersdk.LogLevelInfo,
|
||||
writes: []string{"hello world\n", "goodbye", " world\n"},
|
||||
want: []agentsdk.StartupLog{
|
||||
want: []agentsdk.Log{
|
||||
{
|
||||
Level: codersdk.LogLevelInfo,
|
||||
Output: "hello world",
|
||||
Source: codersdk.WorkspaceAgentLogSourceExternal,
|
||||
},
|
||||
{
|
||||
Level: codersdk.LogLevelInfo,
|
||||
Output: "goodbye world",
|
||||
Source: codersdk.WorkspaceAgentLogSourceExternal,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -143,18 +159,21 @@ func TestStartupLogsWriter_Write(t *testing.T) {
|
|||
ctx: context.Background(),
|
||||
level: codersdk.LogLevelInfo,
|
||||
writes: []string{"hello world\r\n", "\r\r\n", "goodbye world\n"},
|
||||
want: []agentsdk.StartupLog{
|
||||
want: []agentsdk.Log{
|
||||
{
|
||||
Level: codersdk.LogLevelInfo,
|
||||
Output: "hello world",
|
||||
Source: codersdk.WorkspaceAgentLogSourceExternal,
|
||||
},
|
||||
{
|
||||
Level: codersdk.LogLevelInfo,
|
||||
Output: "\r",
|
||||
Source: codersdk.WorkspaceAgentLogSourceExternal,
|
||||
},
|
||||
{
|
||||
Level: codersdk.LogLevelInfo,
|
||||
Output: "goodbye world",
|
||||
Source: codersdk.WorkspaceAgentLogSourceExternal,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -172,8 +191,8 @@ func TestStartupLogsWriter_Write(t *testing.T) {
|
|||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var got []agentsdk.StartupLog
|
||||
send := func(ctx context.Context, log ...agentsdk.StartupLog) error {
|
||||
var got []agentsdk.Log
|
||||
send := func(ctx context.Context, log ...agentsdk.Log) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
|
@ -182,7 +201,7 @@ func TestStartupLogsWriter_Write(t *testing.T) {
|
|||
got = append(got, log...)
|
||||
return nil
|
||||
}
|
||||
w := agentsdk.StartupLogsWriter(tt.ctx, send, tt.level)
|
||||
w := agentsdk.StartupLogsWriter(tt.ctx, send, tt.source, tt.level)
|
||||
for _, s := range tt.writes {
|
||||
_, err := w.Write([]byte(s))
|
||||
if err != nil {
|
||||
|
@ -233,7 +252,7 @@ func TestStartupLogsSender(t *testing.T) {
|
|||
name string
|
||||
sendCount int
|
||||
discard []int
|
||||
patchResp func(req agentsdk.PatchStartupLogs) error
|
||||
patchResp func(req agentsdk.PatchLogs) error
|
||||
}{
|
||||
{
|
||||
name: "single log",
|
||||
|
@ -247,7 +266,7 @@ func TestStartupLogsSender(t *testing.T) {
|
|||
name: "too large",
|
||||
sendCount: 1,
|
||||
discard: []int{1},
|
||||
patchResp: func(req agentsdk.PatchStartupLogs) error {
|
||||
patchResp: func(req agentsdk.PatchLogs) error {
|
||||
return statusError(http.StatusRequestEntityTooLarge)
|
||||
},
|
||||
},
|
||||
|
@ -260,8 +279,8 @@ func TestStartupLogsSender(t *testing.T) {
|
|||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
|
||||
defer cancel()
|
||||
|
||||
got := []agentsdk.StartupLog{}
|
||||
patchStartupLogs := func(_ context.Context, req agentsdk.PatchStartupLogs) error {
|
||||
got := []agentsdk.Log{}
|
||||
patchLogs := func(_ context.Context, req agentsdk.PatchLogs) error {
|
||||
if tt.patchResp != nil {
|
||||
err := tt.patchResp(req)
|
||||
if err != nil {
|
||||
|
@ -272,15 +291,15 @@ func TestStartupLogsSender(t *testing.T) {
|
|||
return nil
|
||||
}
|
||||
|
||||
sendLog, flushAndClose := agentsdk.StartupLogsSender(patchStartupLogs, slogtest.Make(t, nil).Leveled(slog.LevelDebug))
|
||||
sendLog, flushAndClose := agentsdk.LogsSender(patchLogs, slogtest.Make(t, nil).Leveled(slog.LevelDebug))
|
||||
defer func() {
|
||||
err := flushAndClose(ctx)
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
|
||||
var want []agentsdk.StartupLog
|
||||
var want []agentsdk.Log
|
||||
for i := 0; i < tt.sendCount; i++ {
|
||||
want = append(want, agentsdk.StartupLog{
|
||||
want = append(want, agentsdk.Log{
|
||||
CreatedAt: time.Now(),
|
||||
Level: codersdk.LogLevelInfo,
|
||||
Output: fmt.Sprintf("hello world %d", i),
|
||||
|
@ -306,18 +325,18 @@ func TestStartupLogsSender(t *testing.T) {
|
|||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||
defer cancel()
|
||||
|
||||
patchStartupLogs := func(_ context.Context, _ agentsdk.PatchStartupLogs) error {
|
||||
patchLogs := func(_ context.Context, _ agentsdk.PatchLogs) error {
|
||||
assert.Fail(t, "should not be called")
|
||||
return nil
|
||||
}
|
||||
|
||||
sendLog, flushAndClose := agentsdk.StartupLogsSender(patchStartupLogs, slogtest.Make(t, nil).Leveled(slog.LevelDebug))
|
||||
sendLog, flushAndClose := agentsdk.LogsSender(patchLogs, slogtest.Make(t, nil).Leveled(slog.LevelDebug))
|
||||
defer func() {
|
||||
_ = flushAndClose(ctx)
|
||||
}()
|
||||
|
||||
cancel()
|
||||
err := sendLog(ctx, agentsdk.StartupLog{
|
||||
err := sendLog(ctx, agentsdk.Log{
|
||||
CreatedAt: time.Now(),
|
||||
Level: codersdk.LogLevelInfo,
|
||||
Output: "hello world",
|
||||
|
@ -336,18 +355,18 @@ func TestStartupLogsSender(t *testing.T) {
|
|||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||
defer cancel()
|
||||
|
||||
var want, got []agentsdk.StartupLog
|
||||
patchStartupLogs := func(_ context.Context, req agentsdk.PatchStartupLogs) error {
|
||||
var want, got []agentsdk.Log
|
||||
patchLogs := func(_ context.Context, req agentsdk.PatchLogs) error {
|
||||
got = append(got, req.Logs...)
|
||||
return nil
|
||||
}
|
||||
|
||||
sendLog, flushAndClose := agentsdk.StartupLogsSender(patchStartupLogs, slogtest.Make(t, nil).Leveled(slog.LevelDebug))
|
||||
sendLog, flushAndClose := agentsdk.LogsSender(patchLogs, slogtest.Make(t, nil).Leveled(slog.LevelDebug))
|
||||
defer func() {
|
||||
_ = flushAndClose(ctx)
|
||||
}()
|
||||
|
||||
err := sendLog(ctx, agentsdk.StartupLog{
|
||||
err := sendLog(ctx, agentsdk.Log{
|
||||
CreatedAt: time.Now(),
|
||||
Level: codersdk.LogLevelInfo,
|
||||
Output: "hello world",
|
||||
|
|
|
@ -153,8 +153,8 @@ type WorkspaceAgent struct {
|
|||
StartupScript string `json:"startup_script,omitempty"`
|
||||
StartupScriptBehavior WorkspaceAgentStartupScriptBehavior `json:"startup_script_behavior"`
|
||||
StartupScriptTimeoutSeconds int32 `json:"startup_script_timeout_seconds"` // StartupScriptTimeoutSeconds is the number of seconds to wait for the startup script to complete. If the script does not complete within this time, the agent lifecycle will be marked as start_timeout.
|
||||
StartupLogsLength int32 `json:"startup_logs_length"`
|
||||
StartupLogsOverflowed bool `json:"startup_logs_overflowed"`
|
||||
LogsLength int32 `json:"logs_length"`
|
||||
LogsOverflowed bool `json:"logs_overflowed"`
|
||||
Directory string `json:"directory,omitempty"`
|
||||
ExpandedDirectory string `json:"expanded_directory,omitempty"`
|
||||
Version string `json:"version"`
|
||||
|
@ -628,7 +628,7 @@ func (c *Client) WorkspaceAgentListeningPorts(ctx context.Context, agentID uuid.
|
|||
}
|
||||
|
||||
//nolint:revive // Follow is a control flag on the server as well.
|
||||
func (c *Client) WorkspaceAgentStartupLogsAfter(ctx context.Context, agentID uuid.UUID, after int64, follow bool) (<-chan []WorkspaceAgentStartupLog, io.Closer, error) {
|
||||
func (c *Client) WorkspaceAgentLogsAfter(ctx context.Context, agentID uuid.UUID, after int64, follow bool) (<-chan []WorkspaceAgentLog, io.Closer, error) {
|
||||
var queryParams []string
|
||||
if after != 0 {
|
||||
queryParams = append(queryParams, fmt.Sprintf("after=%d", after))
|
||||
|
@ -640,7 +640,7 @@ func (c *Client) WorkspaceAgentStartupLogsAfter(ctx context.Context, agentID uui
|
|||
if len(queryParams) > 0 {
|
||||
query = "?" + strings.Join(queryParams, "&")
|
||||
}
|
||||
reqURL, err := c.URL.Parse(fmt.Sprintf("/api/v2/workspaceagents/%s/startup-logs%s", agentID, query))
|
||||
reqURL, err := c.URL.Parse(fmt.Sprintf("/api/v2/workspaceagents/%s/logs%s", agentID, query))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -656,13 +656,13 @@ func (c *Client) WorkspaceAgentStartupLogsAfter(ctx context.Context, agentID uui
|
|||
return nil, nil, ReadBodyAsError(resp)
|
||||
}
|
||||
|
||||
var logs []WorkspaceAgentStartupLog
|
||||
var logs []WorkspaceAgentLog
|
||||
err = json.NewDecoder(resp.Body).Decode(&logs)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("decode startup logs: %w", err)
|
||||
}
|
||||
|
||||
ch := make(chan []WorkspaceAgentStartupLog, 1)
|
||||
ch := make(chan []WorkspaceAgentLog, 1)
|
||||
ch <- logs
|
||||
close(ch)
|
||||
return ch, closeFunc(func() error { return nil }), nil
|
||||
|
@ -690,7 +690,7 @@ func (c *Client) WorkspaceAgentStartupLogsAfter(ctx context.Context, agentID uui
|
|||
}
|
||||
return nil, nil, ReadBodyAsError(res)
|
||||
}
|
||||
logChunks := make(chan []WorkspaceAgentStartupLog)
|
||||
logChunks := make(chan []WorkspaceAgentLog)
|
||||
closed := make(chan struct{})
|
||||
ctx, wsNetConn := websocketNetConn(ctx, conn, websocket.MessageText)
|
||||
decoder := json.NewDecoder(wsNetConn)
|
||||
|
@ -699,7 +699,7 @@ func (c *Client) WorkspaceAgentStartupLogsAfter(ctx context.Context, agentID uui
|
|||
defer close(logChunks)
|
||||
defer conn.Close(websocket.StatusGoingAway, "")
|
||||
for {
|
||||
var logs []WorkspaceAgentStartupLog
|
||||
var logs []WorkspaceAgentLog
|
||||
err = decoder.Decode(&logs)
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -744,7 +744,7 @@ const (
|
|||
GitProviderBitBucket GitProvider = "bitbucket"
|
||||
)
|
||||
|
||||
type WorkspaceAgentStartupLog struct {
|
||||
type WorkspaceAgentLog struct {
|
||||
ID int64 `json:"id"`
|
||||
CreatedAt time.Time `json:"created_at" format:"date-time"`
|
||||
Output string `json:"output"`
|
||||
|
@ -756,3 +756,14 @@ type AgentSubsystem string
|
|||
const (
|
||||
AgentSubsystemEnvbox AgentSubsystem = "envbox"
|
||||
)
|
||||
|
||||
type WorkspaceAgentLogSource string
|
||||
|
||||
const (
|
||||
WorkspaceAgentLogSourceStartupScript WorkspaceAgentLogSource = "startup_script"
|
||||
WorkspaceAgentLogSourceShutdownScript WorkspaceAgentLogSource = "shutdown_script"
|
||||
WorkspaceAgentLogSourceKubernetes WorkspaceAgentLogSource = "kubernetes"
|
||||
WorkspaceAgentLogSourceEnvbox WorkspaceAgentLogSource = "envbox"
|
||||
WorkspaceAgentLogSourceEnvbuilder WorkspaceAgentLogSource = "envbuilder"
|
||||
WorkspaceAgentLogSourceExternal WorkspaceAgentLogSource = "external"
|
||||
)
|
||||
|
|
|
@ -44,10 +44,10 @@ We strive to keep the following use cases up to date, but please note that chang
|
|||
|
||||
Workspace agents have a special token that can send logs, metrics, and workspace activity.
|
||||
|
||||
- [Custom workspace logs](../api/agents.md#patch-workspace-agent-startup-logs): Expose messages prior to the Coder init script running (e.g. pulling image, VM starting, restoring snapshot). [coder-logstream-kube](https://github.com/coder/coder-logstream-kube) uses this to show Kubernetes events, such as image pulls or ResourceQuota restrictions.
|
||||
- [Custom workspace logs](../api/agents.md#patch-workspace-agent-logs): Expose messages prior to the Coder init script running (e.g. pulling image, VM starting, restoring snapshot). [coder-logstream-kube](https://github.com/coder/coder-logstream-kube) uses this to show Kubernetes events, such as image pulls or ResourceQuota restrictions.
|
||||
|
||||
```sh
|
||||
curl -X PATCH https://coder.example.com/api/v2/workspaceagents/me/startup-logs \
|
||||
curl -X PATCH https://coder.example.com/api/v2/workspaceagents/me/logs \
|
||||
-H "Coder-Session-Token: $CODER_AGENT_TOKEN" \
|
||||
-d "{
|
||||
\"logs\": [
|
||||
|
|
|
@ -293,6 +293,66 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/me/gitsshkey \
|
|||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Patch workspace agent logs
|
||||
|
||||
### Code samples
|
||||
|
||||
```shell
|
||||
# Example request using curl
|
||||
curl -X PATCH http://coder-server:8080/api/v2/workspaceagents/me/logs \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Coder-Session-Token: API_KEY'
|
||||
```
|
||||
|
||||
`PATCH /workspaceagents/me/logs`
|
||||
|
||||
> Body parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"logs": [
|
||||
{
|
||||
"created_at": "string",
|
||||
"level": "trace",
|
||||
"output": "string",
|
||||
"source": "startup_script"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
| Name | In | Type | Required | Description |
|
||||
| ------ | ---- | -------------------------------------------------- | -------- | ----------- |
|
||||
| `body` | body | [agentsdk.PatchLogs](schemas.md#agentsdkpatchlogs) | true | logs |
|
||||
|
||||
### Example responses
|
||||
|
||||
> 200 Response
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "string",
|
||||
"message": "string",
|
||||
"validations": [
|
||||
{
|
||||
"detail": "string",
|
||||
"field": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Responses
|
||||
|
||||
| Status | Meaning | Description | Schema |
|
||||
| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ |
|
||||
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) |
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Get authorized workspace agent manifest
|
||||
|
||||
### Code samples
|
||||
|
@ -488,7 +548,7 @@ curl -X POST http://coder-server:8080/api/v2/workspaceagents/me/report-stats \
|
|||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Patch workspace agent startup logs
|
||||
## Removed: Patch workspace agent logs
|
||||
|
||||
### Code samples
|
||||
|
||||
|
@ -510,7 +570,8 @@ curl -X PATCH http://coder-server:8080/api/v2/workspaceagents/me/startup-logs \
|
|||
{
|
||||
"created_at": "string",
|
||||
"level": "trace",
|
||||
"output": "string"
|
||||
"output": "string",
|
||||
"source": "startup_script"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -518,9 +579,9 @@ curl -X PATCH http://coder-server:8080/api/v2/workspaceagents/me/startup-logs \
|
|||
|
||||
### Parameters
|
||||
|
||||
| Name | In | Type | Required | Description |
|
||||
| ------ | ---- | ---------------------------------------------------------------- | -------- | ------------ |
|
||||
| `body` | body | [agentsdk.PatchStartupLogs](schemas.md#agentsdkpatchstartuplogs) | true | Startup logs |
|
||||
| Name | In | Type | Required | Description |
|
||||
| ------ | ---- | -------------------------------------------------- | -------- | ----------- |
|
||||
| `body` | body | [agentsdk.PatchLogs](schemas.md#agentsdkpatchlogs) | true | logs |
|
||||
|
||||
### Example responses
|
||||
|
||||
|
@ -621,6 +682,8 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent} \
|
|||
},
|
||||
"lifecycle_state": "created",
|
||||
"login_before_ready": true,
|
||||
"logs_length": 0,
|
||||
"logs_overflowed": true,
|
||||
"name": "string",
|
||||
"operating_system": "string",
|
||||
"ready_at": "2019-08-24T14:15:22Z",
|
||||
|
@ -628,8 +691,6 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent} \
|
|||
"shutdown_script": "string",
|
||||
"shutdown_script_timeout_seconds": 0,
|
||||
"started_at": "2019-08-24T14:15:22Z",
|
||||
"startup_logs_length": 0,
|
||||
"startup_logs_overflowed": true,
|
||||
"startup_script": "string",
|
||||
"startup_script_behavior": "blocking",
|
||||
"startup_script_timeout_seconds": 0,
|
||||
|
@ -806,6 +867,74 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent}/lis
|
|||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Get logs by workspace agent
|
||||
|
||||
### Code samples
|
||||
|
||||
```shell
|
||||
# Example request using curl
|
||||
curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent}/logs \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Coder-Session-Token: API_KEY'
|
||||
```
|
||||
|
||||
`GET /workspaceagents/{workspaceagent}/logs`
|
||||
|
||||
### Parameters
|
||||
|
||||
| Name | In | Type | Required | Description |
|
||||
| ---------------- | ----- | ------------ | -------- | -------------------------------------------- |
|
||||
| `workspaceagent` | path | string(uuid) | true | Workspace agent ID |
|
||||
| `before` | query | integer | false | Before log id |
|
||||
| `after` | query | integer | false | After log id |
|
||||
| `follow` | query | boolean | false | Follow log stream |
|
||||
| `no_compression` | query | boolean | false | Disable compression for WebSocket connection |
|
||||
|
||||
### Example responses
|
||||
|
||||
> 200 Response
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"id": 0,
|
||||
"level": "trace",
|
||||
"output": "string"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Responses
|
||||
|
||||
| Status | Meaning | Description | Schema |
|
||||
| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------------------------------- |
|
||||
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.WorkspaceAgentLog](schemas.md#codersdkworkspaceagentlog) |
|
||||
|
||||
<h3 id="get-logs-by-workspace-agent-responseschema">Response Schema</h3>
|
||||
|
||||
Status Code **200**
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| -------------- | ------------------------------------------------ | -------- | ------------ | ----------- |
|
||||
| `[array item]` | array | false | | |
|
||||
| `» created_at` | string(date-time) | false | | |
|
||||
| `» id` | integer | false | | |
|
||||
| `» level` | [codersdk.LogLevel](schemas.md#codersdkloglevel) | false | | |
|
||||
| `» output` | string | false | | |
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
| Property | Value |
|
||||
| -------- | ------- |
|
||||
| `level` | `trace` |
|
||||
| `level` | `debug` |
|
||||
| `level` | `info` |
|
||||
| `level` | `warn` |
|
||||
| `level` | `error` |
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Open PTY to workspace agent
|
||||
|
||||
### Code samples
|
||||
|
@ -832,7 +961,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent}/pty
|
|||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Get startup logs by workspace agent
|
||||
## Removed: Get logs by workspace agent
|
||||
|
||||
### Code samples
|
||||
|
||||
|
@ -872,11 +1001,11 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent}/sta
|
|||
|
||||
### Responses
|
||||
|
||||
| Status | Meaning | Description | Schema |
|
||||
| ------ | ------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------- |
|
||||
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.WorkspaceAgentStartupLog](schemas.md#codersdkworkspaceagentstartuplog) |
|
||||
| Status | Meaning | Description | Schema |
|
||||
| ------ | ------------------------------------------------------- | ----------- | --------------------------------------------------------------------------- |
|
||||
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.WorkspaceAgentLog](schemas.md#codersdkworkspaceagentlog) |
|
||||
|
||||
<h3 id="get-startup-logs-by-workspace-agent-responseschema">Response Schema</h3>
|
||||
<h3 id="removed:-get-logs-by-workspace-agent-responseschema">Response Schema</h3>
|
||||
|
||||
Status Code **200**
|
||||
|
||||
|
|
|
@ -107,6 +107,8 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam
|
|||
},
|
||||
"lifecycle_state": "created",
|
||||
"login_before_ready": true,
|
||||
"logs_length": 0,
|
||||
"logs_overflowed": true,
|
||||
"name": "string",
|
||||
"operating_system": "string",
|
||||
"ready_at": "2019-08-24T14:15:22Z",
|
||||
|
@ -114,8 +116,6 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam
|
|||
"shutdown_script": "string",
|
||||
"shutdown_script_timeout_seconds": 0,
|
||||
"started_at": "2019-08-24T14:15:22Z",
|
||||
"startup_logs_length": 0,
|
||||
"startup_logs_overflowed": true,
|
||||
"startup_script": "string",
|
||||
"startup_script_behavior": "blocking",
|
||||
"startup_script_timeout_seconds": 0,
|
||||
|
@ -269,6 +269,8 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \
|
|||
},
|
||||
"lifecycle_state": "created",
|
||||
"login_before_ready": true,
|
||||
"logs_length": 0,
|
||||
"logs_overflowed": true,
|
||||
"name": "string",
|
||||
"operating_system": "string",
|
||||
"ready_at": "2019-08-24T14:15:22Z",
|
||||
|
@ -276,8 +278,6 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \
|
|||
"shutdown_script": "string",
|
||||
"shutdown_script_timeout_seconds": 0,
|
||||
"started_at": "2019-08-24T14:15:22Z",
|
||||
"startup_logs_length": 0,
|
||||
"startup_logs_overflowed": true,
|
||||
"startup_script": "string",
|
||||
"startup_script_behavior": "blocking",
|
||||
"startup_script_timeout_seconds": 0,
|
||||
|
@ -570,6 +570,8 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/res
|
|||
},
|
||||
"lifecycle_state": "created",
|
||||
"login_before_ready": true,
|
||||
"logs_length": 0,
|
||||
"logs_overflowed": true,
|
||||
"name": "string",
|
||||
"operating_system": "string",
|
||||
"ready_at": "2019-08-24T14:15:22Z",
|
||||
|
@ -577,8 +579,6 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/res
|
|||
"shutdown_script": "string",
|
||||
"shutdown_script_timeout_seconds": 0,
|
||||
"started_at": "2019-08-24T14:15:22Z",
|
||||
"startup_logs_length": 0,
|
||||
"startup_logs_overflowed": true,
|
||||
"startup_script": "string",
|
||||
"startup_script_behavior": "blocking",
|
||||
"startup_script_timeout_seconds": 0,
|
||||
|
@ -659,6 +659,8 @@ Status Code **200**
|
|||
| `»»»» preferred` | boolean | false | | |
|
||||
| `»» lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](schemas.md#codersdkworkspaceagentlifecycle) | false | | |
|
||||
| `»» login_before_ready` | boolean | false | | Deprecated: Use StartupScriptBehavior instead. |
|
||||
| `»» logs_length` | integer | false | | |
|
||||
| `»» logs_overflowed` | boolean | false | | |
|
||||
| `»» name` | string | false | | |
|
||||
| `»» operating_system` | string | false | | |
|
||||
| `»» ready_at` | string(date-time) | false | | |
|
||||
|
@ -666,8 +668,6 @@ Status Code **200**
|
|||
| `»» shutdown_script` | string | false | | |
|
||||
| `»» shutdown_script_timeout_seconds` | integer | false | | |
|
||||
| `»» started_at` | string(date-time) | false | | |
|
||||
| `»» startup_logs_length` | integer | false | | |
|
||||
| `»» startup_logs_overflowed` | boolean | false | | |
|
||||
| `»» startup_script` | string | false | | |
|
||||
| `»» startup_script_behavior` | [codersdk.WorkspaceAgentStartupScriptBehavior](schemas.md#codersdkworkspaceagentstartupscriptbehavior) | false | | |
|
||||
| `»» startup_script_timeout_seconds` | integer | false | | »startup script timeout seconds is the number of seconds to wait for the startup script to complete. If the script does not complete within this time, the agent lifecycle will be marked as start_timeout. |
|
||||
|
@ -828,6 +828,8 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta
|
|||
},
|
||||
"lifecycle_state": "created",
|
||||
"login_before_ready": true,
|
||||
"logs_length": 0,
|
||||
"logs_overflowed": true,
|
||||
"name": "string",
|
||||
"operating_system": "string",
|
||||
"ready_at": "2019-08-24T14:15:22Z",
|
||||
|
@ -835,8 +837,6 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta
|
|||
"shutdown_script": "string",
|
||||
"shutdown_script_timeout_seconds": 0,
|
||||
"started_at": "2019-08-24T14:15:22Z",
|
||||
"startup_logs_length": 0,
|
||||
"startup_logs_overflowed": true,
|
||||
"startup_script": "string",
|
||||
"startup_script_behavior": "blocking",
|
||||
"startup_script_timeout_seconds": 0,
|
||||
|
@ -995,6 +995,8 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/builds \
|
|||
},
|
||||
"lifecycle_state": "created",
|
||||
"login_before_ready": true,
|
||||
"logs_length": 0,
|
||||
"logs_overflowed": true,
|
||||
"name": "string",
|
||||
"operating_system": "string",
|
||||
"ready_at": "2019-08-24T14:15:22Z",
|
||||
|
@ -1002,8 +1004,6 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/builds \
|
|||
"shutdown_script": "string",
|
||||
"shutdown_script_timeout_seconds": 0,
|
||||
"started_at": "2019-08-24T14:15:22Z",
|
||||
"startup_logs_length": 0,
|
||||
"startup_logs_overflowed": true,
|
||||
"startup_script": "string",
|
||||
"startup_script_behavior": "blocking",
|
||||
"startup_script_timeout_seconds": 0,
|
||||
|
@ -1120,6 +1120,8 @@ Status Code **200**
|
|||
| `»»»»» preferred` | boolean | false | | |
|
||||
| `»»» lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](schemas.md#codersdkworkspaceagentlifecycle) | false | | |
|
||||
| `»»» login_before_ready` | boolean | false | | Deprecated: Use StartupScriptBehavior instead. |
|
||||
| `»»» logs_length` | integer | false | | |
|
||||
| `»»» logs_overflowed` | boolean | false | | |
|
||||
| `»»» name` | string | false | | |
|
||||
| `»»» operating_system` | string | false | | |
|
||||
| `»»» ready_at` | string(date-time) | false | | |
|
||||
|
@ -1127,8 +1129,6 @@ Status Code **200**
|
|||
| `»»» shutdown_script` | string | false | | |
|
||||
| `»»» shutdown_script_timeout_seconds` | integer | false | | |
|
||||
| `»»» started_at` | string(date-time) | false | | |
|
||||
| `»»» startup_logs_length` | integer | false | | |
|
||||
| `»»» startup_logs_overflowed` | boolean | false | | |
|
||||
| `»»» startup_script` | string | false | | |
|
||||
| `»»» startup_script_behavior` | [codersdk.WorkspaceAgentStartupScriptBehavior](schemas.md#codersdkworkspaceagentstartupscriptbehavior) | false | | |
|
||||
| `»»» startup_script_timeout_seconds` | integer | false | | »»startup script timeout seconds is the number of seconds to wait for the startup script to complete. If the script does not complete within this time, the agent lifecycle will be marked as start_timeout. |
|
||||
|
@ -1343,6 +1343,8 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{workspace}/builds \
|
|||
},
|
||||
"lifecycle_state": "created",
|
||||
"login_before_ready": true,
|
||||
"logs_length": 0,
|
||||
"logs_overflowed": true,
|
||||
"name": "string",
|
||||
"operating_system": "string",
|
||||
"ready_at": "2019-08-24T14:15:22Z",
|
||||
|
@ -1350,8 +1352,6 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{workspace}/builds \
|
|||
"shutdown_script": "string",
|
||||
"shutdown_script_timeout_seconds": 0,
|
||||
"started_at": "2019-08-24T14:15:22Z",
|
||||
"startup_logs_length": 0,
|
||||
"startup_logs_overflowed": true,
|
||||
"startup_script": "string",
|
||||
"startup_script_behavior": "blocking",
|
||||
"startup_script_timeout_seconds": 0,
|
||||
|
|
|
@ -157,6 +157,26 @@
|
|||
| ---------------- | ------ | -------- | ------------ | ----------- |
|
||||
| `json_web_token` | string | true | | |
|
||||
|
||||
## agentsdk.Log
|
||||
|
||||
```json
|
||||
{
|
||||
"created_at": "string",
|
||||
"level": "trace",
|
||||
"output": "string",
|
||||
"source": "startup_script"
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ------------ | -------------------------------------------------------------------- | -------- | ------------ | ----------- |
|
||||
| `created_at` | string | false | | |
|
||||
| `level` | [codersdk.LogLevel](#codersdkloglevel) | false | | |
|
||||
| `output` | string | false | | |
|
||||
| `source` | [codersdk.WorkspaceAgentLogSource](#codersdkworkspaceagentlogsource) | false | | |
|
||||
|
||||
## agentsdk.Manifest
|
||||
|
||||
```json
|
||||
|
@ -277,7 +297,7 @@
|
|||
| `startup_script_timeout` | integer | false | | |
|
||||
| `vscode_port_proxy_uri` | string | false | | |
|
||||
|
||||
## agentsdk.PatchStartupLogs
|
||||
## agentsdk.PatchLogs
|
||||
|
||||
```json
|
||||
{
|
||||
|
@ -285,7 +305,8 @@
|
|||
{
|
||||
"created_at": "string",
|
||||
"level": "trace",
|
||||
"output": "string"
|
||||
"output": "string",
|
||||
"source": "startup_script"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -293,9 +314,9 @@
|
|||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ------ | --------------------------------------------------- | -------- | ------------ | ----------- |
|
||||
| `logs` | array of [agentsdk.StartupLog](#agentsdkstartuplog) | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ------ | ------------------------------------- | -------- | ------------ | ----------- |
|
||||
| `logs` | array of [agentsdk.Log](#agentsdklog) | false | | |
|
||||
|
||||
## agentsdk.PostAppHealthsRequest
|
||||
|
||||
|
@ -369,24 +390,6 @@
|
|||
| `subsystem` | [codersdk.AgentSubsystem](#codersdkagentsubsystem) | false | | |
|
||||
| `version` | string | false | | |
|
||||
|
||||
## agentsdk.StartupLog
|
||||
|
||||
```json
|
||||
{
|
||||
"created_at": "string",
|
||||
"level": "trace",
|
||||
"output": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ------------ | -------------------------------------- | -------- | ------------ | ----------- |
|
||||
| `created_at` | string | false | | |
|
||||
| `level` | [codersdk.LogLevel](#codersdkloglevel) | false | | |
|
||||
| `output` | string | false | | |
|
||||
|
||||
## agentsdk.Stats
|
||||
|
||||
```json
|
||||
|
@ -5240,6 +5243,8 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
|||
},
|
||||
"lifecycle_state": "created",
|
||||
"login_before_ready": true,
|
||||
"logs_length": 0,
|
||||
"logs_overflowed": true,
|
||||
"name": "string",
|
||||
"operating_system": "string",
|
||||
"ready_at": "2019-08-24T14:15:22Z",
|
||||
|
@ -5247,8 +5252,6 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
|||
"shutdown_script": "string",
|
||||
"shutdown_script_timeout_seconds": 0,
|
||||
"started_at": "2019-08-24T14:15:22Z",
|
||||
"startup_logs_length": 0,
|
||||
"startup_logs_overflowed": true,
|
||||
"startup_script": "string",
|
||||
"startup_script_behavior": "blocking",
|
||||
"startup_script_timeout_seconds": 0,
|
||||
|
@ -5381,6 +5384,8 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
|||
},
|
||||
"lifecycle_state": "created",
|
||||
"login_before_ready": true,
|
||||
"logs_length": 0,
|
||||
"logs_overflowed": true,
|
||||
"name": "string",
|
||||
"operating_system": "string",
|
||||
"ready_at": "2019-08-24T14:15:22Z",
|
||||
|
@ -5388,8 +5393,6 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
|||
"shutdown_script": "string",
|
||||
"shutdown_script_timeout_seconds": 0,
|
||||
"started_at": "2019-08-24T14:15:22Z",
|
||||
"startup_logs_length": 0,
|
||||
"startup_logs_overflowed": true,
|
||||
"startup_script": "string",
|
||||
"startup_script_behavior": "blocking",
|
||||
"startup_script_timeout_seconds": 0,
|
||||
|
@ -5423,6 +5426,8 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
|||
| » `[any property]` | [codersdk.DERPRegion](#codersdkderpregion) | false | | |
|
||||
| `lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](#codersdkworkspaceagentlifecycle) | false | | |
|
||||
| `login_before_ready` | boolean | false | | Deprecated: Use StartupScriptBehavior instead. |
|
||||
| `logs_length` | integer | false | | |
|
||||
| `logs_overflowed` | boolean | false | | |
|
||||
| `name` | string | false | | |
|
||||
| `operating_system` | string | false | | |
|
||||
| `ready_at` | string | false | | |
|
||||
|
@ -5430,8 +5435,6 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
|||
| `shutdown_script` | string | false | | |
|
||||
| `shutdown_script_timeout_seconds` | integer | false | | |
|
||||
| `started_at` | string | false | | |
|
||||
| `startup_logs_length` | integer | false | | |
|
||||
| `startup_logs_overflowed` | boolean | false | | |
|
||||
| `startup_script` | string | false | | |
|
||||
| `startup_script_behavior` | [codersdk.WorkspaceAgentStartupScriptBehavior](#codersdkworkspaceagentstartupscriptbehavior) | false | | |
|
||||
| `startup_script_timeout_seconds` | integer | false | | Startup script timeout seconds is the number of seconds to wait for the startup script to complete. If the script does not complete within this time, the agent lifecycle will be marked as start_timeout. |
|
||||
|
@ -5583,6 +5586,45 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
|||
| ------- | ------------------------------------------------------------------------------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `ports` | array of [codersdk.WorkspaceAgentListeningPort](#codersdkworkspaceagentlisteningport) | false | | If there are no ports in the list, nothing should be displayed in the UI. There must not be a "no ports available" message or anything similar, as there will always be no ports displayed on platforms where our port detection logic is unsupported. |
|
||||
|
||||
## codersdk.WorkspaceAgentLog
|
||||
|
||||
```json
|
||||
{
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"id": 0,
|
||||
"level": "trace",
|
||||
"output": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ------------ | -------------------------------------- | -------- | ------------ | ----------- |
|
||||
| `created_at` | string | false | | |
|
||||
| `id` | integer | false | | |
|
||||
| `level` | [codersdk.LogLevel](#codersdkloglevel) | false | | |
|
||||
| `output` | string | false | | |
|
||||
|
||||
## codersdk.WorkspaceAgentLogSource
|
||||
|
||||
```json
|
||||
"startup_script"
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
| Value |
|
||||
| ----------------- |
|
||||
| `startup_script` |
|
||||
| `shutdown_script` |
|
||||
| `kubernetes` |
|
||||
| `envbox` |
|
||||
| `envbuilder` |
|
||||
| `external` |
|
||||
|
||||
## codersdk.WorkspaceAgentMetadataDescription
|
||||
|
||||
```json
|
||||
|
@ -5605,26 +5647,6 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
|||
| `script` | string | false | | |
|
||||
| `timeout` | integer | false | | |
|
||||
|
||||
## codersdk.WorkspaceAgentStartupLog
|
||||
|
||||
```json
|
||||
{
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"id": 0,
|
||||
"level": "trace",
|
||||
"output": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ------------ | -------------------------------------- | -------- | ------------ | ----------- |
|
||||
| `created_at` | string | false | | |
|
||||
| `id` | integer | false | | |
|
||||
| `level` | [codersdk.LogLevel](#codersdkloglevel) | false | | |
|
||||
| `output` | string | false | | |
|
||||
|
||||
## codersdk.WorkspaceAgentStartupScriptBehavior
|
||||
|
||||
```json
|
||||
|
@ -5820,6 +5842,8 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
|||
},
|
||||
"lifecycle_state": "created",
|
||||
"login_before_ready": true,
|
||||
"logs_length": 0,
|
||||
"logs_overflowed": true,
|
||||
"name": "string",
|
||||
"operating_system": "string",
|
||||
"ready_at": "2019-08-24T14:15:22Z",
|
||||
|
@ -5827,8 +5851,6 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
|||
"shutdown_script": "string",
|
||||
"shutdown_script_timeout_seconds": 0,
|
||||
"started_at": "2019-08-24T14:15:22Z",
|
||||
"startup_logs_length": 0,
|
||||
"startup_logs_overflowed": true,
|
||||
"startup_script": "string",
|
||||
"startup_script_behavior": "blocking",
|
||||
"startup_script_timeout_seconds": 0,
|
||||
|
@ -6129,6 +6151,8 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
|||
},
|
||||
"lifecycle_state": "created",
|
||||
"login_before_ready": true,
|
||||
"logs_length": 0,
|
||||
"logs_overflowed": true,
|
||||
"name": "string",
|
||||
"operating_system": "string",
|
||||
"ready_at": "2019-08-24T14:15:22Z",
|
||||
|
@ -6136,8 +6160,6 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
|||
"shutdown_script": "string",
|
||||
"shutdown_script_timeout_seconds": 0,
|
||||
"started_at": "2019-08-24T14:15:22Z",
|
||||
"startup_logs_length": 0,
|
||||
"startup_logs_overflowed": true,
|
||||
"startup_script": "string",
|
||||
"startup_script_behavior": "blocking",
|
||||
"startup_script_timeout_seconds": 0,
|
||||
|
@ -6341,6 +6363,8 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
|||
},
|
||||
"lifecycle_state": "created",
|
||||
"login_before_ready": true,
|
||||
"logs_length": 0,
|
||||
"logs_overflowed": true,
|
||||
"name": "string",
|
||||
"operating_system": "string",
|
||||
"ready_at": "2019-08-24T14:15:22Z",
|
||||
|
@ -6348,8 +6372,6 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
|||
"shutdown_script": "string",
|
||||
"shutdown_script_timeout_seconds": 0,
|
||||
"started_at": "2019-08-24T14:15:22Z",
|
||||
"startup_logs_length": 0,
|
||||
"startup_logs_overflowed": true,
|
||||
"startup_script": "string",
|
||||
"startup_script_behavior": "blocking",
|
||||
"startup_script_timeout_seconds": 0,
|
||||
|
|
|
@ -1620,6 +1620,8 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/d
|
|||
},
|
||||
"lifecycle_state": "created",
|
||||
"login_before_ready": true,
|
||||
"logs_length": 0,
|
||||
"logs_overflowed": true,
|
||||
"name": "string",
|
||||
"operating_system": "string",
|
||||
"ready_at": "2019-08-24T14:15:22Z",
|
||||
|
@ -1627,8 +1629,6 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/d
|
|||
"shutdown_script": "string",
|
||||
"shutdown_script_timeout_seconds": 0,
|
||||
"started_at": "2019-08-24T14:15:22Z",
|
||||
"startup_logs_length": 0,
|
||||
"startup_logs_overflowed": true,
|
||||
"startup_script": "string",
|
||||
"startup_script_behavior": "blocking",
|
||||
"startup_script_timeout_seconds": 0,
|
||||
|
@ -1709,6 +1709,8 @@ Status Code **200**
|
|||
| `»»»» preferred` | boolean | false | | |
|
||||
| `»» lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](schemas.md#codersdkworkspaceagentlifecycle) | false | | |
|
||||
| `»» login_before_ready` | boolean | false | | Deprecated: Use StartupScriptBehavior instead. |
|
||||
| `»» logs_length` | integer | false | | |
|
||||
| `»» logs_overflowed` | boolean | false | | |
|
||||
| `»» name` | string | false | | |
|
||||
| `»» operating_system` | string | false | | |
|
||||
| `»» ready_at` | string(date-time) | false | | |
|
||||
|
@ -1716,8 +1718,6 @@ Status Code **200**
|
|||
| `»» shutdown_script` | string | false | | |
|
||||
| `»» shutdown_script_timeout_seconds` | integer | false | | |
|
||||
| `»» started_at` | string(date-time) | false | | |
|
||||
| `»» startup_logs_length` | integer | false | | |
|
||||
| `»» startup_logs_overflowed` | boolean | false | | |
|
||||
| `»» startup_script` | string | false | | |
|
||||
| `»» startup_script_behavior` | [codersdk.WorkspaceAgentStartupScriptBehavior](schemas.md#codersdkworkspaceagentstartupscriptbehavior) | false | | |
|
||||
| `»» startup_script_timeout_seconds` | integer | false | | »startup script timeout seconds is the number of seconds to wait for the startup script to complete. If the script does not complete within this time, the agent lifecycle will be marked as start_timeout. |
|
||||
|
@ -2012,6 +2012,8 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/r
|
|||
},
|
||||
"lifecycle_state": "created",
|
||||
"login_before_ready": true,
|
||||
"logs_length": 0,
|
||||
"logs_overflowed": true,
|
||||
"name": "string",
|
||||
"operating_system": "string",
|
||||
"ready_at": "2019-08-24T14:15:22Z",
|
||||
|
@ -2019,8 +2021,6 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/r
|
|||
"shutdown_script": "string",
|
||||
"shutdown_script_timeout_seconds": 0,
|
||||
"started_at": "2019-08-24T14:15:22Z",
|
||||
"startup_logs_length": 0,
|
||||
"startup_logs_overflowed": true,
|
||||
"startup_script": "string",
|
||||
"startup_script_behavior": "blocking",
|
||||
"startup_script_timeout_seconds": 0,
|
||||
|
@ -2101,6 +2101,8 @@ Status Code **200**
|
|||
| `»»»» preferred` | boolean | false | | |
|
||||
| `»» lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](schemas.md#codersdkworkspaceagentlifecycle) | false | | |
|
||||
| `»» login_before_ready` | boolean | false | | Deprecated: Use StartupScriptBehavior instead. |
|
||||
| `»» logs_length` | integer | false | | |
|
||||
| `»» logs_overflowed` | boolean | false | | |
|
||||
| `»» name` | string | false | | |
|
||||
| `»» operating_system` | string | false | | |
|
||||
| `»» ready_at` | string(date-time) | false | | |
|
||||
|
@ -2108,8 +2110,6 @@ Status Code **200**
|
|||
| `»» shutdown_script` | string | false | | |
|
||||
| `»» shutdown_script_timeout_seconds` | integer | false | | |
|
||||
| `»» started_at` | string(date-time) | false | | |
|
||||
| `»» startup_logs_length` | integer | false | | |
|
||||
| `»» startup_logs_overflowed` | boolean | false | | |
|
||||
| `»» startup_script` | string | false | | |
|
||||
| `»» startup_script_behavior` | [codersdk.WorkspaceAgentStartupScriptBehavior](schemas.md#codersdkworkspaceagentstartupscriptbehavior) | false | | |
|
||||
| `»» startup_script_timeout_seconds` | integer | false | | »startup script timeout seconds is the number of seconds to wait for the startup script to complete. If the script does not complete within this time, the agent lifecycle will be marked as start_timeout. |
|
||||
|
|
|
@ -135,6 +135,8 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/member
|
|||
},
|
||||
"lifecycle_state": "created",
|
||||
"login_before_ready": true,
|
||||
"logs_length": 0,
|
||||
"logs_overflowed": true,
|
||||
"name": "string",
|
||||
"operating_system": "string",
|
||||
"ready_at": "2019-08-24T14:15:22Z",
|
||||
|
@ -142,8 +144,6 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/member
|
|||
"shutdown_script": "string",
|
||||
"shutdown_script_timeout_seconds": 0,
|
||||
"started_at": "2019-08-24T14:15:22Z",
|
||||
"startup_logs_length": 0,
|
||||
"startup_logs_overflowed": true,
|
||||
"startup_script": "string",
|
||||
"startup_script_behavior": "blocking",
|
||||
"startup_script_timeout_seconds": 0,
|
||||
|
@ -323,6 +323,8 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam
|
|||
},
|
||||
"lifecycle_state": "created",
|
||||
"login_before_ready": true,
|
||||
"logs_length": 0,
|
||||
"logs_overflowed": true,
|
||||
"name": "string",
|
||||
"operating_system": "string",
|
||||
"ready_at": "2019-08-24T14:15:22Z",
|
||||
|
@ -330,8 +332,6 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam
|
|||
"shutdown_script": "string",
|
||||
"shutdown_script_timeout_seconds": 0,
|
||||
"started_at": "2019-08-24T14:15:22Z",
|
||||
"startup_logs_length": 0,
|
||||
"startup_logs_overflowed": true,
|
||||
"startup_script": "string",
|
||||
"startup_script_behavior": "blocking",
|
||||
"startup_script_timeout_seconds": 0,
|
||||
|
@ -510,6 +510,8 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \
|
|||
},
|
||||
"lifecycle_state": "created",
|
||||
"login_before_ready": true,
|
||||
"logs_length": 0,
|
||||
"logs_overflowed": true,
|
||||
"name": "string",
|
||||
"operating_system": "string",
|
||||
"ready_at": "2019-08-24T14:15:22Z",
|
||||
|
@ -517,8 +519,6 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \
|
|||
"shutdown_script": "string",
|
||||
"shutdown_script_timeout_seconds": 0,
|
||||
"started_at": "2019-08-24T14:15:22Z",
|
||||
"startup_logs_length": 0,
|
||||
"startup_logs_overflowed": true,
|
||||
"startup_script": "string",
|
||||
"startup_script_behavior": "blocking",
|
||||
"startup_script_timeout_seconds": 0,
|
||||
|
@ -699,6 +699,8 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \
|
|||
},
|
||||
"lifecycle_state": "created",
|
||||
"login_before_ready": true,
|
||||
"logs_length": 0,
|
||||
"logs_overflowed": true,
|
||||
"name": "string",
|
||||
"operating_system": "string",
|
||||
"ready_at": "2019-08-24T14:15:22Z",
|
||||
|
@ -706,8 +708,6 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \
|
|||
"shutdown_script": "string",
|
||||
"shutdown_script_timeout_seconds": 0,
|
||||
"started_at": "2019-08-24T14:15:22Z",
|
||||
"startup_logs_length": 0,
|
||||
"startup_logs_overflowed": true,
|
||||
"startup_script": "string",
|
||||
"startup_script_behavior": "blocking",
|
||||
"startup_script_timeout_seconds": 0,
|
||||
|
|
|
@ -775,11 +775,11 @@ export const getWorkspaceBuildLogs = async (
|
|||
return response.data
|
||||
}
|
||||
|
||||
export const getWorkspaceAgentStartupLogs = async (
|
||||
export const getWorkspaceAgentLogs = async (
|
||||
agentID: string,
|
||||
): Promise<TypesGen.WorkspaceAgentStartupLog[]> => {
|
||||
const response = await axios.get<TypesGen.WorkspaceAgentStartupLog[]>(
|
||||
`/api/v2/workspaceagents/${agentID}/startup-logs`,
|
||||
): Promise<TypesGen.WorkspaceAgentLog[]> => {
|
||||
const response = await axios.get<TypesGen.WorkspaceAgentLog[]>(
|
||||
`/api/v2/workspaceagents/${agentID}/logs`,
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
|
@ -1284,21 +1284,21 @@ export const watchBuildLogsByTemplateVersionId = (
|
|||
return socket
|
||||
}
|
||||
|
||||
type WatchStartupLogsOptions = {
|
||||
type WatchWorkspaceAgentLogsOptions = {
|
||||
after: number
|
||||
onMessage: (logs: TypesGen.WorkspaceAgentStartupLog[]) => void
|
||||
onMessage: (logs: TypesGen.WorkspaceAgentLog[]) => void
|
||||
onDone: () => void
|
||||
onError: (error: Error) => void
|
||||
}
|
||||
|
||||
export const watchStartupLogs = (
|
||||
export const watchWorkspaceAgentLogs = (
|
||||
agentId: string,
|
||||
{ after, onMessage, onDone, onError }: WatchStartupLogsOptions,
|
||||
{ after, onMessage, onDone, onError }: WatchWorkspaceAgentLogsOptions,
|
||||
) => {
|
||||
// WebSocket compression in Safari (confirmed in 16.5) is broken when
|
||||
// the server sends large messages. The following error is seen:
|
||||
//
|
||||
// WebSocket connection to 'wss://.../startup-logs?follow&after=0' failed: The operation couldn’t be completed. Protocol error
|
||||
// WebSocket connection to 'wss://.../logs?follow&after=0' failed: The operation couldn’t be completed. Protocol error
|
||||
//
|
||||
const noCompression =
|
||||
userAgentParser(navigator.userAgent).browser.name === "Safari"
|
||||
|
@ -1307,11 +1307,11 @@ export const watchStartupLogs = (
|
|||
|
||||
const proto = location.protocol === "https:" ? "wss:" : "ws:"
|
||||
const socket = new WebSocket(
|
||||
`${proto}//${location.host}/api/v2/workspaceagents/${agentId}/startup-logs?follow&after=${after}${noCompression}`,
|
||||
`${proto}//${location.host}/api/v2/workspaceagents/${agentId}/logs?follow&after=${after}${noCompression}`,
|
||||
)
|
||||
socket.binaryType = "blob"
|
||||
socket.addEventListener("message", (event) => {
|
||||
const logs = JSON.parse(event.data) as TypesGen.WorkspaceAgentStartupLog[]
|
||||
const logs = JSON.parse(event.data) as TypesGen.WorkspaceAgentLog[]
|
||||
onMessage(logs)
|
||||
})
|
||||
socket.addEventListener("error", () => {
|
||||
|
|
|
@ -1302,8 +1302,8 @@ export interface WorkspaceAgent {
|
|||
readonly startup_script?: string
|
||||
readonly startup_script_behavior: WorkspaceAgentStartupScriptBehavior
|
||||
readonly startup_script_timeout_seconds: number
|
||||
readonly startup_logs_length: number
|
||||
readonly startup_logs_overflowed: boolean
|
||||
readonly logs_length: number
|
||||
readonly logs_overflowed: boolean
|
||||
readonly directory?: string
|
||||
readonly expanded_directory?: string
|
||||
readonly version: string
|
||||
|
@ -1336,6 +1336,14 @@ export interface WorkspaceAgentListeningPortsResponse {
|
|||
readonly ports: WorkspaceAgentListeningPort[]
|
||||
}
|
||||
|
||||
// From codersdk/workspaceagents.go
|
||||
export interface WorkspaceAgentLog {
|
||||
readonly id: number
|
||||
readonly created_at: string
|
||||
readonly output: string
|
||||
readonly level: LogLevel
|
||||
}
|
||||
|
||||
// From codersdk/workspaceagents.go
|
||||
export interface WorkspaceAgentMetadata {
|
||||
readonly result: WorkspaceAgentMetadataResult
|
||||
|
@ -1359,14 +1367,6 @@ export interface WorkspaceAgentMetadataResult {
|
|||
readonly error: string
|
||||
}
|
||||
|
||||
// From codersdk/workspaceagents.go
|
||||
export interface WorkspaceAgentStartupLog {
|
||||
readonly id: number
|
||||
readonly created_at: string
|
||||
readonly output: string
|
||||
readonly level: LogLevel
|
||||
}
|
||||
|
||||
// From codersdk/workspaceapps.go
|
||||
export interface WorkspaceApp {
|
||||
readonly id: string
|
||||
|
@ -1820,6 +1820,23 @@ export const WorkspaceAgentLifecycles: WorkspaceAgentLifecycle[] = [
|
|||
"starting",
|
||||
]
|
||||
|
||||
// From codersdk/workspaceagents.go
|
||||
export type WorkspaceAgentLogSource =
|
||||
| "envbox"
|
||||
| "envbuilder"
|
||||
| "external"
|
||||
| "kubernetes"
|
||||
| "shutdown_script"
|
||||
| "startup_script"
|
||||
export const WorkspaceAgentLogSources: WorkspaceAgentLogSource[] = [
|
||||
"envbox",
|
||||
"envbuilder",
|
||||
"external",
|
||||
"kubernetes",
|
||||
"shutdown_script",
|
||||
"startup_script",
|
||||
]
|
||||
|
||||
// From codersdk/workspaceagents.go
|
||||
export type WorkspaceAgentStartupScriptBehavior = "blocking" | "non-blocking"
|
||||
export const WorkspaceAgentStartupScriptBehaviors: WorkspaceAgentStartupScriptBehavior[] =
|
||||
|
|
|
@ -215,7 +215,7 @@ Started.args = {
|
|||
...Example.args,
|
||||
agent: {
|
||||
...MockWorkspaceAgentReady,
|
||||
startup_logs_length: 1,
|
||||
logs_length: 1,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ export interface AgentRowProps {
|
|||
hideVSCodeDesktopButton?: boolean
|
||||
serverVersion: string
|
||||
onUpdateAgent: () => void
|
||||
storybookStartupLogs?: LineWithID[]
|
||||
storybookLogs?: LineWithID[]
|
||||
storybookAgentMetadata?: WorkspaceAgentMetadata[]
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ export const AgentRow: FC<AgentRowProps> = ({
|
|||
hideVSCodeDesktopButton,
|
||||
serverVersion,
|
||||
onUpdateAgent,
|
||||
storybookStartupLogs,
|
||||
storybookLogs,
|
||||
storybookAgentMetadata,
|
||||
sshPrefix,
|
||||
}) => {
|
||||
|
@ -75,10 +75,10 @@ export const AgentRow: FC<AgentRowProps> = ({
|
|||
context: { agentID: agent.id },
|
||||
services: process.env.STORYBOOK
|
||||
? {
|
||||
getStartupLogs: async () => {
|
||||
return storybookStartupLogs || []
|
||||
getLogs: async () => {
|
||||
return storybookLogs || []
|
||||
},
|
||||
streamStartupLogs: () => async () => {
|
||||
streamLogs: () => async () => {
|
||||
// noop
|
||||
},
|
||||
}
|
||||
|
@ -93,39 +93,38 @@ export const AgentRow: FC<AgentRowProps> = ({
|
|||
((agent.status === "connected" && hasAppsToDisplay) ||
|
||||
agent.status === "connecting")
|
||||
const hasStartupFeatures =
|
||||
Boolean(agent.startup_logs_length) ||
|
||||
Boolean(logsMachine.context.startupLogs?.length)
|
||||
Boolean(agent.logs_length) || Boolean(logsMachine.context.logs?.length)
|
||||
const { proxy } = useProxy()
|
||||
|
||||
const [showStartupLogs, setShowStartupLogs] = useState(
|
||||
const [showLogs, setShowLogs] = useState(
|
||||
["starting", "start_timeout"].includes(agent.lifecycle_state) &&
|
||||
hasStartupFeatures,
|
||||
)
|
||||
useEffect(() => {
|
||||
setShowStartupLogs(agent.lifecycle_state !== "ready" && hasStartupFeatures)
|
||||
setShowLogs(agent.lifecycle_state !== "ready" && hasStartupFeatures)
|
||||
}, [agent.lifecycle_state, hasStartupFeatures])
|
||||
// External applications can provide startup logs for an agent during it's spawn.
|
||||
// These could be Kubernetes logs, or other logs that are useful to the user.
|
||||
// For this reason, we want to fetch these logs when the agent is starting.
|
||||
useEffect(() => {
|
||||
if (agent.lifecycle_state === "starting") {
|
||||
sendLogsEvent("FETCH_STARTUP_LOGS")
|
||||
sendLogsEvent("FETCH_LOGS")
|
||||
}
|
||||
}, [sendLogsEvent, agent.lifecycle_state])
|
||||
useEffect(() => {
|
||||
// We only want to fetch logs when they are actually shown,
|
||||
// otherwise we can make a lot of requests that aren't necessary.
|
||||
if (showStartupLogs && logsMachine.can("FETCH_STARTUP_LOGS")) {
|
||||
sendLogsEvent("FETCH_STARTUP_LOGS")
|
||||
if (showLogs && logsMachine.can("FETCH_LOGS")) {
|
||||
sendLogsEvent("FETCH_LOGS")
|
||||
}
|
||||
}, [logsMachine, sendLogsEvent, showStartupLogs])
|
||||
}, [logsMachine, sendLogsEvent, showLogs])
|
||||
const logListRef = useRef<List>(null)
|
||||
const logListDivRef = useRef<HTMLDivElement>(null)
|
||||
const startupLogs = useMemo(() => {
|
||||
const allLogs = logsMachine.context.startupLogs || []
|
||||
const allLogs = logsMachine.context.logs || []
|
||||
|
||||
const logs = [...allLogs]
|
||||
if (agent.startup_logs_overflowed) {
|
||||
if (agent.logs_overflowed) {
|
||||
logs.push({
|
||||
id: -1,
|
||||
level: "error",
|
||||
|
@ -134,7 +133,7 @@ export const AgentRow: FC<AgentRowProps> = ({
|
|||
})
|
||||
}
|
||||
return logs
|
||||
}, [logsMachine.context.startupLogs, agent.startup_logs_overflowed])
|
||||
}, [logsMachine.context.logs, agent.logs_overflowed])
|
||||
const [bottomOfLogs, setBottomOfLogs] = useState(true)
|
||||
// This is a layout effect to remove flicker when we're scrolling to the bottom.
|
||||
useLayoutEffect(() => {
|
||||
|
@ -142,7 +141,7 @@ export const AgentRow: FC<AgentRowProps> = ({
|
|||
if (bottomOfLogs && logListRef.current) {
|
||||
logListRef.current.scrollToItem(startupLogs.length - 1, "end")
|
||||
}
|
||||
}, [showStartupLogs, startupLogs, logListRef, bottomOfLogs])
|
||||
}, [showLogs, startupLogs, logListRef, bottomOfLogs])
|
||||
|
||||
// This is a bit of a hack on the react-window API to get the scroll position.
|
||||
// If we're scrolled to the bottom, we want to keep the list scrolled to the bottom.
|
||||
|
@ -284,7 +283,7 @@ export const AgentRow: FC<AgentRowProps> = ({
|
|||
|
||||
{hasStartupFeatures && (
|
||||
<div className={styles.logsPanel}>
|
||||
<Collapse in={showStartupLogs}>
|
||||
<Collapse in={showLogs}>
|
||||
<AutoSizer disableHeight>
|
||||
{({ width }) => (
|
||||
<List
|
||||
|
@ -310,14 +309,14 @@ export const AgentRow: FC<AgentRowProps> = ({
|
|||
</Collapse>
|
||||
|
||||
<div className={styles.logsPanelButtons}>
|
||||
{showStartupLogs ? (
|
||||
{showLogs ? (
|
||||
<button
|
||||
className={combineClasses([
|
||||
styles.logsPanelButton,
|
||||
styles.toggleLogsButton,
|
||||
])}
|
||||
onClick={() => {
|
||||
setShowStartupLogs((v) => !v)
|
||||
setShowLogs((v) => !v)
|
||||
}}
|
||||
>
|
||||
<CloseDropdown />
|
||||
|
@ -330,7 +329,7 @@ export const AgentRow: FC<AgentRowProps> = ({
|
|||
styles.toggleLogsButton,
|
||||
])}
|
||||
onClick={() => {
|
||||
setShowStartupLogs((v) => !v)
|
||||
setShowLogs((v) => !v)
|
||||
}}
|
||||
>
|
||||
<OpenDropdown />
|
||||
|
|
|
@ -43,10 +43,12 @@ const renderWorkspacePage = async () => {
|
|||
jest
|
||||
.spyOn(api, "getDeploymentValues")
|
||||
.mockResolvedValueOnce(MockDeploymentConfig)
|
||||
jest.spyOn(api, "watchStartupLogs").mockImplementation((_, options) => {
|
||||
options.onDone()
|
||||
return new WebSocket("")
|
||||
})
|
||||
jest
|
||||
.spyOn(api, "watchWorkspaceAgentLogs")
|
||||
.mockImplementation((_, options) => {
|
||||
options.onDone()
|
||||
return new WebSocket("")
|
||||
})
|
||||
renderWithAuth(<WorkspacePage />, {
|
||||
route: `/@${MockWorkspace.owner_name}/${MockWorkspace.name}`,
|
||||
path: "/:username/:workspace",
|
||||
|
|
|
@ -555,8 +555,8 @@ export const MockWorkspaceAgent: TypesGen.WorkspaceAgent = {
|
|||
lifecycle_state: "starting",
|
||||
login_before_ready: false, // Deprecated.
|
||||
startup_script_behavior: "blocking",
|
||||
startup_logs_length: 0,
|
||||
startup_logs_overflowed: false,
|
||||
logs_length: 0,
|
||||
logs_overflowed: false,
|
||||
startup_script_timeout_seconds: 120,
|
||||
shutdown_script_timeout_seconds: 120,
|
||||
subsystem: "envbox",
|
||||
|
@ -1789,7 +1789,7 @@ export const MockDeploymentSSH: TypesGen.SSHConfigResponse = {
|
|||
ssh_config_options: {},
|
||||
}
|
||||
|
||||
export const MockStartupLogs: TypesGen.WorkspaceAgentStartupLog[] = [
|
||||
export const MockWorkspaceAgentLogs: TypesGen.WorkspaceAgentLog[] = [
|
||||
{
|
||||
id: 166663,
|
||||
created_at: "2023-05-04T11:30:41.402072Z",
|
||||
|
|
|
@ -384,7 +384,7 @@ export const handlers = [
|
|||
return res(ctx.status(200), ctx.json(M.MockDeploymentSSH))
|
||||
}),
|
||||
|
||||
rest.get("/api/v2/workspaceagents/:agent/startup-logs", (_, res, ctx) => {
|
||||
return res(ctx.status(200), ctx.json(M.MockStartupLogs))
|
||||
rest.get("/api/v2/workspaceagents/:agent/logs", (_, res, ctx) => {
|
||||
return res(ctx.status(200), ctx.json(M.MockWorkspaceAgentLogs))
|
||||
}),
|
||||
]
|
||||
|
|
|
@ -16,21 +16,21 @@ export const workspaceAgentLogsMachine = createMachine(
|
|||
schema: {
|
||||
events: {} as
|
||||
| {
|
||||
type: "ADD_STARTUP_LOGS"
|
||||
type: "ADD_LOGS"
|
||||
logs: LineWithID[]
|
||||
}
|
||||
| {
|
||||
type: "FETCH_STARTUP_LOGS"
|
||||
type: "FETCH_LOGS"
|
||||
}
|
||||
| {
|
||||
type: "STARTUP_DONE"
|
||||
type: "DONE"
|
||||
},
|
||||
context: {} as {
|
||||
agentID: string
|
||||
startupLogs?: LineWithID[]
|
||||
logs?: LineWithID[]
|
||||
},
|
||||
services: {} as {
|
||||
getStartupLogs: {
|
||||
getLogs: {
|
||||
data: LineWithID[]
|
||||
}
|
||||
},
|
||||
|
@ -40,29 +40,29 @@ export const workspaceAgentLogsMachine = createMachine(
|
|||
states: {
|
||||
waiting: {
|
||||
on: {
|
||||
FETCH_STARTUP_LOGS: "loading",
|
||||
FETCH_LOGS: "loading",
|
||||
},
|
||||
},
|
||||
loading: {
|
||||
invoke: {
|
||||
src: "getStartupLogs",
|
||||
src: "getLogs",
|
||||
onDone: {
|
||||
target: "watchStartupLogs",
|
||||
actions: ["assignStartupLogs"],
|
||||
target: "watchLogs",
|
||||
actions: ["assignLogs"],
|
||||
},
|
||||
},
|
||||
},
|
||||
watchStartupLogs: {
|
||||
id: "watchingStartupLogs",
|
||||
watchLogs: {
|
||||
id: "watchingLogs",
|
||||
invoke: {
|
||||
id: "streamStartupLogs",
|
||||
src: "streamStartupLogs",
|
||||
id: "streamLogs",
|
||||
src: "streamLogs",
|
||||
},
|
||||
on: {
|
||||
ADD_STARTUP_LOGS: {
|
||||
actions: "addStartupLogs",
|
||||
ADD_LOGS: {
|
||||
actions: "addLogs",
|
||||
},
|
||||
STARTUP_DONE: {
|
||||
DONE: {
|
||||
target: "loaded",
|
||||
},
|
||||
},
|
||||
|
@ -74,8 +74,8 @@ export const workspaceAgentLogsMachine = createMachine(
|
|||
},
|
||||
{
|
||||
services: {
|
||||
getStartupLogs: (ctx) =>
|
||||
API.getWorkspaceAgentStartupLogs(ctx.agentID).then((data) =>
|
||||
getLogs: (ctx) =>
|
||||
API.getWorkspaceAgentLogs(ctx.agentID).then((data) =>
|
||||
data.map((log) => ({
|
||||
id: log.id,
|
||||
level: log.level || "info",
|
||||
|
@ -83,17 +83,17 @@ export const workspaceAgentLogsMachine = createMachine(
|
|||
time: log.created_at,
|
||||
})),
|
||||
),
|
||||
streamStartupLogs: (ctx) => async (callback) => {
|
||||
streamLogs: (ctx) => async (callback) => {
|
||||
let after = 0
|
||||
if (ctx.startupLogs && ctx.startupLogs.length > 0) {
|
||||
after = ctx.startupLogs[ctx.startupLogs.length - 1].id
|
||||
if (ctx.logs && ctx.logs.length > 0) {
|
||||
after = ctx.logs[ctx.logs.length - 1].id
|
||||
}
|
||||
|
||||
const socket = API.watchStartupLogs(ctx.agentID, {
|
||||
const socket = API.watchWorkspaceAgentLogs(ctx.agentID, {
|
||||
after,
|
||||
onMessage: (logs) => {
|
||||
callback({
|
||||
type: "ADD_STARTUP_LOGS",
|
||||
type: "ADD_LOGS",
|
||||
logs: logs.map((log) => ({
|
||||
id: log.id,
|
||||
level: log.level || "info",
|
||||
|
@ -103,7 +103,7 @@ export const workspaceAgentLogsMachine = createMachine(
|
|||
})
|
||||
},
|
||||
onDone: () => {
|
||||
callback({ type: "STARTUP_DONE" })
|
||||
callback({ type: "DONE" })
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error(error)
|
||||
|
@ -116,12 +116,12 @@ export const workspaceAgentLogsMachine = createMachine(
|
|||
},
|
||||
},
|
||||
actions: {
|
||||
assignStartupLogs: assign({
|
||||
startupLogs: (_, { data }) => data,
|
||||
assignLogs: assign({
|
||||
logs: (_, { data }) => data,
|
||||
}),
|
||||
addStartupLogs: assign({
|
||||
startupLogs: (context, event) => {
|
||||
const previousLogs = context.startupLogs ?? []
|
||||
addLogs: assign({
|
||||
logs: (context, event) => {
|
||||
const previousLogs = context.logs ?? []
|
||||
return [...previousLogs, ...event.logs]
|
||||
},
|
||||
}),
|
||||
|
|
|
@ -46,7 +46,7 @@ export default defineConfig({
|
|||
secure: process.env.NODE_ENV === "production",
|
||||
configure: (proxy) => {
|
||||
// Vite does not catch socket errors, and stops the webserver.
|
||||
// As /startup-logs endpoint can return HTTP 4xx status, we need to embrace
|
||||
// As /logs endpoint can return HTTP 4xx status, we need to embrace
|
||||
// Vite with a custom error handler to prevent from quitting.
|
||||
proxy.on("proxyReqWs", (proxyReq, req, socket) => {
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
|
|
Loading…
Reference in New Issue