Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d062b5b

Browse files
committed
Return jobs with WorkspaceHistory
1 parent c448e99 commit d062b5b

File tree

12 files changed

+156
-58
lines changed

12 files changed

+156
-58
lines changed

.vscode/settings.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"httpmw",
3636
"moby",
3737
"nhooyr",
38+
"nolint",
3839
"nosec",
3940
"oneof",
4041
"protobuf",
@@ -43,6 +44,7 @@
4344
"retrier",
4445
"sdkproto",
4546
"stretchr",
47+
"unconvert",
4648
"xerrors",
4749
"yamux"
4850
]

coderd/provisionerdaemons.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -515,10 +515,6 @@ func (server *provisionerdServer) CompleteJob(ctx context.Context, completed *pr
515515
ID: workspaceHistory.ID,
516516
UpdatedAt: database.Now(),
517517
ProvisionerState: jobType.WorkspaceProvision.State,
518-
CompletedAt: sql.NullTime{
519-
Time: database.Now(),
520-
Valid: true,
521-
},
522518
})
523519
if err != nil {
524520
return xerrors.Errorf("update workspace history: %w", err)

coderd/provisioners.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package coderd
2+
3+
import (
4+
"time"
5+
6+
"github.com/google/uuid"
7+
8+
"github.com/coder/coder/database"
9+
)
10+
11+
type ProvisionerJobStatus string
12+
13+
const (
14+
ProvisionerJobStatusPending ProvisionerJobStatus = "pending"
15+
ProvisionerJobStatusRunning ProvisionerJobStatus = "running"
16+
ProvisionerJobStatusSucceeded ProvisionerJobStatus = "succeeded"
17+
ProvisionerJobStatusFailed ProvisionerJobStatus = "failed"
18+
)
19+
20+
type ProvisionerJob struct {
21+
CreatedAt time.Time `json:"created_at"`
22+
UpdatedAt time.Time `json:"updated_at"`
23+
StartedAt *time.Time `json:"started_at,omitempty"`
24+
CancelledAt *time.Time `json:"canceled_at,omitempty"`
25+
CompletedAt *time.Time `json:"completed_at,omitempty"`
26+
Status ProvisionerJobStatus `json:"status"`
27+
Error string `json:"error,omitempty"`
28+
Provisioner database.ProvisionerType `json:"provisioner"`
29+
WorkerID *uuid.UUID `json:"worker_id,omitempty"`
30+
}
31+
32+
func convertProvisionerJob(provisionerJob database.ProvisionerJob) ProvisionerJob {
33+
job := ProvisionerJob{
34+
CreatedAt: provisionerJob.CreatedAt,
35+
UpdatedAt: provisionerJob.UpdatedAt,
36+
Error: provisionerJob.Error.String,
37+
Provisioner: provisionerJob.Provisioner,
38+
}
39+
if provisionerJob.StartedAt.Valid {
40+
job.StartedAt = &provisionerJob.StartedAt.Time
41+
}
42+
if provisionerJob.CancelledAt.Valid {
43+
job.CancelledAt = &provisionerJob.CancelledAt.Time
44+
}
45+
if provisionerJob.CompletedAt.Valid {
46+
job.CompletedAt = &provisionerJob.CompletedAt.Time
47+
}
48+
if provisionerJob.WorkerID.Valid {
49+
job.WorkerID = &provisionerJob.WorkerID.UUID
50+
}
51+
52+
switch {
53+
case provisionerJob.CancelledAt.Valid:
54+
job.Status = ProvisionerJobStatusFailed
55+
case !provisionerJob.StartedAt.Valid:
56+
job.Status = ProvisionerJobStatusPending
57+
case provisionerJob.CompletedAt.Valid:
58+
job.Status = ProvisionerJobStatusSucceeded
59+
case database.Now().Sub(provisionerJob.UpdatedAt) > 30*time.Second:
60+
job.Status = ProvisionerJobStatusFailed
61+
job.Error = "Worker failed to update job in time."
62+
default:
63+
job.Status = ProvisionerJobStatusRunning
64+
}
65+
66+
return job
67+
}

coderd/workspacehistory.go

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package coderd
22

33
import (
44
"database/sql"
5+
"encoding/json"
56
"errors"
67
"fmt"
78
"net/http"
@@ -22,13 +23,13 @@ type WorkspaceHistory struct {
2223
ID uuid.UUID `json:"id"`
2324
CreatedAt time.Time `json:"created_at"`
2425
UpdatedAt time.Time `json:"updated_at"`
25-
CompletedAt time.Time `json:"completed_at"`
2626
WorkspaceID uuid.UUID `json:"workspace_id"`
2727
ProjectHistoryID uuid.UUID `json:"project_history_id"`
2828
BeforeID uuid.UUID `json:"before_id"`
2929
AfterID uuid.UUID `json:"after_id"`
3030
Transition database.WorkspaceTransition `json:"transition"`
3131
Initiator string `json:"initiator"`
32+
Job ProvisionerJob `json:"job"`
3233
}
3334

3435
// CreateWorkspaceHistoryRequest provides options to update the latest workspace history.
@@ -37,8 +38,6 @@ type CreateWorkspaceHistoryRequest struct {
3738
Transition database.WorkspaceTransition `json:"transition" validate:"oneof=create start stop delete,required"`
3839
}
3940

40-
// Begins transitioning a workspace to new state. This queues a provision job to asynchronously
41-
// update the underlying infrastructure. Only one historical transition can occur at a time.
4241
func (api *api) postWorkspaceHistoryByUser(rw http.ResponseWriter, r *http.Request) {
4342
var createBuild CreateWorkspaceHistoryRequest
4443
if !httpapi.Read(rw, r, &createBuild) {
@@ -63,16 +62,28 @@ func (api *api) postWorkspaceHistoryByUser(rw http.ResponseWriter, r *http.Reque
6362
})
6463
return
6564
}
65+
project, err := api.Database.GetProjectByID(r.Context(), projectHistory.ProjectID)
66+
if err != nil {
67+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
68+
Message: fmt.Sprintf("get project: %s", err),
69+
})
70+
return
71+
}
6672

6773
// Store prior history ID if it exists to update it after we create new!
6874
priorHistoryID := uuid.NullUUID{}
6975
priorHistory, err := api.Database.GetWorkspaceHistoryByWorkspaceIDWithoutAfter(r.Context(), workspace.ID)
7076
if err == nil {
71-
if !priorHistory.CompletedAt.Valid {
72-
httpapi.Write(rw, http.StatusConflict, httpapi.Response{
73-
Message: "a workspace build is already active",
74-
})
75-
return
77+
priorJob, err := api.Database.GetProvisionerJobByID(r.Context(), priorHistory.ProvisionJobID)
78+
if err == nil {
79+
convertedJob := convertProvisionerJob(priorJob)
80+
if convertedJob.Status == ProvisionerJobStatusPending ||
81+
convertedJob.Status == ProvisionerJobStatusRunning {
82+
httpapi.Write(rw, http.StatusConflict, httpapi.Response{
83+
Message: "a workspace build is already active",
84+
})
85+
return
86+
}
7687
}
7788

7889
priorHistoryID = uuid.NullUUID{
@@ -87,10 +98,34 @@ func (api *api) postWorkspaceHistoryByUser(rw http.ResponseWriter, r *http.Reque
8798
return
8899
}
89100

101+
var provisionerJob database.ProvisionerJob
90102
var workspaceHistory database.WorkspaceHistory
91103
// This must happen in a transaction to ensure history can be inserted, and
92104
// the prior history can update it's "after" column to point at the new.
93105
err = api.Database.InTx(func(db database.Store) error {
106+
// Generate the ID before-hand so the provisioner job is aware of it!
107+
workspaceHistoryID := uuid.New()
108+
input, err := json.Marshal(workspaceProvisionJob{
109+
WorkspaceHistoryID: workspaceHistoryID,
110+
})
111+
if err != nil {
112+
return xerrors.Errorf("marshal provision job: %w", err)
113+
}
114+
115+
provisionerJob, err = db.InsertProvisionerJob(r.Context(), database.InsertProvisionerJobParams{
116+
ID: uuid.New(),
117+
CreatedAt: database.Now(),
118+
UpdatedAt: database.Now(),
119+
InitiatorID: user.ID,
120+
Provisioner: project.Provisioner,
121+
Type: database.ProvisionerJobTypeWorkspaceProvision,
122+
ProjectID: project.ID,
123+
Input: input,
124+
})
125+
if err != nil {
126+
return xerrors.Errorf("insert provisioner job: %w", err)
127+
}
128+
94129
workspaceHistory, err = db.InsertWorkspaceHistory(r.Context(), database.InsertWorkspaceHistoryParams{
95130
ID: uuid.New(),
96131
CreatedAt: database.Now(),
@@ -100,8 +135,7 @@ func (api *api) postWorkspaceHistoryByUser(rw http.ResponseWriter, r *http.Reque
100135
BeforeID: priorHistoryID,
101136
Initiator: user.ID,
102137
Transition: createBuild.Transition,
103-
// This should create a provision job once that gets implemented!
104-
ProvisionJobID: uuid.New(),
138+
ProvisionJobID: provisionerJob.ID,
105139
})
106140
if err != nil {
107141
return xerrors.Errorf("insert workspace history: %w", err)
@@ -132,7 +166,7 @@ func (api *api) postWorkspaceHistoryByUser(rw http.ResponseWriter, r *http.Reque
132166
}
133167

134168
render.Status(r, http.StatusCreated)
135-
render.JSON(rw, r, convertWorkspaceHistory(workspaceHistory))
169+
render.JSON(rw, r, convertWorkspaceHistory(workspaceHistory, provisionerJob))
136170
}
137171

138172
// Returns all workspace history. This is not sorted. Use before/after to chronologically sort.
@@ -152,7 +186,14 @@ func (api *api) workspaceHistoryByUser(rw http.ResponseWriter, r *http.Request)
152186

153187
apiHistory := make([]WorkspaceHistory, 0, len(histories))
154188
for _, history := range histories {
155-
apiHistory = append(apiHistory, convertWorkspaceHistory(history))
189+
job, err := api.Database.GetProvisionerJobByID(r.Context(), history.ProvisionJobID)
190+
if err != nil {
191+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
192+
Message: fmt.Sprintf("get provisioner job: %s", err),
193+
})
194+
return
195+
}
196+
apiHistory = append(apiHistory, convertWorkspaceHistory(history, job))
156197
}
157198

158199
render.Status(r, http.StatusOK)
@@ -176,9 +217,33 @@ func (api *api) latestWorkspaceHistoryByUser(rw http.ResponseWriter, r *http.Req
176217
})
177218
return
178219
}
220+
job, err := api.Database.GetProvisionerJobByID(r.Context(), history.ProvisionJobID)
221+
if err != nil {
222+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
223+
Message: fmt.Sprintf("get provisioner job: %s", err),
224+
})
225+
return
226+
}
179227

180228
render.Status(r, http.StatusOK)
181-
render.JSON(rw, r, convertWorkspaceHistory(history))
229+
render.JSON(rw, r, convertWorkspaceHistory(history, job))
230+
}
231+
232+
// Converts the internal history representation to a public external-facing model.
233+
func convertWorkspaceHistory(workspaceHistory database.WorkspaceHistory, provisionerJob database.ProvisionerJob) WorkspaceHistory {
234+
//nolint:unconvert
235+
return WorkspaceHistory(WorkspaceHistory{
236+
ID: workspaceHistory.ID,
237+
CreatedAt: workspaceHistory.CreatedAt,
238+
UpdatedAt: workspaceHistory.UpdatedAt,
239+
WorkspaceID: workspaceHistory.WorkspaceID,
240+
ProjectHistoryID: workspaceHistory.ProjectHistoryID,
241+
BeforeID: workspaceHistory.BeforeID.UUID,
242+
AfterID: workspaceHistory.AfterID.UUID,
243+
Transition: workspaceHistory.Transition,
244+
Initiator: workspaceHistory.Initiator,
245+
Job: convertProvisionerJob(provisionerJob),
246+
})
182247
}
183248

184249
func workspaceHistoryLogsChannel(workspaceHistoryID uuid.UUID) string {

coderd/workspaces.go

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -149,20 +149,3 @@ func (*api) workspaceByUser(rw http.ResponseWriter, r *http.Request) {
149149
func convertWorkspace(workspace database.Workspace) Workspace {
150150
return Workspace(workspace)
151151
}
152-
153-
// Converts the internal history representation to a public external-facing model.
154-
func convertWorkspaceHistory(workspaceHistory database.WorkspaceHistory) WorkspaceHistory {
155-
//nolint:unconvert
156-
return WorkspaceHistory(WorkspaceHistory{
157-
ID: workspaceHistory.ID,
158-
CreatedAt: workspaceHistory.CreatedAt,
159-
UpdatedAt: workspaceHistory.UpdatedAt,
160-
CompletedAt: workspaceHistory.CompletedAt.Time,
161-
WorkspaceID: workspaceHistory.WorkspaceID,
162-
ProjectHistoryID: workspaceHistory.ProjectHistoryID,
163-
BeforeID: workspaceHistory.BeforeID.UUID,
164-
AfterID: workspaceHistory.AfterID.UUID,
165-
Transition: workspaceHistory.Transition,
166-
Initiator: workspaceHistory.Initiator,
167-
})
168-
}

database/databasefake/databasefake.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -764,7 +764,6 @@ func (q *fakeQuerier) UpdateWorkspaceHistoryByID(_ context.Context, arg database
764764
continue
765765
}
766766
workspaceHistory.UpdatedAt = arg.UpdatedAt
767-
workspaceHistory.CompletedAt = arg.CompletedAt
768767
workspaceHistory.AfterID = arg.AfterID
769768
workspaceHistory.ProvisionerState = arg.ProvisionerState
770769
q.workspaceHistory[index] = workspaceHistory

database/dump.sql

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

database/migrations/000003_workspaces.up.sql

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ CREATE TABLE workspace_history (
2020
id uuid NOT NULL UNIQUE,
2121
created_at timestamptz NOT NULL,
2222
updated_at timestamptz NOT NULL,
23-
completed_at timestamptz,
2423
workspace_id uuid NOT NULL REFERENCES workspace (id) ON DELETE CASCADE,
2524
project_history_id uuid NOT NULL REFERENCES project_history (id) ON DELETE CASCADE,
2625
name varchar(64) NOT NULL,

database/models.go

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

database/query.sql

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -619,8 +619,7 @@ UPDATE
619619
workspace_history
620620
SET
621621
updated_at = $2,
622-
completed_at = $3,
623-
after_id = $4,
624-
provisioner_state = $5
622+
after_id = $3,
623+
provisioner_state = $4
625624
WHERE
626625
id = $1;

0 commit comments

Comments
 (0)