Skip to content

Commit b3613ec

Browse files
committed
chore: prevent nil derefs in non-critical paths
1 parent 5981abd commit b3613ec

File tree

1 file changed

+17
-5
lines changed

1 file changed

+17
-5
lines changed

cli/cliui/agent.go

+17-5
Original file line numberDiff line numberDiff line change
@@ -200,12 +200,12 @@ func Agent(ctx context.Context, writer io.Writer, agentID uuid.UUID, opts AgentO
200200

201201
switch agent.LifecycleState {
202202
case codersdk.WorkspaceAgentLifecycleReady:
203-
sw.Complete(stage, agent.ReadyAt.Sub(*agent.StartedAt))
203+
sw.Complete(stage, safeDuration(agent.ReadyAt, agent.StartedAt))
204204
case codersdk.WorkspaceAgentLifecycleStartTimeout:
205205
sw.Fail(stage, 0)
206206
sw.Log(time.Time{}, codersdk.LogLevelWarn, "Warning: A startup script timed out and your workspace may be incomplete.")
207207
case codersdk.WorkspaceAgentLifecycleStartError:
208-
sw.Fail(stage, agent.ReadyAt.Sub(*agent.StartedAt))
208+
sw.Fail(stage, safeDuration(agent.ReadyAt, agent.StartedAt))
209209
// Use zero time (omitted) to separate these from the startup logs.
210210
sw.Log(time.Time{}, codersdk.LogLevelWarn, "Warning: A startup script exited with an error and your workspace may be incomplete.")
211211
sw.Log(time.Time{}, codersdk.LogLevelWarn, troubleshootingMessage(agent, "https://coder.com/docs/v2/latest/templates#startup-script-exited-with-an-error"))
@@ -221,7 +221,7 @@ func Agent(ctx context.Context, writer io.Writer, agentID uuid.UUID, opts AgentO
221221
case agent.LifecycleState.ShuttingDown():
222222
// We no longer know if the startup script failed or not,
223223
// but we need to tell the user something.
224-
sw.Complete(stage, agent.ReadyAt.Sub(*agent.StartedAt))
224+
sw.Complete(stage, safeDuration(agent.ReadyAt, agent.StartedAt))
225225
return errAgentShuttingDown
226226
}
227227
}
@@ -238,13 +238,12 @@ func Agent(ctx context.Context, writer io.Writer, agentID uuid.UUID, opts AgentO
238238
sw.Log(time.Now(), codersdk.LogLevelWarn, "Wait for it to reconnect or restart your workspace.")
239239
sw.Log(time.Now(), codersdk.LogLevelWarn, troubleshootingMessage(agent, "https://coder.com/docs/v2/latest/templates#agent-connection-issues"))
240240

241-
disconnectedAt := *agent.DisconnectedAt
242241
for agent.Status == codersdk.WorkspaceAgentDisconnected {
243242
if agent, err = fetch(); err != nil {
244243
return xerrors.Errorf("fetch: %w", err)
245244
}
246245
}
247-
sw.Complete(stage, agent.LastConnectedAt.Sub(disconnectedAt))
246+
sw.Complete(stage, safeDuration(agent.LastConnectedAt, agent.DisconnectedAt))
248247
}
249248
}
250249
}
@@ -257,6 +256,19 @@ func troubleshootingMessage(agent codersdk.WorkspaceAgent, url string) string {
257256
return m
258257
}
259258

259+
// safeDuration returns a-b. If a or b is nil, it returns 0.
260+
// This is because we often dereference a time pointer, which can
261+
// cause a panic. These dereferences are used to calculate durations,
262+
// which are not critical, and therefor should not break things
263+
// when it fails.
264+
// A panic has been observed in a test.
265+
func safeDuration(a, b *time.Time) time.Duration {
266+
if a == nil || b == nil {
267+
return 0
268+
}
269+
return a.Sub(*b)
270+
}
271+
260272
type closeFunc func() error
261273

262274
func (c closeFunc) Close() error {

0 commit comments

Comments
 (0)