| 45 | } |
| 46 | |
| 47 | func (a *LifecycleAPI) UpdateLifecycle(ctx context.Context, req *agentproto.UpdateLifecycleRequest) (*agentproto.Lifecycle, error) { |
| 48 | workspaceAgent, err := a.AgentFn(ctx) |
| 49 | if err != nil { |
| 50 | return nil, err |
| 51 | } |
| 52 | |
| 53 | logger := a.Log.With( |
| 54 | slog.F("workspace_id", a.WorkspaceID), |
| 55 | slog.F("payload", req), |
| 56 | ) |
| 57 | logger.Debug(ctx, "workspace agent state report") |
| 58 | |
| 59 | var lifecycleState database.WorkspaceAgentLifecycleState |
| 60 | switch req.Lifecycle.State { |
| 61 | case agentproto.Lifecycle_CREATED: |
| 62 | lifecycleState = database.WorkspaceAgentLifecycleStateCreated |
| 63 | case agentproto.Lifecycle_STARTING: |
| 64 | lifecycleState = database.WorkspaceAgentLifecycleStateStarting |
| 65 | case agentproto.Lifecycle_START_TIMEOUT: |
| 66 | lifecycleState = database.WorkspaceAgentLifecycleStateStartTimeout |
| 67 | case agentproto.Lifecycle_START_ERROR: |
| 68 | lifecycleState = database.WorkspaceAgentLifecycleStateStartError |
| 69 | case agentproto.Lifecycle_READY: |
| 70 | lifecycleState = database.WorkspaceAgentLifecycleStateReady |
| 71 | case agentproto.Lifecycle_SHUTTING_DOWN: |
| 72 | lifecycleState = database.WorkspaceAgentLifecycleStateShuttingDown |
| 73 | case agentproto.Lifecycle_SHUTDOWN_TIMEOUT: |
| 74 | lifecycleState = database.WorkspaceAgentLifecycleStateShutdownTimeout |
| 75 | case agentproto.Lifecycle_SHUTDOWN_ERROR: |
| 76 | lifecycleState = database.WorkspaceAgentLifecycleStateShutdownError |
| 77 | case agentproto.Lifecycle_OFF: |
| 78 | lifecycleState = database.WorkspaceAgentLifecycleStateOff |
| 79 | default: |
| 80 | return nil, xerrors.Errorf("unknown lifecycle state %q", req.Lifecycle.State) |
| 81 | } |
| 82 | if !lifecycleState.Valid() { |
| 83 | return nil, xerrors.Errorf("unknown lifecycle state %q", req.Lifecycle.State) |
| 84 | } |
| 85 | |
| 86 | changedAt := req.Lifecycle.ChangedAt.AsTime() |
| 87 | if changedAt.IsZero() { |
| 88 | changedAt = a.now() |
| 89 | req.Lifecycle.ChangedAt = timestamppb.New(changedAt) |
| 90 | } |
| 91 | dbChangedAt := sql.NullTime{Time: changedAt, Valid: true} |
| 92 | |
| 93 | startedAt := workspaceAgent.StartedAt |
| 94 | readyAt := workspaceAgent.ReadyAt |
| 95 | switch lifecycleState { |
| 96 | case database.WorkspaceAgentLifecycleStateStarting: |
| 97 | startedAt = dbChangedAt |
| 98 | // This agent is (re)starting, so it's not ready yet. |
| 99 | readyAt.Time = time.Time{} |
| 100 | readyAt.Valid = false |
| 101 | case database.WorkspaceAgentLifecycleStateReady, |
| 102 | database.WorkspaceAgentLifecycleStateStartTimeout, |
| 103 | database.WorkspaceAgentLifecycleStateStartError: |
| 104 | if !startedAt.Valid { |