Skip to content

Commit 65de6ee

Browse files
authored
feat: Add streaming endpoint for workspace history (#157)
* feat: Add parameter querying to the API * feat: Add streaming endpoint for workspace history Enables a buildlog-like flow for reading job output. * Fix empty parameter source and destination * Add comment for usage of workspace history logs endpoint
1 parent 430341b commit 65de6ee

22 files changed

+688
-115
lines changed

coderd/coderd.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ func New(options *Options) http.Handler {
7272
r.Route("/{projecthistory}", func(r chi.Router) {
7373
r.Use(httpmw.ExtractProjectHistoryParam(api.Database))
7474
r.Get("/", api.projectHistoryByOrganizationAndName)
75+
r.Get("/parameters", api.projectHistoryParametersByOrganizationAndName)
7576
})
7677
})
7778
})
@@ -96,6 +97,7 @@ func New(options *Options) http.Handler {
9697
r.Route("/{workspacehistory}", func(r chi.Router) {
9798
r.Use(httpmw.ExtractWorkspaceHistoryParam(options.Database))
9899
r.Get("/", api.workspaceHistoryByName)
100+
r.Get("/logs", api.workspaceHistoryLogsByName)
99101
})
100102
})
101103
})

coderd/parameters.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ import (
1616

1717
// CreateParameterValueRequest is used to create a new parameter value for a scope.
1818
type CreateParameterValueRequest struct {
19-
Name string `json:"name"`
20-
SourceValue string `json:"source_value"`
21-
SourceScheme database.ParameterSourceScheme `json:"source_scheme"`
22-
DestinationScheme database.ParameterDestinationScheme `json:"destination_scheme"`
23-
DestinationValue string `json:"destination_value"`
19+
Name string `json:"name" validate:"required"`
20+
SourceValue string `json:"source_value" validate:"required"`
21+
SourceScheme database.ParameterSourceScheme `json:"source_scheme" validate:"oneof=data,required"`
22+
DestinationScheme database.ParameterDestinationScheme `json:"destination_scheme" validate:"oneof=environment_variable provisioner_variable,required"`
23+
DestinationValue string `json:"destination_value" validate:"required"`
2424
}
2525

2626
// ParameterValue represents a set value for the scope.

coderd/projecthistory.go

Lines changed: 94 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,27 @@ type ProjectHistory struct {
3131
Import ProvisionerJob `json:"import"`
3232
}
3333

34+
// ProjectParameter represents a parameter parsed from project history source on creation.
35+
type ProjectParameter struct {
36+
ID uuid.UUID `json:"id"`
37+
CreatedAt time.Time `json:"created_at"`
38+
ProjectHistoryID uuid.UUID `json:"project_history_id"`
39+
Name string `json:"name"`
40+
Description string `json:"description,omitempty"`
41+
DefaultSourceScheme database.ParameterSourceScheme `json:"default_source_scheme,omitempty"`
42+
DefaultSourceValue string `json:"default_source_value,omitempty"`
43+
AllowOverrideSource bool `json:"allow_override_source"`
44+
DefaultDestinationScheme database.ParameterDestinationScheme `json:"default_destination_scheme,omitempty"`
45+
DefaultDestinationValue string `json:"default_destination_value,omitempty"`
46+
AllowOverrideDestination bool `json:"allow_override_destination"`
47+
DefaultRefresh string `json:"default_refresh"`
48+
RedisplayValue bool `json:"redisplay_value"`
49+
ValidationError string `json:"validation_error,omitempty"`
50+
ValidationCondition string `json:"validation_condition,omitempty"`
51+
ValidationTypeSystem database.ParameterTypeSystem `json:"validation_type_system,omitempty"`
52+
ValidationValueType string `json:"validation_value_type,omitempty"`
53+
}
54+
3455
// CreateProjectHistoryRequest enables callers to create a new Project Version.
3556
type CreateProjectHistoryRequest struct {
3657
StorageMethod database.ProjectStorageMethod `json:"storage_method" validate:"oneof=inline-archive,required"`
@@ -160,14 +181,81 @@ func (api *api) postProjectHistoryByOrganization(rw http.ResponseWriter, r *http
160181
render.JSON(rw, r, convertProjectHistory(projectHistory, provisionerJob))
161182
}
162183

184+
func (api *api) projectHistoryParametersByOrganizationAndName(rw http.ResponseWriter, r *http.Request) {
185+
projectHistory := httpmw.ProjectHistoryParam(r)
186+
job, err := api.Database.GetProvisionerJobByID(r.Context(), projectHistory.ImportJobID)
187+
if err != nil {
188+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
189+
Message: fmt.Sprintf("get provisioner job: %s", err),
190+
})
191+
return
192+
}
193+
apiJob := convertProvisionerJob(job)
194+
if !apiJob.Status.Completed() {
195+
httpapi.Write(rw, http.StatusPreconditionRequired, httpapi.Response{
196+
Message: fmt.Sprintf("import job hasn't completed: %s", apiJob.Status),
197+
})
198+
return
199+
}
200+
if apiJob.Status != ProvisionerJobStatusSucceeded {
201+
httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{
202+
Message: "import job wasn't successful. no parameters were parsed",
203+
})
204+
return
205+
}
206+
207+
parameters, err := api.Database.GetProjectParametersByHistoryID(r.Context(), projectHistory.ID)
208+
if errors.Is(err, sql.ErrNoRows) {
209+
err = nil
210+
parameters = []database.ProjectParameter{}
211+
}
212+
if err != nil {
213+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
214+
Message: fmt.Sprintf("get project parameters: %s", err),
215+
})
216+
return
217+
}
218+
219+
apiParameters := make([]ProjectParameter, 0, len(parameters))
220+
for _, parameter := range parameters {
221+
apiParameters = append(apiParameters, convertProjectParameter(parameter))
222+
}
223+
224+
render.Status(r, http.StatusOK)
225+
render.JSON(rw, r, apiParameters)
226+
}
227+
163228
func convertProjectHistory(history database.ProjectHistory, job database.ProvisionerJob) ProjectHistory {
164229
return ProjectHistory{
165-
ID: history.ID,
166-
ProjectID: history.ProjectID,
167-
CreatedAt: history.CreatedAt,
168-
UpdatedAt: history.UpdatedAt,
169-
Name: history.Name,
170-
Import: convertProvisionerJob(job),
230+
ID: history.ID,
231+
ProjectID: history.ProjectID,
232+
CreatedAt: history.CreatedAt,
233+
UpdatedAt: history.UpdatedAt,
234+
Name: history.Name,
235+
StorageMethod: history.StorageMethod,
236+
Import: convertProvisionerJob(job),
237+
}
238+
}
239+
240+
func convertProjectParameter(parameter database.ProjectParameter) ProjectParameter {
241+
return ProjectParameter{
242+
ID: parameter.ID,
243+
CreatedAt: parameter.CreatedAt,
244+
ProjectHistoryID: parameter.ProjectHistoryID,
245+
Name: parameter.Name,
246+
Description: parameter.Description,
247+
DefaultSourceScheme: parameter.DefaultSourceScheme,
248+
DefaultSourceValue: parameter.DefaultSourceValue.String,
249+
AllowOverrideSource: parameter.AllowOverrideSource,
250+
DefaultDestinationScheme: parameter.DefaultDestinationScheme,
251+
DefaultDestinationValue: parameter.DefaultDestinationValue.String,
252+
AllowOverrideDestination: parameter.AllowOverrideDestination,
253+
DefaultRefresh: parameter.DefaultRefresh,
254+
RedisplayValue: parameter.RedisplayValue,
255+
ValidationError: parameter.ValidationError,
256+
ValidationCondition: parameter.ValidationCondition,
257+
ValidationTypeSystem: parameter.ValidationTypeSystem,
258+
ValidationValueType: parameter.ValidationValueType,
171259
}
172260
}
173261

coderd/projects_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package coderd_test
22

33
import (
4+
"archive/tar"
5+
"bytes"
46
"context"
57
"testing"
8+
"time"
69

710
"github.com/stretchr/testify/require"
811

@@ -124,4 +127,42 @@ func TestProjects(t *testing.T) {
124127
})
125128
require.NoError(t, err)
126129
})
130+
131+
t.Run("Import", func(t *testing.T) {
132+
t.Parallel()
133+
server := coderdtest.New(t)
134+
user := server.RandomInitialUser(t)
135+
_ = server.AddProvisionerd(t)
136+
project, err := server.Client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
137+
Name: "someproject",
138+
Provisioner: database.ProvisionerTypeTerraform,
139+
})
140+
require.NoError(t, err)
141+
var buffer bytes.Buffer
142+
writer := tar.NewWriter(&buffer)
143+
content := `variable "example" {
144+
default = "hi"
145+
}`
146+
err = writer.WriteHeader(&tar.Header{
147+
Name: "main.tf",
148+
Size: int64(len(content)),
149+
})
150+
require.NoError(t, err)
151+
_, err = writer.Write([]byte(content))
152+
require.NoError(t, err)
153+
history, err := server.Client.CreateProjectHistory(context.Background(), user.Organization, project.Name, coderd.CreateProjectHistoryRequest{
154+
StorageMethod: database.ProjectStorageMethodInlineArchive,
155+
StorageSource: buffer.Bytes(),
156+
})
157+
require.NoError(t, err)
158+
require.Eventually(t, func() bool {
159+
projectHistory, err := server.Client.ProjectHistory(context.Background(), user.Organization, project.Name, history.Name)
160+
require.NoError(t, err)
161+
return projectHistory.Import.Status.Completed()
162+
}, 15*time.Second, 10*time.Millisecond)
163+
params, err := server.Client.ProjectHistoryParameters(context.Background(), user.Organization, project.Name, history.Name)
164+
require.NoError(t, err)
165+
require.Len(t, params, 1)
166+
require.Equal(t, "example", params[0].Name)
167+
})
127168
}

coderd/provisionerdaemons.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,9 @@ func (server *provisionerdServer) CompleteJob(ctx context.Context, completed *pr
451451
ValidationValueType: protoParameter.ValidationValueType,
452452
ValidationTypeSystem: validationTypeSystem,
453453

454+
DefaultSourceScheme: database.ParameterSourceSchemeNone,
455+
DefaultDestinationScheme: database.ParameterDestinationSchemeNone,
456+
454457
AllowOverrideDestination: protoParameter.AllowOverrideDestination,
455458
AllowOverrideSource: protoParameter.AllowOverrideSource,
456459
}
@@ -574,6 +577,8 @@ func (server *provisionerdServer) CompleteJob(ctx context.Context, completed *pr
574577

575578
func convertValidationTypeSystem(typeSystem sdkproto.ParameterSchema_TypeSystem) (database.ParameterTypeSystem, error) {
576579
switch typeSystem {
580+
case sdkproto.ParameterSchema_None:
581+
return database.ParameterTypeSystemNone, nil
577582
case sdkproto.ParameterSchema_HCL:
578583
return database.ParameterTypeSystemHCL, nil
579584
default:

coderd/workspacehistory.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
"github.com/go-chi/render"
1212
"github.com/google/uuid"
13+
"github.com/moby/moby/pkg/namesgenerator"
1314
"golang.org/x/xerrors"
1415

1516
"github.com/coder/coder/database"
@@ -155,6 +156,7 @@ func (api *api) postWorkspaceHistoryByUser(rw http.ResponseWriter, r *http.Reque
155156
WorkspaceID: workspace.ID,
156157
ProjectHistoryID: projectHistory.ID,
157158
BeforeID: priorHistoryID,
159+
Name: namesgenerator.GetRandomName(1),
158160
Initiator: user.ID,
159161
Transition: createBuild.Transition,
160162
ProvisionJobID: provisionerJob.ID,
@@ -253,7 +255,3 @@ func convertWorkspaceHistory(workspaceHistory database.WorkspaceHistory, provisi
253255
Provision: convertProvisionerJob(provisionerJob),
254256
})
255257
}
256-
257-
func workspaceHistoryLogsChannel(workspaceHistoryID uuid.UUID) string {
258-
return fmt.Sprintf("workspace-history-logs:%s", workspaceHistoryID)
259-
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy