Skip to content

Commit da9fc00

Browse files
committed
add support for example id in route
1 parent 300a763 commit da9fc00

File tree

4 files changed

+80
-12
lines changed

4 files changed

+80
-12
lines changed

coderd/files.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ import (
1919
"github.com/coder/coder/codersdk"
2020
)
2121

22+
const (
23+
uploadFileContentTypeHeader = "application/x-tar"
24+
)
25+
2226
func (api *API) postFile(rw http.ResponseWriter, r *http.Request) {
2327
ctx := r.Context()
2428
apiKey := httpmw.APIKey(r)
@@ -32,7 +36,7 @@ func (api *API) postFile(rw http.ResponseWriter, r *http.Request) {
3236
contentType := r.Header.Get("Content-Type")
3337

3438
switch contentType {
35-
case "application/x-tar":
39+
case uploadFileContentTypeHeader:
3640
default:
3741
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
3842
Message: fmt.Sprintf("Unsupported content type header %q.", contentType),

coderd/templateversions.go

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

33
import (
44
"context"
5+
"crypto/sha256"
56
"database/sql"
7+
"encoding/hex"
68
"encoding/json"
79
"errors"
810
"fmt"
@@ -23,6 +25,7 @@ import (
2325
"github.com/coder/coder/coderd/provisionerdserver"
2426
"github.com/coder/coder/coderd/rbac"
2527
"github.com/coder/coder/codersdk"
28+
"github.com/coder/coder/examples"
2629
)
2730

2831
func (api *API) templateVersion(rw http.ResponseWriter, r *http.Request) {
@@ -834,19 +837,79 @@ func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht
834837
// Ensures the "owner" is properly applied.
835838
tags := provisionerdserver.MutateTags(apiKey.UserID, req.ProvisionerTags)
836839

837-
file, err := api.Database.GetFileByID(ctx, req.FileID)
838-
if errors.Is(err, sql.ErrNoRows) {
839-
httpapi.Write(ctx, rw, http.StatusNotFound, codersdk.Response{
840-
Message: "File not found.",
840+
if req.ExampleID != "" && req.FileID != uuid.Nil {
841+
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
842+
Message: "You cannot specify both an example_id and a file_id.",
841843
})
842844
return
843845
}
844-
if err != nil {
845-
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
846-
Message: "Internal error fetching file.",
847-
Detail: err.Error(),
846+
847+
var file database.File
848+
var err error
849+
// if example id is specified we need to copy the embedded tar into a new file in the database
850+
if req.ExampleID != "" {
851+
if !api.Authorize(r, rbac.ActionCreate, rbac.ResourceFile.WithOwner(apiKey.UserID.String())) {
852+
httpapi.Forbidden(rw)
853+
return
854+
}
855+
// ensure we can read the file that either already exists or will be created
856+
if !api.Authorize(r, rbac.ActionRead, rbac.ResourceFile.WithOwner(apiKey.UserID.String())) {
857+
httpapi.Forbidden(rw)
858+
return
859+
}
860+
861+
// lookup template tar from embedded examples
862+
tar, err := examples.Archive(req.ExampleID)
863+
if err != nil {
864+
if xerrors.Is(err, examples.ErrNotFound) {
865+
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
866+
Message: "Example not found.",
867+
Detail: err.Error(),
868+
})
869+
return
870+
}
871+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
872+
Message: "Internal error fetching example.",
873+
Detail: err.Error(),
874+
})
875+
return
876+
}
877+
878+
// upload a copy of the template tar as a file in the database
879+
hashBytes := sha256.Sum256(tar)
880+
hash := hex.EncodeToString(hashBytes[:])
881+
file, err = api.Database.InsertFile(ctx, database.InsertFileParams{
882+
ID: uuid.New(),
883+
Hash: hash,
884+
CreatedBy: apiKey.UserID,
885+
CreatedAt: database.Now(),
886+
Mimetype: uploadFileContentTypeHeader,
887+
Data: tar,
848888
})
849-
return
889+
if err != nil {
890+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
891+
Message: "Internal error creating file.",
892+
Detail: err.Error(),
893+
})
894+
return
895+
}
896+
}
897+
898+
if req.FileID != uuid.Nil {
899+
file, err = api.Database.GetFileByID(ctx, req.FileID)
900+
if errors.Is(err, sql.ErrNoRows) {
901+
httpapi.Write(ctx, rw, http.StatusNotFound, codersdk.Response{
902+
Message: "File not found.",
903+
})
904+
return
905+
}
906+
if err != nil {
907+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
908+
Message: "Internal error fetching file.",
909+
Detail: err.Error(),
910+
})
911+
return
912+
}
850913
}
851914

852915
if !api.Authorize(r, rbac.ActionRead, file) {

codersdk/organizations.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ type CreateTemplateVersionRequest struct {
3939
TemplateID uuid.UUID `json:"template_id,omitempty"`
4040
StorageMethod ProvisionerStorageMethod `json:"storage_method" validate:"oneof=file,required"`
4141
FileID uuid.UUID `json:"file_id,omitempty" validate:"required_without=ExampleID"`
42-
ExampleID uuid.UUID `json:"example_id,omitempty" validate:"required_without=FileID"`
42+
ExampleID string `json:"example_id,omitempty" validate:"required_without=FileID"`
4343
Provisioner ProvisionerType `json:"provisioner" validate:"oneof=terraform echo,required"`
4444
ProvisionerTags map[string]string `json:"tags"`
4545

examples/examples.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ var (
2424
examples = make([]codersdk.TemplateExample, 0)
2525
parseExamples sync.Once
2626
archives = singleflight.Group{}
27+
ErrNotFound = xerrors.New("example not found")
2728
)
2829

2930
const rootDir = "templates"
@@ -116,7 +117,7 @@ func Archive(exampleID string) ([]byte, error) {
116117
}
117118

118119
if selected.ID == "" {
119-
return nil, xerrors.Errorf("example with id %q not found", exampleID)
120+
return nil, xerrors.Errorf("example with id %q not found: %w", exampleID, ErrNotFound)
120121
}
121122

122123
exampleFiles, err := fs.Sub(files, path.Join(rootDir, exampleID))

0 commit comments

Comments
 (0)