mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
refactor: Allow provisioner jobs to be disconnected from projects (#194)
* Nest jobs under an organization * Rename project parameter to parameter schema * Update references when computing project parameters * Add files endpoint * Allow one-off project import jobs * Allow variables to be injected that are not defined by the schema * Update API to use jobs first * Fix CLI tests * Fix linting * Fix hex length for files table * Reduce memory allocation for windows
This commit is contained in:
+16
-3
@@ -102,14 +102,27 @@ func New(options *Options) http.Handler {
|
||||
})
|
||||
})
|
||||
|
||||
r.Route("/files", func(r chi.Router) {
|
||||
r.Use(httpmw.ExtractAPIKey(options.Database, nil))
|
||||
r.Post("/", api.postFiles)
|
||||
})
|
||||
|
||||
r.Route("/provisioners", func(r chi.Router) {
|
||||
r.Route("/daemons", func(r chi.Router) {
|
||||
r.Get("/", api.provisionerDaemons)
|
||||
r.Get("/serve", api.provisionerDaemonsServe)
|
||||
})
|
||||
r.Route("/jobs/{provisionerjob}", func(r chi.Router) {
|
||||
r.Use(httpmw.ExtractProvisionerJobParam(options.Database))
|
||||
r.Get("/logs", api.provisionerJobLogsByID)
|
||||
r.Route("/jobs/{organization}", func(r chi.Router) {
|
||||
r.Use(
|
||||
httpmw.ExtractAPIKey(options.Database, nil),
|
||||
httpmw.ExtractOrganizationParam(options.Database),
|
||||
)
|
||||
r.Post("/import", api.postProvisionerImportJobByOrganization)
|
||||
r.Route("/{provisionerjob}", func(r chi.Router) {
|
||||
r.Use(httpmw.ExtractProvisionerJobParam(options.Database))
|
||||
r.Get("/", api.provisionerJobByOrganization)
|
||||
r.Get("/logs", api.provisionerJobLogsByID)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -122,40 +122,44 @@ func CreateInitialUser(t *testing.T, client *codersdk.Client) coderd.CreateIniti
|
||||
return req
|
||||
}
|
||||
|
||||
// CreateProjectImportProvisionerJob creates a project import provisioner job
|
||||
// with the responses provided. It uses the "echo" provisioner for compatibility
|
||||
// with testing.
|
||||
func CreateProjectImportProvisionerJob(t *testing.T, client *codersdk.Client, organization string, res *echo.Responses) coderd.ProvisionerJob {
|
||||
data, err := echo.Tar(res)
|
||||
require.NoError(t, err)
|
||||
file, err := client.UploadFile(context.Background(), codersdk.ContentTypeTar, data)
|
||||
require.NoError(t, err)
|
||||
job, err := client.CreateProjectVersionImportProvisionerJob(context.Background(), organization, coderd.CreateProjectImportJobRequest{
|
||||
StorageSource: file.Hash,
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return job
|
||||
}
|
||||
|
||||
// CreateProject creates a project with the "echo" provisioner for
|
||||
// compatibility with testing. The name assigned is randomly generated.
|
||||
func CreateProject(t *testing.T, client *codersdk.Client, organization string) coderd.Project {
|
||||
func CreateProject(t *testing.T, client *codersdk.Client, organization string, job uuid.UUID) coderd.Project {
|
||||
project, err := client.CreateProject(context.Background(), organization, coderd.CreateProjectRequest{
|
||||
Name: randomUsername(),
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
Name: randomUsername(),
|
||||
VersionImportJobID: job,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return project
|
||||
}
|
||||
|
||||
// CreateProjectVersion creates a project version for the "echo" provisioner
|
||||
// for compatibility with testing.
|
||||
func CreateProjectVersion(t *testing.T, client *codersdk.Client, organization, project string, responses *echo.Responses) coderd.ProjectVersion {
|
||||
data, err := echo.Tar(responses)
|
||||
require.NoError(t, err)
|
||||
version, err := client.CreateProjectVersion(context.Background(), organization, project, coderd.CreateProjectVersionRequest{
|
||||
StorageMethod: database.ProjectStorageMethodInlineArchive,
|
||||
StorageSource: data,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return version
|
||||
}
|
||||
|
||||
// AwaitProjectVersionImported awaits for the project import job to reach completed status.
|
||||
func AwaitProjectVersionImported(t *testing.T, client *codersdk.Client, organization, project, version string) coderd.ProjectVersion {
|
||||
var projectVersion coderd.ProjectVersion
|
||||
// AwaitProvisionerJob awaits for a job to reach completed status.
|
||||
func AwaitProvisionerJob(t *testing.T, client *codersdk.Client, organization string, job uuid.UUID) coderd.ProvisionerJob {
|
||||
var provisionerJob coderd.ProvisionerJob
|
||||
require.Eventually(t, func() bool {
|
||||
var err error
|
||||
projectVersion, err = client.ProjectVersion(context.Background(), organization, project, version)
|
||||
provisionerJob, err = client.ProvisionerJob(context.Background(), organization, job)
|
||||
require.NoError(t, err)
|
||||
return projectVersion.Import.Status.Completed()
|
||||
return provisionerJob.Status.Completed()
|
||||
}, 3*time.Second, 25*time.Millisecond)
|
||||
return projectVersion
|
||||
return provisionerJob
|
||||
}
|
||||
|
||||
// CreateWorkspace creates a workspace for the user and project provided.
|
||||
@@ -169,18 +173,6 @@ func CreateWorkspace(t *testing.T, client *codersdk.Client, user string, project
|
||||
return workspace
|
||||
}
|
||||
|
||||
// AwaitWorkspaceHistoryProvisioned awaits for the workspace provision job to reach completed status.
|
||||
func AwaitWorkspaceHistoryProvisioned(t *testing.T, client *codersdk.Client, user, workspace, history string) coderd.WorkspaceHistory {
|
||||
var workspaceHistory coderd.WorkspaceHistory
|
||||
require.Eventually(t, func() bool {
|
||||
var err error
|
||||
workspaceHistory, err = client.WorkspaceHistory(context.Background(), user, workspace, history)
|
||||
require.NoError(t, err)
|
||||
return workspaceHistory.Provision.Status.Completed()
|
||||
}, 3*time.Second, 25*time.Millisecond)
|
||||
return workspaceHistory
|
||||
}
|
||||
|
||||
func randomUsername() string {
|
||||
return strings.ReplaceAll(namesgenerator.GetRandomName(0), "_", "-")
|
||||
}
|
||||
|
||||
@@ -22,15 +22,15 @@ func TestNew(t *testing.T) {
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
closer := coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, nil)
|
||||
coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
|
||||
history, err := client.CreateWorkspaceHistory(context.Background(), "me", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: version.ID,
|
||||
ProjectVersionID: project.ActiveVersionID,
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
coderdtest.AwaitWorkspaceHistoryProvisioned(t, client, "me", workspace.Name, history.Name)
|
||||
coderdtest.AwaitProvisionerJob(t, client, user.Organization, history.ProvisionJobID)
|
||||
closer.Close()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package coderd
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/render"
|
||||
|
||||
"github.com/coder/coder/database"
|
||||
"github.com/coder/coder/httpapi"
|
||||
"github.com/coder/coder/httpmw"
|
||||
)
|
||||
|
||||
type UploadFileResponse struct {
|
||||
Hash string `json:"hash"`
|
||||
}
|
||||
|
||||
func (api *api) postFiles(rw http.ResponseWriter, r *http.Request) {
|
||||
apiKey := httpmw.APIKey(r)
|
||||
contentType := r.Header.Get("Content-Type")
|
||||
|
||||
switch contentType {
|
||||
case "application/x-tar":
|
||||
default:
|
||||
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
|
||||
Message: fmt.Sprintf("unsupported content type: %s", contentType),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
r.Body = http.MaxBytesReader(rw, r.Body, 10*(10<<20))
|
||||
data, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
|
||||
Message: fmt.Sprintf("read file: %s", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
hashBytes := sha256.Sum256(data)
|
||||
file, err := api.Database.InsertFile(r.Context(), database.InsertFileParams{
|
||||
Hash: hex.EncodeToString(hashBytes[:]),
|
||||
CreatedBy: apiKey.UserID,
|
||||
CreatedAt: database.Now(),
|
||||
Mimetype: contentType,
|
||||
Data: data,
|
||||
})
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("insert file: %s", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
render.Status(r, http.StatusCreated)
|
||||
render.JSON(rw, r, UploadFileResponse{
|
||||
Hash: file.Hash,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package coderd_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/coderd/coderdtest"
|
||||
"github.com/coder/coder/codersdk"
|
||||
)
|
||||
|
||||
func TestPostFiles(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("BadContentType", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
_ = coderdtest.CreateInitialUser(t, client)
|
||||
_, err := client.UploadFile(context.Background(), "bad", []byte{'a'})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("Insert", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
_ = coderdtest.CreateInitialUser(t, client)
|
||||
_, err := client.UploadFile(context.Background(), codersdk.ContentTypeTar, make([]byte, 1024))
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
@@ -15,11 +15,11 @@ import (
|
||||
|
||||
// Scope targets identifiers to pull parameters from.
|
||||
type Scope struct {
|
||||
OrganizationID string
|
||||
ProjectID uuid.UUID
|
||||
ProjectVersionID uuid.UUID
|
||||
UserID sql.NullString
|
||||
WorkspaceID uuid.NullUUID
|
||||
ImportJobID uuid.UUID
|
||||
OrganizationID string
|
||||
ProjectID uuid.NullUUID
|
||||
UserID sql.NullString
|
||||
WorkspaceID uuid.NullUUID
|
||||
}
|
||||
|
||||
// Value represents a computed parameter.
|
||||
@@ -35,29 +35,27 @@ type Value struct {
|
||||
// Compute accepts a scope in which parameter values are sourced.
|
||||
// These sources are iterated in a hierarchical fashion to determine
|
||||
// the runtime parameter values for a project.
|
||||
func Compute(ctx context.Context, db database.Store, scope Scope) ([]Value, error) {
|
||||
func Compute(ctx context.Context, db database.Store, scope Scope, additional ...database.ParameterValue) ([]Value, error) {
|
||||
compute := &compute{
|
||||
db: db,
|
||||
computedParameterByName: map[string]Value{},
|
||||
projectVersionParametersByName: map[string]database.ProjectVersionParameter{},
|
||||
db: db,
|
||||
computedParameterByName: map[string]Value{},
|
||||
parameterSchemasByName: map[string]database.ParameterSchema{},
|
||||
}
|
||||
|
||||
// All parameters for the project version!
|
||||
projectVersionParameters, err := db.GetProjectVersionParametersByVersionID(ctx, scope.ProjectVersionID)
|
||||
// All parameters for the import job ID!
|
||||
parameterSchemas, err := db.GetParameterSchemasByJobID(ctx, scope.ImportJobID)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
// This occurs when the project version has defined
|
||||
// no parameters, so we have nothing to compute!
|
||||
return []Value{}, nil
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("get project parameters: %w", err)
|
||||
}
|
||||
for _, projectVersionParameter := range projectVersionParameters {
|
||||
compute.projectVersionParametersByName[projectVersionParameter.Name] = projectVersionParameter
|
||||
for _, projectVersionParameter := range parameterSchemas {
|
||||
compute.parameterSchemasByName[projectVersionParameter.Name] = projectVersionParameter
|
||||
}
|
||||
|
||||
// Organization parameters come first!
|
||||
err = compute.inject(ctx, database.GetParameterValuesByScopeParams{
|
||||
err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{
|
||||
Scope: database.ParameterScopeOrganization,
|
||||
ScopeID: scope.OrganizationID,
|
||||
})
|
||||
@@ -66,7 +64,7 @@ func Compute(ctx context.Context, db database.Store, scope Scope) ([]Value, erro
|
||||
}
|
||||
|
||||
// Default project parameter values come second!
|
||||
for _, projectVersionParameter := range projectVersionParameters {
|
||||
for _, projectVersionParameter := range parameterSchemas {
|
||||
if !projectVersionParameter.DefaultSourceValue.Valid {
|
||||
continue
|
||||
}
|
||||
@@ -89,25 +87,27 @@ func Compute(ctx context.Context, db database.Store, scope Scope) ([]Value, erro
|
||||
},
|
||||
DefaultValue: true,
|
||||
Scope: database.ParameterScopeProject,
|
||||
ScopeID: scope.ProjectID.String(),
|
||||
ScopeID: scope.ProjectID.UUID.String(),
|
||||
}
|
||||
default:
|
||||
return nil, xerrors.Errorf("unsupported source scheme for project version parameter %q: %q", projectVersionParameter.Name, string(projectVersionParameter.DefaultSourceScheme))
|
||||
}
|
||||
}
|
||||
|
||||
// Project parameters come third!
|
||||
err = compute.inject(ctx, database.GetParameterValuesByScopeParams{
|
||||
Scope: database.ParameterScopeProject,
|
||||
ScopeID: scope.ProjectID.String(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if scope.ProjectID.Valid {
|
||||
// Project parameters come third!
|
||||
err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{
|
||||
Scope: database.ParameterScopeProject,
|
||||
ScopeID: scope.ProjectID.UUID.String(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if scope.UserID.Valid {
|
||||
// User parameters come fourth!
|
||||
err = compute.inject(ctx, database.GetParameterValuesByScopeParams{
|
||||
err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{
|
||||
Scope: database.ParameterScopeUser,
|
||||
ScopeID: scope.UserID.String,
|
||||
})
|
||||
@@ -118,7 +118,7 @@ func Compute(ctx context.Context, db database.Store, scope Scope) ([]Value, erro
|
||||
|
||||
if scope.WorkspaceID.Valid {
|
||||
// Workspace parameters come last!
|
||||
err = compute.inject(ctx, database.GetParameterValuesByScopeParams{
|
||||
err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{
|
||||
Scope: database.ParameterScopeWorkspace,
|
||||
ScopeID: scope.WorkspaceID.UUID.String(),
|
||||
})
|
||||
@@ -127,7 +127,14 @@ func Compute(ctx context.Context, db database.Store, scope Scope) ([]Value, erro
|
||||
}
|
||||
}
|
||||
|
||||
for _, projectVersionParameter := range compute.projectVersionParametersByName {
|
||||
for _, parameterValue := range additional {
|
||||
err = compute.injectSingle(parameterValue)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("inject %q: %w", parameterValue.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, projectVersionParameter := range compute.parameterSchemasByName {
|
||||
if _, ok := compute.computedParameterByName[projectVersionParameter.Name]; ok {
|
||||
continue
|
||||
}
|
||||
@@ -145,13 +152,13 @@ func Compute(ctx context.Context, db database.Store, scope Scope) ([]Value, erro
|
||||
}
|
||||
|
||||
type compute struct {
|
||||
db database.Store
|
||||
computedParameterByName map[string]Value
|
||||
projectVersionParametersByName map[string]database.ProjectVersionParameter
|
||||
db database.Store
|
||||
computedParameterByName map[string]Value
|
||||
parameterSchemasByName map[string]database.ParameterSchema
|
||||
}
|
||||
|
||||
// Validates and computes the value for parameters; setting the value on "parameterByName".
|
||||
func (c *compute) inject(ctx context.Context, scopeParams database.GetParameterValuesByScopeParams) error {
|
||||
func (c *compute) injectScope(ctx context.Context, scopeParams database.GetParameterValuesByScopeParams) error {
|
||||
scopedParameters, err := c.db.GetParameterValuesByScope(ctx, scopeParams)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
err = nil
|
||||
@@ -161,39 +168,45 @@ func (c *compute) inject(ctx context.Context, scopeParams database.GetParameterV
|
||||
}
|
||||
|
||||
for _, scopedParameter := range scopedParameters {
|
||||
projectVersionParameter, hasProjectVersionParameter := c.projectVersionParametersByName[scopedParameter.Name]
|
||||
if !hasProjectVersionParameter {
|
||||
// Don't inject parameters that aren't defined by the project.
|
||||
continue
|
||||
err = c.injectSingle(scopedParameter)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("inject single %q: %w", scopedParameter.Name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *compute) injectSingle(scopedParameter database.ParameterValue) error {
|
||||
parameterSchema, hasParameterSchema := c.parameterSchemasByName[scopedParameter.Name]
|
||||
if hasParameterSchema {
|
||||
// Don't inject parameters that aren't defined by the project.
|
||||
_, hasExistingParameter := c.computedParameterByName[scopedParameter.Name]
|
||||
if hasExistingParameter {
|
||||
// If a parameter already exists, check if this variable can override it.
|
||||
// Injection hierarchy is the responsibility of the caller. This check ensures
|
||||
// project parameters cannot be overridden if already set.
|
||||
if !projectVersionParameter.AllowOverrideSource && scopedParameter.Scope != database.ParameterScopeProject {
|
||||
continue
|
||||
if !parameterSchema.AllowOverrideSource && scopedParameter.Scope != database.ParameterScopeProject {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
destinationScheme, err := convertDestinationScheme(scopedParameter.DestinationScheme)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("convert destination scheme: %w", err)
|
||||
}
|
||||
destinationScheme, err := convertDestinationScheme(scopedParameter.DestinationScheme)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("convert destination scheme: %w", err)
|
||||
}
|
||||
|
||||
switch scopedParameter.SourceScheme {
|
||||
case database.ParameterSourceSchemeData:
|
||||
c.computedParameterByName[projectVersionParameter.Name] = Value{
|
||||
Proto: &proto.ParameterValue{
|
||||
DestinationScheme: destinationScheme,
|
||||
Name: scopedParameter.SourceValue,
|
||||
Value: scopedParameter.DestinationValue,
|
||||
},
|
||||
}
|
||||
default:
|
||||
return xerrors.Errorf("unsupported source scheme: %q", string(projectVersionParameter.DefaultSourceScheme))
|
||||
switch scopedParameter.SourceScheme {
|
||||
case database.ParameterSourceSchemeData:
|
||||
c.computedParameterByName[scopedParameter.Name] = Value{
|
||||
Proto: &proto.ParameterValue{
|
||||
DestinationScheme: destinationScheme,
|
||||
Name: scopedParameter.SourceValue,
|
||||
Value: scopedParameter.DestinationValue,
|
||||
},
|
||||
}
|
||||
default:
|
||||
return xerrors.Errorf("unsupported source scheme: %q", string(parameterSchema.DefaultSourceScheme))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -19,9 +19,12 @@ func TestCompute(t *testing.T) {
|
||||
t.Parallel()
|
||||
generateScope := func() projectparameter.Scope {
|
||||
return projectparameter.Scope{
|
||||
OrganizationID: uuid.New().String(),
|
||||
ProjectID: uuid.New(),
|
||||
ProjectVersionID: uuid.New(),
|
||||
ImportJobID: uuid.New(),
|
||||
OrganizationID: uuid.NewString(),
|
||||
ProjectID: uuid.NullUUID{
|
||||
UUID: uuid.New(),
|
||||
Valid: true,
|
||||
},
|
||||
WorkspaceID: uuid.NullUUID{
|
||||
UUID: uuid.New(),
|
||||
Valid: true,
|
||||
@@ -36,9 +39,9 @@ func TestCompute(t *testing.T) {
|
||||
AllowOverrideSource bool
|
||||
AllowOverrideDestination bool
|
||||
DefaultDestinationScheme database.ParameterDestinationScheme
|
||||
ProjectVersionID uuid.UUID
|
||||
ImportJobID uuid.UUID
|
||||
}
|
||||
generateProjectParameter := func(t *testing.T, db database.Store, opts projectParameterOptions) database.ProjectVersionParameter {
|
||||
generateProjectParameter := func(t *testing.T, db database.Store, opts projectParameterOptions) database.ParameterSchema {
|
||||
if opts.DefaultDestinationScheme == "" {
|
||||
opts.DefaultDestinationScheme = database.ParameterDestinationSchemeEnvironmentVariable
|
||||
}
|
||||
@@ -48,10 +51,10 @@ func TestCompute(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
destinationValue, err := cryptorand.String(8)
|
||||
require.NoError(t, err)
|
||||
param, err := db.InsertProjectVersionParameter(context.Background(), database.InsertProjectVersionParameterParams{
|
||||
param, err := db.InsertParameterSchema(context.Background(), database.InsertParameterSchemaParams{
|
||||
ID: uuid.New(),
|
||||
Name: name,
|
||||
ProjectVersionID: opts.ProjectVersionID,
|
||||
JobID: opts.ImportJobID,
|
||||
DefaultSourceScheme: database.ParameterSourceSchemeData,
|
||||
DefaultSourceValue: sql.NullString{
|
||||
String: sourceValue,
|
||||
@@ -73,10 +76,10 @@ func TestCompute(t *testing.T) {
|
||||
t.Parallel()
|
||||
db := databasefake.New()
|
||||
scope := generateScope()
|
||||
parameter, err := db.InsertProjectVersionParameter(context.Background(), database.InsertProjectVersionParameterParams{
|
||||
ID: uuid.New(),
|
||||
ProjectVersionID: scope.ProjectVersionID,
|
||||
Name: "hey",
|
||||
parameter, err := db.InsertParameterSchema(context.Background(), database.InsertParameterSchemaParams{
|
||||
ID: uuid.New(),
|
||||
JobID: scope.ImportJobID,
|
||||
Name: "hey",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -92,7 +95,7 @@ func TestCompute(t *testing.T) {
|
||||
db := databasefake.New()
|
||||
scope := generateScope()
|
||||
parameter := generateProjectParameter(t, db, projectParameterOptions{
|
||||
ProjectVersionID: scope.ProjectVersionID,
|
||||
ImportJobID: scope.ImportJobID,
|
||||
DefaultDestinationScheme: database.ParameterDestinationSchemeProvisionerVariable,
|
||||
})
|
||||
values, err := projectparameter.Compute(context.Background(), db, scope)
|
||||
@@ -101,7 +104,7 @@ func TestCompute(t *testing.T) {
|
||||
value := values[0]
|
||||
require.True(t, value.DefaultValue)
|
||||
require.Equal(t, database.ParameterScopeProject, value.Scope)
|
||||
require.Equal(t, scope.ProjectID.String(), value.ScopeID)
|
||||
require.Equal(t, scope.ProjectID.UUID.String(), value.ScopeID)
|
||||
require.Equal(t, value.Proto.Name, parameter.DefaultDestinationValue.String)
|
||||
require.Equal(t, value.Proto.DestinationScheme, proto.ParameterDestination_PROVISIONER_VARIABLE)
|
||||
require.Equal(t, value.Proto.Value, parameter.DefaultSourceValue.String)
|
||||
@@ -112,7 +115,7 @@ func TestCompute(t *testing.T) {
|
||||
db := databasefake.New()
|
||||
scope := generateScope()
|
||||
parameter := generateProjectParameter(t, db, projectParameterOptions{
|
||||
ProjectVersionID: scope.ProjectVersionID,
|
||||
ImportJobID: scope.ImportJobID,
|
||||
})
|
||||
_, err := db.InsertParameterValue(context.Background(), database.InsertParameterValueParams{
|
||||
ID: uuid.New(),
|
||||
@@ -138,13 +141,13 @@ func TestCompute(t *testing.T) {
|
||||
db := databasefake.New()
|
||||
scope := generateScope()
|
||||
parameter := generateProjectParameter(t, db, projectParameterOptions{
|
||||
ProjectVersionID: scope.ProjectVersionID,
|
||||
ImportJobID: scope.ImportJobID,
|
||||
})
|
||||
value, err := db.InsertParameterValue(context.Background(), database.InsertParameterValueParams{
|
||||
ID: uuid.New(),
|
||||
Name: parameter.Name,
|
||||
Scope: database.ParameterScopeProject,
|
||||
ScopeID: scope.ProjectID.String(),
|
||||
ScopeID: scope.ProjectID.UUID.String(),
|
||||
SourceScheme: database.ParameterSourceSchemeData,
|
||||
SourceValue: "nop",
|
||||
DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable,
|
||||
@@ -164,7 +167,7 @@ func TestCompute(t *testing.T) {
|
||||
db := databasefake.New()
|
||||
scope := generateScope()
|
||||
parameter := generateProjectParameter(t, db, projectParameterOptions{
|
||||
ProjectVersionID: scope.ProjectVersionID,
|
||||
ImportJobID: scope.ImportJobID,
|
||||
})
|
||||
_, err := db.InsertParameterValue(context.Background(), database.InsertParameterValueParams{
|
||||
ID: uuid.New(),
|
||||
@@ -190,7 +193,7 @@ func TestCompute(t *testing.T) {
|
||||
scope := generateScope()
|
||||
parameter := generateProjectParameter(t, db, projectParameterOptions{
|
||||
AllowOverrideSource: true,
|
||||
ProjectVersionID: scope.ProjectVersionID,
|
||||
ImportJobID: scope.ImportJobID,
|
||||
})
|
||||
_, err := db.InsertParameterValue(context.Background(), database.InsertParameterValueParams{
|
||||
ID: uuid.New(),
|
||||
@@ -209,4 +212,37 @@ func TestCompute(t *testing.T) {
|
||||
require.Len(t, values, 1)
|
||||
require.Equal(t, false, values[0].DefaultValue)
|
||||
})
|
||||
|
||||
t.Run("AdditionalOverwriteWorkspace", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
db := databasefake.New()
|
||||
scope := generateScope()
|
||||
parameter := generateProjectParameter(t, db, projectParameterOptions{
|
||||
AllowOverrideSource: true,
|
||||
ImportJobID: scope.ImportJobID,
|
||||
})
|
||||
_, err := db.InsertParameterValue(context.Background(), database.InsertParameterValueParams{
|
||||
ID: uuid.New(),
|
||||
Name: parameter.Name,
|
||||
Scope: database.ParameterScopeWorkspace,
|
||||
ScopeID: scope.WorkspaceID.UUID.String(),
|
||||
SourceScheme: database.ParameterSourceSchemeData,
|
||||
SourceValue: "nop",
|
||||
DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable,
|
||||
DestinationValue: "projectvalue",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
values, err := projectparameter.Compute(context.Background(), db, scope, database.ParameterValue{
|
||||
Name: parameter.Name,
|
||||
Scope: database.ParameterScopeUser,
|
||||
SourceScheme: database.ParameterSourceSchemeData,
|
||||
SourceValue: "nop",
|
||||
DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable,
|
||||
DestinationValue: "testing",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, values, 1)
|
||||
require.Equal(t, "testing", values[0].Proto.Value)
|
||||
})
|
||||
}
|
||||
|
||||
+55
-12
@@ -8,6 +8,8 @@ import (
|
||||
|
||||
"github.com/go-chi/render"
|
||||
"github.com/google/uuid"
|
||||
"github.com/moby/moby/pkg/namesgenerator"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/database"
|
||||
"github.com/coder/coder/httpapi"
|
||||
@@ -21,8 +23,15 @@ type Project database.Project
|
||||
|
||||
// CreateProjectRequest enables callers to create a new Project.
|
||||
type CreateProjectRequest struct {
|
||||
Name string `json:"name" validate:"username,required"`
|
||||
Provisioner database.ProvisionerType `json:"provisioner" validate:"oneof=terraform echo,required"`
|
||||
Name string `json:"name" validate:"username,required"`
|
||||
|
||||
// VersionImportJobID is an in-progress or completed job to use as
|
||||
// an initial version of the project.
|
||||
//
|
||||
// This is required on creation to enable a user-flow of validating
|
||||
// the project works. There is no reason the data-model cannot support
|
||||
// empty projects, but it doesn't make sense for users.
|
||||
VersionImportJobID uuid.UUID `json:"import_job_id" validate:"required"`
|
||||
}
|
||||
|
||||
// Lists all projects the authenticated user has access to.
|
||||
@@ -76,7 +85,7 @@ func (api *api) projectsByOrganization(rw http.ResponseWriter, r *http.Request)
|
||||
render.JSON(rw, r, projects)
|
||||
}
|
||||
|
||||
// Creates a new project in an organization.
|
||||
// Create a new project in an organization.
|
||||
func (api *api) postProjectsByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
var createProject CreateProjectRequest
|
||||
if !httpapi.Read(rw, r, &createProject) {
|
||||
@@ -99,25 +108,59 @@ func (api *api) postProjectsByOrganization(rw http.ResponseWriter, r *http.Reque
|
||||
}
|
||||
if !errors.Is(err, sql.ErrNoRows) {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("get project by name: %s", err.Error()),
|
||||
Message: fmt.Sprintf("get project by name: %s", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
importJob, err := api.Database.GetProvisionerJobByID(r.Context(), createProject.VersionImportJobID)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
|
||||
Message: "import job does not exist",
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("get import job by id: %s", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
project, err := api.Database.InsertProject(r.Context(), database.InsertProjectParams{
|
||||
ID: uuid.New(),
|
||||
CreatedAt: database.Now(),
|
||||
UpdatedAt: database.Now(),
|
||||
OrganizationID: organization.ID,
|
||||
Name: createProject.Name,
|
||||
Provisioner: createProject.Provisioner,
|
||||
var project Project
|
||||
err = api.Database.InTx(func(db database.Store) error {
|
||||
projectVersionID := uuid.New()
|
||||
dbProject, err := db.InsertProject(r.Context(), database.InsertProjectParams{
|
||||
ID: uuid.New(),
|
||||
CreatedAt: database.Now(),
|
||||
UpdatedAt: database.Now(),
|
||||
OrganizationID: organization.ID,
|
||||
Name: createProject.Name,
|
||||
Provisioner: importJob.Provisioner,
|
||||
ActiveVersionID: projectVersionID,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("insert project: %s", err)
|
||||
}
|
||||
_, err = db.InsertProjectVersion(r.Context(), database.InsertProjectVersionParams{
|
||||
ID: projectVersionID,
|
||||
ProjectID: dbProject.ID,
|
||||
CreatedAt: database.Now(),
|
||||
UpdatedAt: database.Now(),
|
||||
Name: namesgenerator.GetRandomName(1),
|
||||
ImportJobID: importJob.ID,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("insert project version: %s", err)
|
||||
}
|
||||
project = Project(dbProject)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("insert project: %s", err),
|
||||
Message: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusCreated)
|
||||
render.JSON(rw, r, project)
|
||||
}
|
||||
|
||||
+18
-10
@@ -30,7 +30,8 @@ func TestProjects(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
_ = coderdtest.CreateProject(t, client, user.Organization)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
_ = coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
projects, err := client.Projects(context.Background(), "")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, projects, 1)
|
||||
@@ -53,7 +54,8 @@ func TestProjectsByOrganization(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
_ = coderdtest.CreateProject(t, client, user.Organization)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
_ = coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
projects, err := client.Projects(context.Background(), "")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, projects, 1)
|
||||
@@ -66,17 +68,19 @@ func TestPostProjectsByOrganization(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
_ = coderdtest.CreateProject(t, client, user.Organization)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
_ = coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
})
|
||||
|
||||
t.Run("AlreadyExists", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
_, err := client.CreateProject(context.Background(), user.Organization, coderd.CreateProjectRequest{
|
||||
Name: project.Name,
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
Name: project.Name,
|
||||
VersionImportJobID: job.ID,
|
||||
})
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
@@ -90,7 +94,8 @@ func TestProjectByOrganization(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
_, err := client.Project(context.Background(), user.Organization, project.Name)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
@@ -102,7 +107,8 @@ func TestPostParametersByProject(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
_, err := client.CreateProjectParameter(context.Background(), user.Organization, project.Name, coderd.CreateParameterValueRequest{
|
||||
Name: "somename",
|
||||
SourceValue: "tomato",
|
||||
@@ -120,7 +126,8 @@ func TestParametersByProject(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
params, err := client.ProjectParameters(context.Background(), user.Organization, project.Name)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, params)
|
||||
@@ -130,7 +137,8 @@ func TestParametersByProject(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
_, err := client.CreateProjectParameter(context.Background(), user.Organization, project.Name, coderd.CreateParameterValueRequest{
|
||||
Name: "example",
|
||||
SourceValue: "source-value",
|
||||
|
||||
+38
-90
@@ -1,10 +1,7 @@
|
||||
package coderd
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -13,7 +10,6 @@ import (
|
||||
"github.com/go-chi/render"
|
||||
"github.com/google/uuid"
|
||||
"github.com/moby/moby/pkg/namesgenerator"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/database"
|
||||
"github.com/coder/coder/httpapi"
|
||||
@@ -22,20 +18,18 @@ import (
|
||||
|
||||
// ProjectVersion represents a single version of a project.
|
||||
type ProjectVersion struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
ProjectID uuid.UUID `json:"project_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
Name string `json:"name"`
|
||||
StorageMethod database.ProjectStorageMethod `json:"storage_method"`
|
||||
Import ProvisionerJob `json:"import"`
|
||||
ID uuid.UUID `json:"id"`
|
||||
ProjectID uuid.UUID `json:"project_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
Name string `json:"name"`
|
||||
ImportJobID uuid.UUID `json:"import_job_id"`
|
||||
}
|
||||
|
||||
// ProjectVersionParameter represents a parameter parsed from project version source on creation.
|
||||
type ProjectVersionParameter struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
ProjectVersionID uuid.UUID `json:"project_version_id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
DefaultSourceScheme database.ParameterSourceScheme `json:"default_source_scheme,omitempty"`
|
||||
@@ -54,8 +48,7 @@ type ProjectVersionParameter struct {
|
||||
|
||||
// CreateProjectVersionRequest enables callers to create a new Project Version.
|
||||
type CreateProjectVersionRequest struct {
|
||||
StorageMethod database.ProjectStorageMethod `json:"storage_method" validate:"oneof=inline-archive,required"`
|
||||
StorageSource []byte `json:"storage_source" validate:"max=1048576,required"`
|
||||
ImportJobID uuid.UUID `json:"import_job_id" validate:"required"`
|
||||
}
|
||||
|
||||
// Lists versions for a single project.
|
||||
@@ -74,31 +67,17 @@ func (api *api) projectVersionsByOrganization(rw http.ResponseWriter, r *http.Re
|
||||
}
|
||||
apiVersion := make([]ProjectVersion, 0)
|
||||
for _, version := range version {
|
||||
job, err := api.Database.GetProvisionerJobByID(r.Context(), version.ImportJobID)
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("get provisioner job: %s", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
apiVersion = append(apiVersion, convertProjectVersion(version, job))
|
||||
apiVersion = append(apiVersion, convertProjectVersion(version))
|
||||
}
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(rw, r, apiVersion)
|
||||
}
|
||||
|
||||
// Return a single project version by organization and name.
|
||||
func (api *api) projectVersionByOrganizationAndName(rw http.ResponseWriter, r *http.Request) {
|
||||
func (*api) projectVersionByOrganizationAndName(rw http.ResponseWriter, r *http.Request) {
|
||||
projectVersion := httpmw.ProjectVersionParam(r)
|
||||
job, err := api.Database.GetProvisionerJobByID(r.Context(), projectVersion.ImportJobID)
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("get provisioner job: %s", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(rw, r, convertProjectVersion(projectVersion, job))
|
||||
render.JSON(rw, r, convertProjectVersion(projectVersion))
|
||||
}
|
||||
|
||||
// Creates a new version of the project. An import job is queued to parse
|
||||
@@ -109,66 +88,37 @@ func (api *api) postProjectVersionByOrganization(rw http.ResponseWriter, r *http
|
||||
if !httpapi.Read(rw, r, &createProjectVersion) {
|
||||
return
|
||||
}
|
||||
|
||||
tarReader := tar.NewReader(bytes.NewReader(createProjectVersion.StorageSource))
|
||||
_, err := tarReader.Next()
|
||||
job, err := api.Database.GetProvisionerJobByID(r.Context(), createProjectVersion.ImportJobID)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
|
||||
Message: "job not found",
|
||||
})
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
|
||||
Message: "the archive must be a tar",
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("get provisioner job: %s", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
apiKey := httpmw.APIKey(r)
|
||||
project := httpmw.ProjectParam(r)
|
||||
|
||||
var provisionerJob database.ProvisionerJob
|
||||
var projectVersion database.ProjectVersion
|
||||
err = api.Database.InTx(func(db database.Store) error {
|
||||
provisionerJobID := uuid.New()
|
||||
projectVersion, err = api.Database.InsertProjectVersion(r.Context(), database.InsertProjectVersionParams{
|
||||
ID: uuid.New(),
|
||||
ProjectID: project.ID,
|
||||
CreatedAt: database.Now(),
|
||||
UpdatedAt: database.Now(),
|
||||
Name: namesgenerator.GetRandomName(1),
|
||||
StorageMethod: createProjectVersion.StorageMethod,
|
||||
StorageSource: createProjectVersion.StorageSource,
|
||||
ImportJobID: provisionerJobID,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("insert project version: %s", err)
|
||||
}
|
||||
|
||||
input, err := json.Marshal(projectImportJob{
|
||||
ProjectVersionID: projectVersion.ID,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("marshal import job: %w", err)
|
||||
}
|
||||
provisionerJob, err = db.InsertProvisionerJob(r.Context(), database.InsertProvisionerJobParams{
|
||||
ID: provisionerJobID,
|
||||
CreatedAt: database.Now(),
|
||||
UpdatedAt: database.Now(),
|
||||
InitiatorID: apiKey.UserID,
|
||||
Provisioner: project.Provisioner,
|
||||
Type: database.ProvisionerJobTypeProjectImport,
|
||||
Input: input,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("insert provisioner job: %w", err)
|
||||
}
|
||||
return nil
|
||||
projectVersion, err := api.Database.InsertProjectVersion(r.Context(), database.InsertProjectVersionParams{
|
||||
ID: uuid.New(),
|
||||
ProjectID: project.ID,
|
||||
CreatedAt: database.Now(),
|
||||
UpdatedAt: database.Now(),
|
||||
Name: namesgenerator.GetRandomName(1),
|
||||
ImportJobID: job.ID,
|
||||
})
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: err.Error(),
|
||||
Message: fmt.Sprintf("insert project version: %s", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusCreated)
|
||||
render.JSON(rw, r, convertProjectVersion(projectVersion, provisionerJob))
|
||||
render.JSON(rw, r, convertProjectVersion(projectVersion))
|
||||
}
|
||||
|
||||
func (api *api) projectVersionParametersByOrganizationAndName(rw http.ResponseWriter, r *http.Request) {
|
||||
@@ -194,10 +144,10 @@ func (api *api) projectVersionParametersByOrganizationAndName(rw http.ResponseWr
|
||||
return
|
||||
}
|
||||
|
||||
parameters, err := api.Database.GetProjectVersionParametersByVersionID(r.Context(), projectVersion.ID)
|
||||
parameters, err := api.Database.GetParameterSchemasByJobID(r.Context(), projectVersion.ImportJobID)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
err = nil
|
||||
parameters = []database.ProjectVersionParameter{}
|
||||
parameters = []database.ParameterSchema{}
|
||||
}
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
@@ -215,23 +165,21 @@ func (api *api) projectVersionParametersByOrganizationAndName(rw http.ResponseWr
|
||||
render.JSON(rw, r, apiParameters)
|
||||
}
|
||||
|
||||
func convertProjectVersion(version database.ProjectVersion, job database.ProvisionerJob) ProjectVersion {
|
||||
func convertProjectVersion(version database.ProjectVersion) ProjectVersion {
|
||||
return ProjectVersion{
|
||||
ID: version.ID,
|
||||
ProjectID: version.ProjectID,
|
||||
CreatedAt: version.CreatedAt,
|
||||
UpdatedAt: version.UpdatedAt,
|
||||
Name: version.Name,
|
||||
StorageMethod: version.StorageMethod,
|
||||
Import: convertProvisionerJob(job),
|
||||
ID: version.ID,
|
||||
ProjectID: version.ProjectID,
|
||||
CreatedAt: version.CreatedAt,
|
||||
UpdatedAt: version.UpdatedAt,
|
||||
Name: version.Name,
|
||||
ImportJobID: version.ImportJobID,
|
||||
}
|
||||
}
|
||||
|
||||
func convertProjectParameter(parameter database.ProjectVersionParameter) ProjectVersionParameter {
|
||||
func convertProjectParameter(parameter database.ParameterSchema) ProjectVersionParameter {
|
||||
return ProjectVersionParameter{
|
||||
ID: parameter.ID,
|
||||
CreatedAt: parameter.CreatedAt,
|
||||
ProjectVersionID: parameter.ProjectVersionID,
|
||||
Name: parameter.Name,
|
||||
Description: parameter.Description,
|
||||
DefaultSourceScheme: parameter.DefaultSourceScheme,
|
||||
|
||||
@@ -10,32 +10,21 @@ import (
|
||||
"github.com/coder/coder/coderd"
|
||||
"github.com/coder/coder/coderd/coderdtest"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/database"
|
||||
"github.com/coder/coder/provisioner/echo"
|
||||
"github.com/coder/coder/provisionersdk/proto"
|
||||
)
|
||||
|
||||
func TestProjectVersionsByOrganization(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("ListEmpty", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
versions, err := client.ProjectVersions(context.Background(), user.Organization, project.Name)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, versions)
|
||||
require.Len(t, versions, 0)
|
||||
})
|
||||
|
||||
t.Run("List", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
_ = coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, nil)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
versions, err := client.ProjectVersions(context.Background(), user.Organization, project.Name)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, versions)
|
||||
require.Len(t, versions, 1)
|
||||
})
|
||||
}
|
||||
@@ -46,9 +35,10 @@ func TestProjectVersionByOrganizationAndName(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, nil)
|
||||
require.Equal(t, version.Import.Status, coderd.ProvisionerJobStatusPending)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
_, err := client.ProjectVersion(context.Background(), user.Organization, project.Name, project.ActiveVersionID.String())
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -58,20 +48,12 @@ func TestPostProjectVersionByOrganization(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
_ = coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, nil)
|
||||
})
|
||||
|
||||
t.Run("InvalidStorage", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
_, err := client.CreateProjectVersion(context.Background(), user.Organization, project.Name, coderd.CreateProjectVersionRequest{
|
||||
StorageMethod: database.ProjectStorageMethod("invalid"),
|
||||
StorageSource: []byte{},
|
||||
ImportJobID: job.ID,
|
||||
})
|
||||
require.Error(t, err)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -81,9 +63,9 @@ func TestProjectVersionParametersByOrganizationAndName(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, nil)
|
||||
_, err := client.ProjectVersionParameters(context.Background(), user.Organization, project.Name, version.Name)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
_, err := client.ProjectVersionParameters(context.Background(), user.Organization, project.Name, project.ActiveVersionID.String())
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusPreconditionRequired, apiErr.StatusCode())
|
||||
@@ -94,12 +76,12 @@ func TestProjectVersionParametersByOrganizationAndName(t *testing.T) {
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
_ = coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, &echo.Responses{
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, &echo.Responses{
|
||||
Provision: []*proto.Provision_Response{{}},
|
||||
})
|
||||
coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name)
|
||||
_, err := client.ProjectVersionParameters(context.Background(), user.Organization, project.Name, version.Name)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID)
|
||||
_, err := client.ProjectVersionParameters(context.Background(), user.Organization, project.Name, project.ActiveVersionID.String())
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode())
|
||||
@@ -109,8 +91,7 @@ func TestProjectVersionParametersByOrganizationAndName(t *testing.T) {
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
_ = coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, &echo.Responses{
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, &echo.Responses{
|
||||
Parse: []*proto.Parse_Response{{
|
||||
Type: &proto.Parse_Response_Complete{
|
||||
Complete: &proto.Parse_Complete{
|
||||
@@ -122,8 +103,9 @@ func TestProjectVersionParametersByOrganizationAndName(t *testing.T) {
|
||||
}},
|
||||
Provision: echo.ProvisionComplete,
|
||||
})
|
||||
coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name)
|
||||
params, err := client.ProjectVersionParameters(context.Background(), user.Organization, project.Name, version.Name)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID)
|
||||
params, err := client.ProjectVersionParameters(context.Background(), user.Organization, project.Name, project.ActiveVersionID.String())
|
||||
require.NoError(t, err)
|
||||
require.Len(t, params, 1)
|
||||
})
|
||||
|
||||
@@ -106,11 +106,17 @@ func (api *api) provisionerDaemonsServe(rw http.ResponseWriter, r *http.Request)
|
||||
// The input for a "workspace_provision" job.
|
||||
type workspaceProvisionJob struct {
|
||||
WorkspaceHistoryID uuid.UUID `json:"workspace_history_id"`
|
||||
DryRun bool `json:"dry_run"`
|
||||
}
|
||||
|
||||
// The input for a "project_import" job.
|
||||
type projectImportJob struct {
|
||||
ProjectVersionID uuid.UUID `json:"project_version_id"`
|
||||
type projectVersionImportJob struct {
|
||||
OrganizationID string `json:"organization_id"`
|
||||
ProjectID uuid.UUID `json:"project_id"`
|
||||
|
||||
AdditionalParameters []database.ParameterValue `json:"parameters"`
|
||||
SkipParameterSchemas bool `json:"skip_parameter_schemas"`
|
||||
SkipResources bool `json:"skip_resources"`
|
||||
}
|
||||
|
||||
// Implementation of the provisioner daemon protobuf server.
|
||||
@@ -176,7 +182,6 @@ func (server *provisionerdServer) AcquireJob(ctx context.Context, _ *proto.Empty
|
||||
Provisioner: string(job.Provisioner),
|
||||
UserName: user.Username,
|
||||
}
|
||||
var projectVersion database.ProjectVersion
|
||||
switch job.Type {
|
||||
case database.ProvisionerJobTypeWorkspaceProvision:
|
||||
var input workspaceProvisionJob
|
||||
@@ -192,7 +197,7 @@ func (server *provisionerdServer) AcquireJob(ctx context.Context, _ *proto.Empty
|
||||
if err != nil {
|
||||
return nil, failJob(fmt.Sprintf("get workspace: %s", err))
|
||||
}
|
||||
projectVersion, err = server.Database.GetProjectVersionByID(ctx, workspaceHistory.ProjectVersionID)
|
||||
projectVersion, err := server.Database.GetProjectVersionByID(ctx, workspaceHistory.ProjectVersionID)
|
||||
if err != nil {
|
||||
return nil, failJob(fmt.Sprintf("get project version: %s", err))
|
||||
}
|
||||
@@ -207,9 +212,12 @@ func (server *provisionerdServer) AcquireJob(ctx context.Context, _ *proto.Empty
|
||||
|
||||
// Compute parameters for the workspace to consume.
|
||||
parameters, err := projectparameter.Compute(ctx, server.Database, projectparameter.Scope{
|
||||
OrganizationID: organization.ID,
|
||||
ProjectID: project.ID,
|
||||
ProjectVersionID: projectVersion.ID,
|
||||
ImportJobID: projectVersion.ImportJobID,
|
||||
OrganizationID: organization.ID,
|
||||
ProjectID: uuid.NullUUID{
|
||||
UUID: project.ID,
|
||||
Valid: true,
|
||||
},
|
||||
UserID: sql.NullString{
|
||||
String: user.ID,
|
||||
Valid: true,
|
||||
@@ -236,29 +244,52 @@ func (server *provisionerdServer) AcquireJob(ctx context.Context, _ *proto.Empty
|
||||
ParameterValues: protoParameters,
|
||||
},
|
||||
}
|
||||
case database.ProvisionerJobTypeProjectImport:
|
||||
var input projectImportJob
|
||||
case database.ProvisionerJobTypeProjectVersionImport:
|
||||
var input projectVersionImportJob
|
||||
err = json.Unmarshal(job.Input, &input)
|
||||
if err != nil {
|
||||
return nil, failJob(fmt.Sprintf("unmarshal job input %q: %s", job.Input, err))
|
||||
}
|
||||
projectVersion, err = server.Database.GetProjectVersionByID(ctx, input.ProjectVersionID)
|
||||
|
||||
// Compute parameters for the workspace to consume.
|
||||
parameters, err := projectparameter.Compute(ctx, server.Database, projectparameter.Scope{
|
||||
ImportJobID: job.ID,
|
||||
OrganizationID: input.OrganizationID,
|
||||
ProjectID: uuid.NullUUID{
|
||||
UUID: input.ProjectID,
|
||||
Valid: input.ProjectID.String() != uuid.Nil.String(),
|
||||
},
|
||||
UserID: sql.NullString{
|
||||
String: user.ID,
|
||||
Valid: true,
|
||||
},
|
||||
}, input.AdditionalParameters...)
|
||||
if err != nil {
|
||||
return nil, failJob(fmt.Sprintf("get project version: %s", err))
|
||||
return nil, failJob(fmt.Sprintf("compute parameters: %s", err))
|
||||
}
|
||||
// Convert parameters to the protobuf type.
|
||||
protoParameters := make([]*sdkproto.ParameterValue, 0, len(parameters))
|
||||
for _, parameter := range parameters {
|
||||
protoParameters = append(protoParameters, parameter.Proto)
|
||||
}
|
||||
|
||||
protoJob.Type = &proto.AcquiredJob_ProjectImport_{
|
||||
ProjectImport: &proto.AcquiredJob_ProjectImport{
|
||||
// This will be replaced once the project import has been refactored.
|
||||
ProjectName: "placeholder",
|
||||
ParameterValues: protoParameters,
|
||||
SkipParameterSchemas: input.SkipParameterSchemas,
|
||||
SkipResources: input.SkipResources,
|
||||
},
|
||||
}
|
||||
}
|
||||
switch projectVersion.StorageMethod {
|
||||
case database.ProjectStorageMethodInlineArchive:
|
||||
protoJob.ProjectSourceArchive = projectVersion.StorageSource
|
||||
switch job.StorageMethod {
|
||||
case database.ProvisionerStorageMethodFile:
|
||||
file, err := server.Database.GetFileByHash(ctx, job.StorageSource)
|
||||
if err != nil {
|
||||
return nil, failJob(fmt.Sprintf("get file by hash: %s", err))
|
||||
}
|
||||
protoJob.ProjectSourceArchive = file.Data
|
||||
default:
|
||||
return nil, failJob(fmt.Sprintf("unsupported storage source: %q", projectVersion.StorageMethod))
|
||||
return nil, failJob(fmt.Sprintf("unsupported storage method: %s", job.StorageMethod))
|
||||
}
|
||||
|
||||
return protoJob, err
|
||||
@@ -374,7 +405,7 @@ func (server *provisionerdServer) CompleteJob(ctx context.Context, completed *pr
|
||||
|
||||
switch jobType := completed.Type.(type) {
|
||||
case *proto.CompletedJob_ProjectImport_:
|
||||
var input projectImportJob
|
||||
var input projectVersionImportJob
|
||||
err = json.Unmarshal(job.Input, &input)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("unmarshal job data: %w", err)
|
||||
@@ -382,17 +413,17 @@ func (server *provisionerdServer) CompleteJob(ctx context.Context, completed *pr
|
||||
|
||||
// Validate that all parameters send from the provisioner daemon
|
||||
// follow the protocol.
|
||||
projectVersionParameters := make([]database.InsertProjectVersionParameterParams, 0, len(jobType.ProjectImport.ParameterSchemas))
|
||||
parameterSchemas := make([]database.InsertParameterSchemaParams, 0, len(jobType.ProjectImport.ParameterSchemas))
|
||||
for _, protoParameter := range jobType.ProjectImport.ParameterSchemas {
|
||||
validationTypeSystem, err := convertValidationTypeSystem(protoParameter.ValidationTypeSystem)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("convert validation type system for %q: %w", protoParameter.Name, err)
|
||||
}
|
||||
|
||||
projectParameter := database.InsertProjectVersionParameterParams{
|
||||
parameterSchema := database.InsertParameterSchemaParams{
|
||||
ID: uuid.New(),
|
||||
CreatedAt: database.Now(),
|
||||
ProjectVersionID: input.ProjectVersionID,
|
||||
JobID: job.ID,
|
||||
Name: protoParameter.Name,
|
||||
Description: protoParameter.Description,
|
||||
RedisplayValue: protoParameter.RedisplayValue,
|
||||
@@ -414,8 +445,8 @@ func (server *provisionerdServer) CompleteJob(ctx context.Context, completed *pr
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("convert parameter source scheme: %w", err)
|
||||
}
|
||||
projectParameter.DefaultSourceScheme = parameterSourceScheme
|
||||
projectParameter.DefaultSourceValue = sql.NullString{
|
||||
parameterSchema.DefaultSourceScheme = parameterSourceScheme
|
||||
parameterSchema.DefaultSourceValue = sql.NullString{
|
||||
String: protoParameter.DefaultSource.Value,
|
||||
Valid: protoParameter.DefaultSource.Value != "",
|
||||
}
|
||||
@@ -427,14 +458,14 @@ func (server *provisionerdServer) CompleteJob(ctx context.Context, completed *pr
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("convert parameter destination scheme: %w", err)
|
||||
}
|
||||
projectParameter.DefaultDestinationScheme = parameterDestinationScheme
|
||||
projectParameter.DefaultDestinationValue = sql.NullString{
|
||||
parameterSchema.DefaultDestinationScheme = parameterDestinationScheme
|
||||
parameterSchema.DefaultDestinationValue = sql.NullString{
|
||||
String: protoParameter.DefaultDestination.Value,
|
||||
Valid: protoParameter.DefaultDestination.Value != "",
|
||||
}
|
||||
}
|
||||
|
||||
projectVersionParameters = append(projectVersionParameters, projectParameter)
|
||||
parameterSchemas = append(parameterSchemas, parameterSchema)
|
||||
}
|
||||
|
||||
// This must occur in a transaction in case of failure.
|
||||
@@ -452,10 +483,10 @@ func (server *provisionerdServer) CompleteJob(ctx context.Context, completed *pr
|
||||
}
|
||||
// This could be a bulk-insert operation to improve performance.
|
||||
// See the "InsertWorkspaceHistoryLogs" query.
|
||||
for _, projectParameter := range projectVersionParameters {
|
||||
_, err = db.InsertProjectVersionParameter(ctx, projectParameter)
|
||||
for _, parameterSchema := range parameterSchemas {
|
||||
_, err = db.InsertParameterSchema(ctx, parameterSchema)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("insert project parameter %q: %w", projectParameter.Name, err)
|
||||
return xerrors.Errorf("insert parameter schema %q: %w", parameterSchema.Name, err)
|
||||
}
|
||||
}
|
||||
server.Logger.Debug(ctx, "marked import job as completed", slog.F("job_id", jobID))
|
||||
|
||||
@@ -21,8 +21,7 @@ func TestProvisionerJobLogsByName(t *testing.T) {
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, &echo.Responses{
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
Provision: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
@@ -37,17 +36,17 @@ func TestProvisionerJobLogsByName(t *testing.T) {
|
||||
},
|
||||
}},
|
||||
})
|
||||
coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
|
||||
history, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: version.ID,
|
||||
ProjectVersionID: project.ActiveVersionID,
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
coderdtest.AwaitWorkspaceHistoryProvisioned(t, client, "", workspace.Name, history.Name)
|
||||
|
||||
coderdtest.AwaitProvisionerJob(t, client, user.Organization, history.ProvisionJobID)
|
||||
// Return the log after completion!
|
||||
logs, err := client.ProvisionerJobLogs(context.Background(), history.Provision.ID)
|
||||
logs, err := client.ProvisionerJobLogs(context.Background(), user.Organization, history.ProvisionJobID)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, logs)
|
||||
require.Len(t, logs, 1)
|
||||
@@ -58,8 +57,7 @@ func TestProvisionerJobLogsByName(t *testing.T) {
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, &echo.Responses{
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
Provision: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
@@ -74,17 +72,18 @@ func TestProvisionerJobLogsByName(t *testing.T) {
|
||||
},
|
||||
}},
|
||||
})
|
||||
coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
|
||||
before := time.Now().UTC()
|
||||
history, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: version.ID,
|
||||
ProjectVersionID: project.ActiveVersionID,
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
coderdtest.AwaitWorkspaceHistoryProvisioned(t, client, "", workspace.Name, history.Name)
|
||||
coderdtest.AwaitProvisionerJob(t, client, user.Organization, history.ProvisionJobID)
|
||||
|
||||
logs, err := client.FollowProvisionerJobLogsAfter(context.Background(), history.Provision.ID, before)
|
||||
logs, err := client.FollowProvisionerJobLogsAfter(context.Background(), user.Organization, history.ProvisionJobID, before)
|
||||
require.NoError(t, err)
|
||||
log, ok := <-logs
|
||||
require.True(t, ok)
|
||||
@@ -99,8 +98,7 @@ func TestProvisionerJobLogsByName(t *testing.T) {
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, &echo.Responses{
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
Provision: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
@@ -115,15 +113,16 @@ func TestProvisionerJobLogsByName(t *testing.T) {
|
||||
},
|
||||
}},
|
||||
})
|
||||
coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
|
||||
before := database.Now()
|
||||
history, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: version.ID,
|
||||
ProjectVersionID: project.ActiveVersionID,
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
logs, err := client.FollowProvisionerJobLogsAfter(context.Background(), history.Provision.ID, before)
|
||||
logs, err := client.FollowProvisionerJobLogsAfter(context.Background(), user.Organization, history.ProvisionJobID, before)
|
||||
require.NoError(t, err)
|
||||
log := <-logs
|
||||
require.Equal(t, "log-output", log.Output)
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
package coderd
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/render"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/coder/coder/database"
|
||||
"github.com/coder/coder/httpapi"
|
||||
"github.com/coder/coder/httpmw"
|
||||
)
|
||||
|
||||
type ProvisionerJobStatus string
|
||||
@@ -37,6 +44,80 @@ type ProvisionerJob struct {
|
||||
WorkerID *uuid.UUID `json:"worker_id,omitempty"`
|
||||
}
|
||||
|
||||
type CreateProjectImportJobRequest struct {
|
||||
StorageMethod database.ProvisionerStorageMethod `json:"storage_method" validate:"oneof=file,required"`
|
||||
StorageSource string `json:"storage_source" validate:"required"`
|
||||
Provisioner database.ProvisionerType `json:"provisioner" validate:"oneof=terraform echo,required"`
|
||||
|
||||
AdditionalParameters []ParameterValue `json:"parameter_values"`
|
||||
SkipParameterSchemas bool `json:"skip_parameter_schemas"`
|
||||
SkipResources bool `json:"skip_resources"`
|
||||
}
|
||||
|
||||
func (*api) provisionerJobByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
job := httpmw.ProvisionerJobParam(r)
|
||||
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(rw, r, convertProvisionerJob(job))
|
||||
}
|
||||
|
||||
func (api *api) postProvisionerImportJobByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
apiKey := httpmw.APIKey(r)
|
||||
organization := httpmw.OrganizationParam(r)
|
||||
var req CreateProjectImportJobRequest
|
||||
if !httpapi.Read(rw, r, &req) {
|
||||
return
|
||||
}
|
||||
file, err := api.Database.GetFileByHash(r.Context(), req.StorageSource)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
|
||||
Message: "file not found",
|
||||
})
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("get file: %s", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
input, err := json.Marshal(projectVersionImportJob{
|
||||
// AdditionalParameters: req.AdditionalParameters,
|
||||
OrganizationID: organization.ID,
|
||||
SkipParameterSchemas: req.SkipParameterSchemas,
|
||||
SkipResources: req.SkipResources,
|
||||
})
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("marshal job: %s", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
job, err := api.Database.InsertProvisionerJob(r.Context(), database.InsertProvisionerJobParams{
|
||||
ID: uuid.New(),
|
||||
CreatedAt: database.Now(),
|
||||
UpdatedAt: database.Now(),
|
||||
OrganizationID: organization.ID,
|
||||
InitiatorID: apiKey.UserID,
|
||||
Provisioner: req.Provisioner,
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
StorageSource: file.Hash,
|
||||
Type: database.ProvisionerJobTypeProjectVersionImport,
|
||||
Input: input,
|
||||
})
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("insert provisioner job: %s", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusCreated)
|
||||
render.JSON(rw, r, convertProvisionerJob(job))
|
||||
}
|
||||
|
||||
func convertProvisionerJob(provisionerJob database.ProvisionerJob) ProvisionerJob {
|
||||
job := ProvisionerJob{
|
||||
ID: provisionerJob.ID,
|
||||
@@ -0,0 +1,52 @@
|
||||
package coderd_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/coderd/coderdtest"
|
||||
"github.com/coder/coder/provisioner/echo"
|
||||
"github.com/coder/coder/provisionersdk/proto"
|
||||
)
|
||||
|
||||
func TestPostProvisionerImportJobByOrganization(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
_ = coderdtest.NewProvisionerDaemon(t, client)
|
||||
before := time.Now()
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, &echo.Responses{
|
||||
Parse: []*proto.Parse_Response{{
|
||||
Type: &proto.Parse_Response_Complete{
|
||||
Complete: &proto.Parse_Complete{
|
||||
ParameterSchemas: []*proto.ParameterSchema{},
|
||||
},
|
||||
},
|
||||
}},
|
||||
Provision: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "dev",
|
||||
Type: "ec2_instance",
|
||||
}},
|
||||
},
|
||||
},
|
||||
}},
|
||||
})
|
||||
logs, err := client.FollowProvisionerJobLogsAfter(context.Background(), user.Organization, job.ID, before)
|
||||
require.NoError(t, err)
|
||||
for {
|
||||
log, ok := <-logs
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
t.Log(log.Output)
|
||||
}
|
||||
})
|
||||
}
|
||||
+18
-31
@@ -31,7 +31,7 @@ type WorkspaceHistory struct {
|
||||
Name string `json:"name"`
|
||||
Transition database.WorkspaceTransition `json:"transition"`
|
||||
Initiator string `json:"initiator"`
|
||||
Provision ProvisionerJob `json:"provision"`
|
||||
ProvisionJobID uuid.UUID `json:"provision_job_id"`
|
||||
}
|
||||
|
||||
// CreateWorkspaceHistoryRequest provides options to update the latest workspace history.
|
||||
@@ -121,7 +121,6 @@ func (api *api) postWorkspaceHistoryByUser(rw http.ResponseWriter, r *http.Reque
|
||||
return
|
||||
}
|
||||
|
||||
var provisionerJob database.ProvisionerJob
|
||||
var workspaceHistory database.WorkspaceHistory
|
||||
// This must happen in a transaction to ensure history can be inserted, and
|
||||
// the prior history can update it's "after" column to point at the new.
|
||||
@@ -150,14 +149,17 @@ func (api *api) postWorkspaceHistoryByUser(rw http.ResponseWriter, r *http.Reque
|
||||
return xerrors.Errorf("marshal provision job: %w", err)
|
||||
}
|
||||
|
||||
provisionerJob, err = db.InsertProvisionerJob(r.Context(), database.InsertProvisionerJobParams{
|
||||
ID: provisionerJobID,
|
||||
CreatedAt: database.Now(),
|
||||
UpdatedAt: database.Now(),
|
||||
InitiatorID: user.ID,
|
||||
Provisioner: project.Provisioner,
|
||||
Type: database.ProvisionerJobTypeWorkspaceProvision,
|
||||
Input: input,
|
||||
_, err = db.InsertProvisionerJob(r.Context(), database.InsertProvisionerJobParams{
|
||||
ID: provisionerJobID,
|
||||
CreatedAt: database.Now(),
|
||||
UpdatedAt: database.Now(),
|
||||
InitiatorID: user.ID,
|
||||
OrganizationID: project.OrganizationID,
|
||||
Provisioner: project.Provisioner,
|
||||
Type: database.ProvisionerJobTypeWorkspaceProvision,
|
||||
StorageMethod: projectVersionJob.StorageMethod,
|
||||
StorageSource: projectVersionJob.StorageSource,
|
||||
Input: input,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("insert provisioner job: %w", err)
|
||||
@@ -189,7 +191,7 @@ func (api *api) postWorkspaceHistoryByUser(rw http.ResponseWriter, r *http.Reque
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusCreated)
|
||||
render.JSON(rw, r, convertWorkspaceHistory(workspaceHistory, provisionerJob))
|
||||
render.JSON(rw, r, convertWorkspaceHistory(workspaceHistory))
|
||||
}
|
||||
|
||||
// Returns all workspace history. This is not sorted. Use before/after to chronologically sort.
|
||||
@@ -210,36 +212,21 @@ func (api *api) workspaceHistoryByUser(rw http.ResponseWriter, r *http.Request)
|
||||
|
||||
apiHistory := make([]WorkspaceHistory, 0, len(history))
|
||||
for _, history := range history {
|
||||
job, err := api.Database.GetProvisionerJobByID(r.Context(), history.ProvisionJobID)
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("get provisioner job: %s", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
apiHistory = append(apiHistory, convertWorkspaceHistory(history, job))
|
||||
apiHistory = append(apiHistory, convertWorkspaceHistory(history))
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(rw, r, apiHistory)
|
||||
}
|
||||
|
||||
func (api *api) workspaceHistoryByName(rw http.ResponseWriter, r *http.Request) {
|
||||
func (*api) workspaceHistoryByName(rw http.ResponseWriter, r *http.Request) {
|
||||
workspaceHistory := httpmw.WorkspaceHistoryParam(r)
|
||||
job, err := api.Database.GetProvisionerJobByID(r.Context(), workspaceHistory.ProvisionJobID)
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("get provisioner job: %s", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(rw, r, convertWorkspaceHistory(workspaceHistory, job))
|
||||
render.JSON(rw, r, convertWorkspaceHistory(workspaceHistory))
|
||||
}
|
||||
|
||||
// Converts the internal history representation to a public external-facing model.
|
||||
func convertWorkspaceHistory(workspaceHistory database.WorkspaceHistory, provisionerJob database.ProvisionerJob) WorkspaceHistory {
|
||||
func convertWorkspaceHistory(workspaceHistory database.WorkspaceHistory) WorkspaceHistory {
|
||||
//nolint:unconvert
|
||||
return WorkspaceHistory(WorkspaceHistory{
|
||||
ID: workspaceHistory.ID,
|
||||
@@ -252,6 +239,6 @@ func convertWorkspaceHistory(workspaceHistory database.WorkspaceHistory, provisi
|
||||
Name: workspaceHistory.Name,
|
||||
Transition: workspaceHistory.Transition,
|
||||
Initiator: workspaceHistory.Initiator,
|
||||
Provision: convertProvisionerJob(provisionerJob),
|
||||
ProvisionJobID: workspaceHistory.ProvisionJobID,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ func TestPostWorkspaceHistoryByUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
|
||||
_, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: uuid.New(),
|
||||
@@ -39,14 +40,14 @@ func TestPostWorkspaceHistoryByUser(t *testing.T) {
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, &echo.Responses{
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, &echo.Responses{
|
||||
Provision: []*proto.Provision_Response{{}},
|
||||
})
|
||||
coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
|
||||
_, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: version.ID,
|
||||
ProjectVersionID: project.ActiveVersionID,
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
})
|
||||
require.Error(t, err)
|
||||
@@ -60,19 +61,19 @@ func TestPostWorkspaceHistoryByUser(t *testing.T) {
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
closeDaemon := coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, nil)
|
||||
coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID)
|
||||
// Close here so workspace history doesn't process!
|
||||
closeDaemon.Close()
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
|
||||
_, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: version.ID,
|
||||
ProjectVersionID: project.ActiveVersionID,
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: version.ID,
|
||||
ProjectVersionID: project.ActiveVersionID,
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
})
|
||||
require.Error(t, err)
|
||||
@@ -86,18 +87,18 @@ func TestPostWorkspaceHistoryByUser(t *testing.T) {
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, nil)
|
||||
coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
|
||||
firstHistory, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: version.ID,
|
||||
ProjectVersionID: project.ActiveVersionID,
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
coderdtest.AwaitWorkspaceHistoryProvisioned(t, client, "me", workspace.Name, firstHistory.Name)
|
||||
coderdtest.AwaitProvisionerJob(t, client, user.Organization, firstHistory.ProvisionJobID)
|
||||
secondHistory, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: version.ID,
|
||||
ProjectVersionID: project.ActiveVersionID,
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
@@ -116,7 +117,8 @@ func TestWorkspaceHistoryByUser(t *testing.T) {
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
|
||||
history, err := client.ListWorkspaceHistory(context.Background(), "me", workspace.Name)
|
||||
require.NoError(t, err)
|
||||
@@ -129,12 +131,12 @@ func TestWorkspaceHistoryByUser(t *testing.T) {
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, nil)
|
||||
coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
|
||||
_, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: version.ID,
|
||||
ProjectVersionID: project.ActiveVersionID,
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
@@ -150,12 +152,12 @@ func TestWorkspaceHistoryByName(t *testing.T) {
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
coderdtest.NewProvisionerDaemon(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
version := coderdtest.CreateProjectVersion(t, client, user.Organization, project.Name, nil)
|
||||
coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
|
||||
history, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{
|
||||
ProjectVersionID: version.ID,
|
||||
ProjectVersionID: project.ActiveVersionID,
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -29,7 +29,8 @@ func TestWorkspaces(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
_ = coderdtest.CreateWorkspace(t, client, "", project.ID)
|
||||
workspaces, err := client.Workspaces(context.Background(), "")
|
||||
require.NoError(t, err)
|
||||
@@ -57,7 +58,8 @@ func TestPostWorkspaceByUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
|
||||
anotherUser := coderd.CreateUserRequest{
|
||||
Email: "another@user.org",
|
||||
@@ -88,7 +90,8 @@ func TestPostWorkspaceByUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "", project.ID)
|
||||
_, err := client.CreateWorkspace(context.Background(), "", coderd.CreateWorkspaceRequest{
|
||||
ProjectID: project.ID,
|
||||
@@ -104,7 +107,8 @@ func TestPostWorkspaceByUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
_ = coderdtest.CreateWorkspace(t, client, "", project.ID)
|
||||
})
|
||||
}
|
||||
@@ -113,7 +117,8 @@ func TestWorkspaceByUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, "", project.ID)
|
||||
_, err := client.Workspace(context.Background(), "", workspace.Name)
|
||||
require.NoError(t, err)
|
||||
@@ -125,7 +130,8 @@ func TestWorkspacesByProject(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
workspaces, err := client.WorkspacesByProject(context.Background(), user.Organization, project.Name)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, workspaces)
|
||||
@@ -135,7 +141,8 @@ func TestWorkspacesByProject(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t)
|
||||
user := coderdtest.CreateInitialUser(t, client)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization)
|
||||
job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil)
|
||||
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||
_ = coderdtest.CreateWorkspace(t, client, "", project.ID)
|
||||
workspaces, err := client.WorkspacesByProject(context.Background(), user.Organization, project.Name)
|
||||
require.NoError(t, err)
|
||||
|
||||
Reference in New Issue
Block a user