Skip to content

Commit 506dce5

Browse files
committed
fix(cli/cliui): handle never ending startup log stream in Agent
1 parent c1e6aa0 commit 506dce5

File tree

2 files changed

+38
-17
lines changed

2 files changed

+38
-17
lines changed

cli/cliui/agent.go

+23-5
Original file line numberDiff line numberDiff line change
@@ -137,26 +137,44 @@ func Agent(ctx context.Context, writer io.Writer, agentID uuid.UUID, opts AgentO
137137
}
138138
defer logsCloser.Close()
139139

140+
var lastLog codersdk.WorkspaceAgentStartupLog
141+
fetchedAgentWhileFollowing := fetchedAgent
142+
if !follow {
143+
fetchedAgentWhileFollowing = nil
144+
}
140145
for {
141146
// This select is essentially and inline `fetch()`.
142147
select {
143148
case <-ctx.Done():
144149
return ctx.Err()
145-
case f := <-fetchedAgent:
150+
case f := <-fetchedAgentWhileFollowing:
146151
if f.err != nil {
147152
return xerrors.Errorf("fetch: %w", f.err)
148153
}
149-
// We could handle changes in the agent status here, like
150-
// if the agent becomes disconnected, we may want to stop.
151-
// But for now, we'll just keep going, hopefully the agent
152-
// will reconnect and update its status.
153154
agent = f.agent
155+
156+
// If the agent is no longer starting, stop following
157+
// logs because FetchLogs will keep streaming forever.
158+
// We do one last non-follow request to ensure we have
159+
// fetched all logs.
160+
if !agent.LifecycleState.Starting() {
161+
_ = logsCloser.Close()
162+
fetchedAgentWhileFollowing = nil
163+
164+
logStream, logsCloser, err = opts.FetchLogs(ctx, agent.ID, lastLog.ID, false)
165+
if err != nil {
166+
return xerrors.Errorf("fetch workspace agent startup logs: %w", err)
167+
}
168+
// Logs are already primed, so we can call close.
169+
_ = logsCloser.Close()
170+
}
154171
case logs, ok := <-logStream:
155172
if !ok {
156173
return nil
157174
}
158175
for _, log := range logs {
159176
sw.Log(log.CreatedAt, log.Level, log.Output)
177+
lastLog = log
160178
}
161179
}
162180
}

cli/cliui/agent_test.go

+15-12
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ func TestAgent(t *testing.T) {
4646
func(_ context.Context, agent *codersdk.WorkspaceAgent, logs chan []codersdk.WorkspaceAgentStartupLog) error {
4747
agent.Status = codersdk.WorkspaceAgentConnected
4848
agent.FirstConnectedAt = ptr.Ref(time.Now())
49-
close(logs)
5049
return nil
5150
},
5251
},
@@ -79,7 +78,6 @@ func TestAgent(t *testing.T) {
7978
agent.FirstConnectedAt = ptr.Ref(time.Now())
8079
agent.LifecycleState = codersdk.WorkspaceAgentLifecycleReady
8180
agent.ReadyAt = ptr.Ref(time.Now())
82-
close(logs)
8381
return nil
8482
},
8583
},
@@ -113,10 +111,6 @@ func TestAgent(t *testing.T) {
113111
agent.LastConnectedAt = ptr.Ref(time.Now())
114112
return nil
115113
},
116-
func(_ context.Context, _ *codersdk.WorkspaceAgent, logs chan []codersdk.WorkspaceAgentStartupLog) error {
117-
close(logs)
118-
return nil
119-
},
120114
},
121115
want: []string{
122116
"⧗ The workspace agent lost connection",
@@ -154,7 +148,6 @@ func TestAgent(t *testing.T) {
154148
Output: "Bye now",
155149
},
156150
}
157-
close(logs)
158151
return nil
159152
},
160153
},
@@ -184,7 +177,6 @@ func TestAgent(t *testing.T) {
184177
Output: "Hello world",
185178
},
186179
}
187-
close(logs)
188180
return nil
189181
},
190182
},
@@ -205,7 +197,6 @@ func TestAgent(t *testing.T) {
205197
func(_ context.Context, agent *codersdk.WorkspaceAgent, logs chan []codersdk.WorkspaceAgentStartupLog) error {
206198
agent.Status = codersdk.WorkspaceAgentDisconnected
207199
agent.LifecycleState = codersdk.WorkspaceAgentLifecycleOff
208-
close(logs)
209200
return nil
210201
},
211202
},
@@ -234,7 +225,6 @@ func TestAgent(t *testing.T) {
234225
func(_ context.Context, agent *codersdk.WorkspaceAgent, logs chan []codersdk.WorkspaceAgentStartupLog) error {
235226
agent.ReadyAt = ptr.Ref(time.Now())
236227
agent.LifecycleState = codersdk.WorkspaceAgentLifecycleShuttingDown
237-
close(logs)
238228
return nil
239229
},
240230
},
@@ -316,8 +306,21 @@ func TestAgent(t *testing.T) {
316306
}
317307
return agent, err
318308
}
319-
tc.opts.FetchLogs = func(_ context.Context, _ uuid.UUID, _ int64, _ bool) (<-chan []codersdk.WorkspaceAgentStartupLog, io.Closer, error) {
320-
return logs, closeFunc(func() error { return nil }), nil
309+
tc.opts.FetchLogs = func(ctx context.Context, _ uuid.UUID, _ int64, follow bool) (<-chan []codersdk.WorkspaceAgentStartupLog, io.Closer, error) {
310+
if follow {
311+
return logs, closeFunc(func() error { return nil }), nil
312+
}
313+
314+
fetchLogs := make(chan []codersdk.WorkspaceAgentStartupLog, 1)
315+
select {
316+
case <-ctx.Done():
317+
return nil, nil, ctx.Err()
318+
case l := <-logs:
319+
fetchLogs <- l
320+
default:
321+
}
322+
close(fetchLogs)
323+
return fetchLogs, closeFunc(func() error { return nil }), nil
321324
}
322325
err := cliui.Agent(inv.Context(), &buf, uuid.Nil, tc.opts)
323326
return err

0 commit comments

Comments
 (0)