feat: add README parsing to template versions (#1500)

This commit is contained in:
Colin Adler 2022-05-17 15:00:48 -05:00 committed by GitHub
parent 0f9559a784
commit 98ccd0eb89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 213 additions and 87 deletions

View File

@ -73,6 +73,10 @@
{
"match": "database/queries/*.sql",
"cmd": "make gen"
},
{
"match": "provisionerd/proto/provisionerd.proto",
"cmd": "make provisionerd/proto/provisionerd.pb.go",
}
]
},

View File

@ -78,7 +78,7 @@ var AuditableResources = auditMap(map[any]map[string]Action{
"created_at": ActionIgnore, // Never changes, but is implicit and not helpful in a diff.
"updated_at": ActionIgnore, // Changes, but is implicit and not helpful in a diff.
"name": ActionTrack,
"description": ActionTrack,
"readme": ActionTrack,
"job_id": ActionIgnore, // Not helpful in a diff because jobs aren't tracked in audit logs.
},
&database.User{}: {

View File

@ -175,8 +175,8 @@ func New(options *Options) (http.Handler, func()) {
r.Use(
apiKeyMiddleware,
httpmw.ExtractTemplateParam(options.Database),
httpmw.ExtractOrganizationParam(options.Database),
)
r.Get("/", api.template)
r.Delete("/", api.deleteTemplate)
r.Route("/versions", func(r chi.Router) {
@ -189,7 +189,6 @@ func New(options *Options) (http.Handler, func()) {
r.Use(
apiKeyMiddleware,
httpmw.ExtractTemplateVersionParam(options.Database),
httpmw.ExtractOrganizationParam(options.Database),
)
r.Get("/", api.templateVersion)

View File

@ -6,6 +6,7 @@ import (
"sort"
"strings"
"sync"
"time"
"github.com/google/uuid"
"golang.org/x/exp/slices"
@ -1189,7 +1190,7 @@ func (q *fakeQuerier) InsertTemplateVersion(_ context.Context, arg database.Inse
CreatedAt: arg.CreatedAt,
UpdatedAt: arg.UpdatedAt,
Name: arg.Name,
Description: arg.Description,
Readme: arg.Readme,
JobID: arg.JobID,
}
q.templateVersions = append(q.templateVersions, version)
@ -1478,7 +1479,7 @@ func (q *fakeQuerier) UpdateTemplateActiveVersionByID(_ context.Context, arg dat
defer q.mutex.Unlock()
for index, template := range q.templates {
if template.ID.String() != arg.ID.String() {
if template.ID != arg.ID {
continue
}
template.ActiveVersionID = arg.ActiveVersionID
@ -1493,7 +1494,7 @@ func (q *fakeQuerier) UpdateTemplateDeletedByID(_ context.Context, arg database.
defer q.mutex.Unlock()
for index, template := range q.templates {
if template.ID.String() != arg.ID.String() {
if template.ID != arg.ID {
continue
}
template.Deleted = arg.Deleted
@ -1508,7 +1509,7 @@ func (q *fakeQuerier) UpdateTemplateVersionByID(_ context.Context, arg database.
defer q.mutex.Unlock()
for index, templateVersion := range q.templateVersions {
if templateVersion.ID.String() != arg.ID.String() {
if templateVersion.ID != arg.ID {
continue
}
templateVersion.TemplateID = arg.TemplateID
@ -1519,12 +1520,28 @@ func (q *fakeQuerier) UpdateTemplateVersionByID(_ context.Context, arg database.
return sql.ErrNoRows
}
func (q *fakeQuerier) UpdateTemplateVersionDescriptionByJobID(_ context.Context, arg database.UpdateTemplateVersionDescriptionByJobIDParams) error {
q.mutex.Lock()
defer q.mutex.Unlock()
for index, templateVersion := range q.templateVersions {
if templateVersion.JobID != arg.JobID {
continue
}
templateVersion.Readme = arg.Readme
templateVersion.UpdatedAt = time.Now()
q.templateVersions[index] = templateVersion
return nil
}
return sql.ErrNoRows
}
func (q *fakeQuerier) UpdateProvisionerDaemonByID(_ context.Context, arg database.UpdateProvisionerDaemonByIDParams) error {
q.mutex.Lock()
defer q.mutex.Unlock()
for index, daemon := range q.provisionerDaemons {
if arg.ID.String() != daemon.ID.String() {
if arg.ID != daemon.ID {
continue
}
daemon.UpdatedAt = arg.UpdatedAt
@ -1540,7 +1557,7 @@ func (q *fakeQuerier) UpdateWorkspaceAgentConnectionByID(_ context.Context, arg
defer q.mutex.Unlock()
for index, agent := range q.provisionerJobAgents {
if agent.ID.String() != arg.ID.String() {
if agent.ID != arg.ID {
continue
}
agent.FirstConnectedAt = arg.FirstConnectedAt
@ -1557,7 +1574,7 @@ func (q *fakeQuerier) UpdateProvisionerJobByID(_ context.Context, arg database.U
defer q.mutex.Unlock()
for index, job := range q.provisionerJobs {
if arg.ID.String() != job.ID.String() {
if arg.ID != job.ID {
continue
}
job.UpdatedAt = arg.UpdatedAt
@ -1572,7 +1589,7 @@ func (q *fakeQuerier) UpdateProvisionerJobWithCancelByID(_ context.Context, arg
defer q.mutex.Unlock()
for index, job := range q.provisionerJobs {
if arg.ID.String() != job.ID.String() {
if arg.ID != job.ID {
continue
}
job.CanceledAt = arg.CanceledAt
@ -1587,7 +1604,7 @@ func (q *fakeQuerier) UpdateProvisionerJobWithCompleteByID(_ context.Context, ar
defer q.mutex.Unlock()
for index, job := range q.provisionerJobs {
if arg.ID.String() != job.ID.String() {
if arg.ID != job.ID {
continue
}
job.UpdatedAt = arg.UpdatedAt
@ -1604,7 +1621,7 @@ func (q *fakeQuerier) UpdateWorkspaceAutostart(_ context.Context, arg database.U
defer q.mutex.Unlock()
for index, workspace := range q.workspaces {
if workspace.ID.String() != arg.ID.String() {
if workspace.ID != arg.ID {
continue
}
workspace.AutostartSchedule = arg.AutostartSchedule
@ -1620,7 +1637,7 @@ func (q *fakeQuerier) UpdateWorkspaceAutostop(_ context.Context, arg database.Up
defer q.mutex.Unlock()
for index, workspace := range q.workspaces {
if workspace.ID.String() != arg.ID.String() {
if workspace.ID != arg.ID {
continue
}
workspace.AutostopSchedule = arg.AutostopSchedule
@ -1636,7 +1653,7 @@ func (q *fakeQuerier) UpdateWorkspaceBuildByID(_ context.Context, arg database.U
defer q.mutex.Unlock()
for index, workspaceBuild := range q.workspaceBuilds {
if workspaceBuild.ID.String() != arg.ID.String() {
if workspaceBuild.ID != arg.ID {
continue
}
workspaceBuild.UpdatedAt = arg.UpdatedAt
@ -1653,7 +1670,7 @@ func (q *fakeQuerier) UpdateWorkspaceDeletedByID(_ context.Context, arg database
defer q.mutex.Unlock()
for index, workspace := range q.workspaces {
if workspace.ID.String() != arg.ID.String() {
if workspace.ID != arg.ID {
continue
}
workspace.Deleted = arg.Deleted

View File

@ -234,7 +234,7 @@ CREATE TABLE template_versions (
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
name character varying(64) NOT NULL,
description character varying(1048576) NOT NULL,
readme character varying(1048576) NOT NULL,
job_id uuid NOT NULL
);

View File

@ -0,0 +1 @@
ALTER TABLE template_versions RENAME README TO description;

View File

@ -0,0 +1 @@
ALTER TABLE template_versions RENAME description TO readme;

View File

@ -446,7 +446,7 @@ type TemplateVersion struct {
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Name string `db:"name" json:"name"`
Description string `db:"description" json:"description"`
Readme string `db:"readme" json:"readme"`
JobID uuid.UUID `db:"job_id" json:"job_id"`
}

View File

@ -103,6 +103,7 @@ type querier interface {
UpdateTemplateActiveVersionByID(ctx context.Context, arg UpdateTemplateActiveVersionByIDParams) error
UpdateTemplateDeletedByID(ctx context.Context, arg UpdateTemplateDeletedByIDParams) error
UpdateTemplateVersionByID(ctx context.Context, arg UpdateTemplateVersionByIDParams) error
UpdateTemplateVersionDescriptionByJobID(ctx context.Context, arg UpdateTemplateVersionDescriptionByJobIDParams) error
UpdateUserHashedPassword(ctx context.Context, arg UpdateUserHashedPasswordParams) error
UpdateUserProfile(ctx context.Context, arg UpdateUserProfileParams) (User, error)
UpdateUserRoles(ctx context.Context, arg UpdateUserRolesParams) (User, error)

View File

@ -1830,7 +1830,7 @@ func (q *sqlQuerier) UpdateTemplateDeletedByID(ctx context.Context, arg UpdateTe
const getTemplateVersionByID = `-- name: GetTemplateVersionByID :one
SELECT
id, template_id, organization_id, created_at, updated_at, name, description, job_id
id, template_id, organization_id, created_at, updated_at, name, readme, job_id
FROM
template_versions
WHERE
@ -1847,7 +1847,7 @@ func (q *sqlQuerier) GetTemplateVersionByID(ctx context.Context, id uuid.UUID) (
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.Description,
&i.Readme,
&i.JobID,
)
return i, err
@ -1855,7 +1855,7 @@ func (q *sqlQuerier) GetTemplateVersionByID(ctx context.Context, id uuid.UUID) (
const getTemplateVersionByJobID = `-- name: GetTemplateVersionByJobID :one
SELECT
id, template_id, organization_id, created_at, updated_at, name, description, job_id
id, template_id, organization_id, created_at, updated_at, name, readme, job_id
FROM
template_versions
WHERE
@ -1872,7 +1872,7 @@ func (q *sqlQuerier) GetTemplateVersionByJobID(ctx context.Context, jobID uuid.U
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.Description,
&i.Readme,
&i.JobID,
)
return i, err
@ -1880,7 +1880,7 @@ func (q *sqlQuerier) GetTemplateVersionByJobID(ctx context.Context, jobID uuid.U
const getTemplateVersionByTemplateIDAndName = `-- name: GetTemplateVersionByTemplateIDAndName :one
SELECT
id, template_id, organization_id, created_at, updated_at, name, description, job_id
id, template_id, organization_id, created_at, updated_at, name, readme, job_id
FROM
template_versions
WHERE
@ -1903,7 +1903,7 @@ func (q *sqlQuerier) GetTemplateVersionByTemplateIDAndName(ctx context.Context,
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.Description,
&i.Readme,
&i.JobID,
)
return i, err
@ -1911,7 +1911,7 @@ func (q *sqlQuerier) GetTemplateVersionByTemplateIDAndName(ctx context.Context,
const getTemplateVersionsByTemplateID = `-- name: GetTemplateVersionsByTemplateID :many
SELECT
id, template_id, organization_id, created_at, updated_at, name, description, job_id
id, template_id, organization_id, created_at, updated_at, name, readme, job_id
FROM
template_versions
WHERE
@ -1972,7 +1972,7 @@ func (q *sqlQuerier) GetTemplateVersionsByTemplateID(ctx context.Context, arg Ge
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.Description,
&i.Readme,
&i.JobID,
); err != nil {
return nil, err
@ -1997,11 +1997,11 @@ INSERT INTO
created_at,
updated_at,
"name",
description,
readme,
job_id
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id, template_id, organization_id, created_at, updated_at, name, description, job_id
($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id, template_id, organization_id, created_at, updated_at, name, readme, job_id
`
type InsertTemplateVersionParams struct {
@ -2011,7 +2011,7 @@ type InsertTemplateVersionParams struct {
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Name string `db:"name" json:"name"`
Description string `db:"description" json:"description"`
Readme string `db:"readme" json:"readme"`
JobID uuid.UUID `db:"job_id" json:"job_id"`
}
@ -2023,7 +2023,7 @@ func (q *sqlQuerier) InsertTemplateVersion(ctx context.Context, arg InsertTempla
arg.CreatedAt,
arg.UpdatedAt,
arg.Name,
arg.Description,
arg.Readme,
arg.JobID,
)
var i TemplateVersion
@ -2034,7 +2034,7 @@ func (q *sqlQuerier) InsertTemplateVersion(ctx context.Context, arg InsertTempla
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.Description,
&i.Readme,
&i.JobID,
)
return i, err
@ -2061,6 +2061,26 @@ func (q *sqlQuerier) UpdateTemplateVersionByID(ctx context.Context, arg UpdateTe
return err
}
const updateTemplateVersionDescriptionByJobID = `-- name: UpdateTemplateVersionDescriptionByJobID :exec
UPDATE
template_versions
SET
readme = $2,
updated_at = now()
WHERE
job_id = $1
`
type UpdateTemplateVersionDescriptionByJobIDParams struct {
JobID uuid.UUID `db:"job_id" json:"job_id"`
Readme string `db:"readme" json:"readme"`
}
func (q *sqlQuerier) UpdateTemplateVersionDescriptionByJobID(ctx context.Context, arg UpdateTemplateVersionDescriptionByJobIDParams) error {
_, err := q.db.ExecContext(ctx, updateTemplateVersionDescriptionByJobID, arg.JobID, arg.Readme)
return err
}
const getAllUserRoles = `-- name: GetAllUserRoles :one
SELECT
-- username is returned just to help for logging purposes

View File

@ -66,7 +66,7 @@ INSERT INTO
created_at,
updated_at,
"name",
description,
readme,
job_id
)
VALUES
@ -80,3 +80,12 @@ SET
updated_at = $3
WHERE
id = $1;
-- name: UpdateTemplateVersionDescriptionByJobID :exec
UPDATE
template_versions
SET
readme = $2,
updated_at = now()
WHERE
job_id = $1;

View File

@ -348,6 +348,16 @@ func (server *provisionerdServer) UpdateJob(ctx context.Context, request *proto.
}
}
if len(request.Readme) > 0 {
err := server.Database.UpdateTemplateVersionDescriptionByJobID(ctx, database.UpdateTemplateVersionDescriptionByJobIDParams{
JobID: job.ID,
Readme: string(request.Readme),
})
if err != nil {
return nil, xerrors.Errorf("update template version description: %w", err)
}
}
if len(request.ParameterSchemas) > 0 {
for _, protoParameter := range request.ParameterSchemas {
validationTypeSystem, err := convertValidationTypeSystem(protoParameter.ValidationTypeSystem)

View File

@ -357,7 +357,7 @@ func (api *api) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht
CreatedAt: database.Now(),
UpdatedAt: database.Now(),
Name: namesgenerator.GetRandomName(1),
Description: "",
Readme: "",
JobID: provisionerJob.ID,
})
if err != nil {
@ -407,5 +407,6 @@ func convertTemplateVersion(version database.TemplateVersion, job codersdk.Provi
UpdatedAt: version.UpdatedAt,
Name: version.Name,
Job: job,
Readme: version.Readme,
}
}

View File

@ -216,7 +216,7 @@ func (api *api) workspaceAgentListen(rw http.ResponseWriter, r *http.Request) {
if err != nil {
return err
}
if build.ID.String() != latestBuild.ID.String() {
if build.ID != latestBuild.ID {
return xerrors.New("build is outdated")
}
return nil

View File

@ -144,7 +144,7 @@ func (api *api) handleAuthInstanceID(rw http.ResponseWriter, r *http.Request, in
})
return
}
if latestHistory.ID.String() != resourceHistory.ID.String() {
if latestHistory.ID != resourceHistory.ID {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("resource found for id %q, but isn't registered on the latest history", instanceID),
})

View File

@ -21,6 +21,7 @@ type TemplateVersion struct {
UpdatedAt time.Time `json:"updated_at"`
Name string `json:"name"`
Job ProvisionerJob `json:"job"`
Readme string `json:"readme"`
}
// TemplateVersionParameterSchema represents a parameter parsed from template version source.

View File

@ -9,7 +9,6 @@ import (
"path/filepath"
"golang.org/x/xerrors"
protobuf "google.golang.org/protobuf/proto"
"github.com/coder/coder/provisionersdk"

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.20.0
// protoc v3.19.4
// source: provisionerd/proto/provisionerd.proto
package proto
@ -502,6 +502,7 @@ type UpdateJobRequest struct {
JobId string `protobuf:"bytes,1,opt,name=job_id,json=jobId,proto3" json:"job_id,omitempty"`
Logs []*Log `protobuf:"bytes,2,rep,name=logs,proto3" json:"logs,omitempty"`
ParameterSchemas []*proto.ParameterSchema `protobuf:"bytes,3,rep,name=parameter_schemas,json=parameterSchemas,proto3" json:"parameter_schemas,omitempty"`
Readme []byte `protobuf:"bytes,4,opt,name=readme,proto3" json:"readme,omitempty"`
}
func (x *UpdateJobRequest) Reset() {
@ -557,6 +558,13 @@ func (x *UpdateJobRequest) GetParameterSchemas() []*proto.ParameterSchema {
return nil
}
func (x *UpdateJobRequest) GetReadme() []byte {
if x != nil {
return x.Readme
}
return nil
}
type UpdateJobResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -1046,7 +1054,7 @@ var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61,
0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x12,
0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52,
0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0x9b, 0x01, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61,
0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0xb3, 0x01, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61,
0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06,
0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f,
0x62, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
@ -1056,39 +1064,40 @@ var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{
0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x63, 0x68,
0x65, 0x6d, 0x61, 0x52, 0x10, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x63,
0x68, 0x65, 0x6d, 0x61, 0x73, 0x22, 0x77, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a,
0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61,
0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x63, 0x61,
0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x12, 0x46, 0x0a, 0x10, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65,
0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50,
0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0f, 0x70,
0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2a, 0x34,
0x0a, 0x09, 0x4c, 0x6f, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x50,
0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x45, 0x4d, 0x4f,
0x4e, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e,
0x45, 0x52, 0x10, 0x01, 0x32, 0x98, 0x02, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
0x6f, 0x6e, 0x65, 0x72, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x3c, 0x0a, 0x0a, 0x41, 0x63,
0x71, 0x75, 0x69, 0x72, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e,
0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x41, 0x63, 0x71,
0x75, 0x69, 0x72, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x12, 0x4c, 0x0a, 0x09, 0x55, 0x70, 0x64, 0x61,
0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
0x6e, 0x65, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
0x6e, 0x65, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x46, 0x61, 0x69, 0x6c, 0x4a, 0x6f,
0x62, 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64,
0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f,
0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12,
0x3e, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1a,
0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f,
0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f,
0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42,
0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f,
0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73,
0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
0x68, 0x65, 0x6d, 0x61, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x18,
0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x22, 0x77, 0x0a,
0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x12, 0x46,
0x0a, 0x10, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75,
0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72,
0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72,
0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2a, 0x34, 0x0a, 0x09, 0x4c, 0x6f, 0x67, 0x53, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e,
0x45, 0x52, 0x5f, 0x44, 0x41, 0x45, 0x4d, 0x4f, 0x4e, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x50,
0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x52, 0x10, 0x01, 0x32, 0x98, 0x02, 0x0a,
0x11, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x44, 0x61, 0x65, 0x6d,
0x6f, 0x6e, 0x12, 0x3c, 0x0a, 0x0a, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x4a, 0x6f, 0x62,
0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e,
0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
0x6e, 0x65, 0x72, 0x64, 0x2e, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x4a, 0x6f, 0x62,
0x12, 0x4c, 0x0a, 0x09, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1e, 0x2e,
0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x64,
0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e,
0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x64,
0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37,
0x0a, 0x07, 0x46, 0x61, 0x69, 0x6c, 0x4a, 0x6f, 0x62, 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76,
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a,
0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3e, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x6c,
0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a,
0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75,
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65,
0x72, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@ -83,6 +83,7 @@ message UpdateJobRequest {
string job_id = 1;
repeated Log logs = 2;
repeated provisioner.ParameterSchema parameter_schemas = 3;
bytes readme = 4;
}
message UpdateJobResponse {

View File

@ -8,6 +8,7 @@ import (
"fmt"
"io"
"os"
"path"
"path/filepath"
"reflect"
"strings"
@ -361,8 +362,8 @@ func (p *Server) runJob(ctx context.Context, job *proto.AcquiredJob) {
return
}
// #nosec
path := filepath.Join(p.opts.WorkDirectory, header.Name)
if !strings.HasPrefix(path, filepath.Clean(p.opts.WorkDirectory)) {
headerPath := filepath.Join(p.opts.WorkDirectory, header.Name)
if !strings.HasPrefix(headerPath, filepath.Clean(p.opts.WorkDirectory)) {
p.failActiveJobf("tar attempts to target relative upper directory")
return
}
@ -372,36 +373,36 @@ func (p *Server) runJob(ctx context.Context, job *proto.AcquiredJob) {
}
switch header.Typeflag {
case tar.TypeDir:
err = os.MkdirAll(path, mode)
err = os.MkdirAll(headerPath, mode)
if err != nil {
p.failActiveJobf("mkdir %q: %s", path, err)
p.failActiveJobf("mkdir %q: %s", headerPath, err)
return
}
p.opts.Logger.Debug(context.Background(), "extracted directory", slog.F("path", path))
p.opts.Logger.Debug(context.Background(), "extracted directory", slog.F("path", headerPath))
case tar.TypeReg:
file, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, mode)
file, err := os.OpenFile(headerPath, os.O_CREATE|os.O_RDWR, mode)
if err != nil {
p.failActiveJobf("create file %q (mode %s): %s", path, mode, err)
p.failActiveJobf("create file %q (mode %s): %s", headerPath, mode, err)
return
}
// Max file size of 10MB.
size, err := io.CopyN(file, reader, (1<<20)*10)
// Max file size of 10MiB.
size, err := io.CopyN(file, reader, 10<<20)
if errors.Is(err, io.EOF) {
err = nil
}
if err != nil {
_ = file.Close()
p.failActiveJobf("copy file %q: %s", path, err)
p.failActiveJobf("copy file %q: %s", headerPath, err)
return
}
err = file.Close()
if err != nil {
p.failActiveJobf("close file %q: %s", path, err)
p.failActiveJobf("close file %q: %s", headerPath, err)
return
}
p.opts.Logger.Debug(context.Background(), "extracted file",
slog.F("size_bytes", size),
slog.F("path", path),
slog.F("path", headerPath),
slog.F("mode", mode),
)
}
@ -411,6 +412,7 @@ func (p *Server) runJob(ctx context.Context, job *proto.AcquiredJob) {
case *proto.AcquiredJob_TemplateImport_:
p.opts.Logger.Debug(context.Background(), "acquired job is template import")
p.runReadmeParse(ctx, job)
p.runTemplateImport(ctx, shutdown, provisioner, job)
case *proto.AcquiredJob_WorkspaceBuild_:
p.opts.Logger.Debug(context.Background(), "acquired job is workspace provision",
@ -450,6 +452,51 @@ func (p *Server) runJob(ctx context.Context, job *proto.AcquiredJob) {
}
}
// ReadmeFile is the location we look for to extract documentation from template
// versions.
const ReadmeFile = "README.md"
func (p *Server) runReadmeParse(ctx context.Context, job *proto.AcquiredJob) {
client, ok := p.client()
if !ok {
p.failActiveJobf("client disconnected")
return
}
fi, err := os.ReadFile(path.Join(p.opts.WorkDirectory, ReadmeFile))
if err != nil {
_, err := client.UpdateJob(ctx, &proto.UpdateJobRequest{
JobId: job.GetJobId(),
Logs: []*proto.Log{{
Source: proto.LogSource_PROVISIONER_DAEMON,
Level: sdkproto.LogLevel_DEBUG,
Stage: "No README.md provided",
CreatedAt: time.Now().UTC().UnixMilli(),
}},
})
if err != nil {
p.failActiveJobf("write log: %s", err)
}
return
}
_, err = client.UpdateJob(ctx, &proto.UpdateJobRequest{
JobId: job.GetJobId(),
Logs: []*proto.Log{{
Source: proto.LogSource_PROVISIONER_DAEMON,
Level: sdkproto.LogLevel_INFO,
Stage: "Adding README.md...",
CreatedAt: time.Now().UTC().UnixMilli(),
}},
Readme: fi,
})
if err != nil {
p.failActiveJobf("write log: %s", err)
return
}
}
func (p *Server) runTemplateImport(ctx, shutdown context.Context, provisioner sdkproto.DRPCProvisionerClient, job *proto.AcquiredJob) {
client, ok := p.client()
if !ok {

View File

@ -214,6 +214,7 @@ func TestProvisionerd(t *testing.T) {
didLog atomic.Bool
didAcquireJob atomic.Bool
didDryRun atomic.Bool
didReadme atomic.Bool
completeChan = make(chan struct{})
completeOnce sync.Once
)
@ -230,7 +231,8 @@ func TestProvisionerd(t *testing.T) {
JobId: "test",
Provisioner: "someprovisioner",
TemplateSourceArchive: createTar(t, map[string]string{
"test.txt": "content",
"test.txt": "content",
provisionerd.ReadmeFile: "# A cool template 😎\n",
}),
Type: &proto.AcquiredJob_TemplateImport_{
TemplateImport: &proto.AcquiredJob_TemplateImport{
@ -240,9 +242,12 @@ func TestProvisionerd(t *testing.T) {
}, nil
},
updateJob: func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) {
if len(update.Logs) != 0 {
if len(update.Logs) > 0 {
didLog.Store(true)
}
if len(update.Readme) > 0 {
didReadme.Store(true)
}
return &proto.UpdateJobResponse{}, nil
},
completeJob: func(ctx context.Context, job *proto.CompletedJob) (*proto.Empty, error) {

View File

@ -231,9 +231,10 @@ export interface TemplateVersion {
readonly updated_at: string
readonly name: string
readonly job: ProvisionerJob
readonly readme: string
}
// From codersdk/templateversions.go:30:6
// From codersdk/templateversions.go:31:6
export interface TemplateVersionParameter {
// Named type "github.com/coder/coder/coderd/database.ParameterValue" unknown, using "any"
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@ -242,7 +243,7 @@ export interface TemplateVersionParameter {
readonly default_source_value: boolean
}
// From codersdk/templateversions.go:27:6
// From codersdk/templateversions.go:28:6
export interface TemplateVersionParameterSchema {
readonly id: string
readonly created_at: string