diff --git a/coderd/coderd.go b/coderd/coderd.go index 076ea4721af30..82486b98722ef 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -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) + }) }) }) }) diff --git a/coderd/coderdtest/coderdtest.go b/coderd/coderdtest/coderdtest.go index 430ed0c66a283..82295ef35af9b 100644 --- a/coderd/coderdtest/coderdtest.go +++ b/coderd/coderdtest/coderdtest.go @@ -122,40 +122,44 @@ func CreateInitialUser(t *testing.T, client *codersdk.Client) coderd.CreateIniti return req } -// 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 { - project, err := client.CreateProject(context.Background(), organization, coderd.CreateProjectRequest{ - Name: randomUsername(), - Provisioner: database.ProvisionerTypeEcho, +// 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 project + return job } -// 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, +// 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, job uuid.UUID) coderd.Project { + project, err := client.CreateProject(context.Background(), organization, coderd.CreateProjectRequest{ + Name: randomUsername(), + VersionImportJobID: job, }) require.NoError(t, err) - return version + return project } -// 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), "_", "-") } diff --git a/coderd/coderdtest/coderdtest_test.go b/coderd/coderdtest/coderdtest_test.go index 0c0d876fe94bc..b388ea6428f43 100644 --- a/coderd/coderdtest/coderdtest_test.go +++ b/coderd/coderdtest/coderdtest_test.go @@ -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() } diff --git a/coderd/files.go b/coderd/files.go new file mode 100644 index 0000000000000..01337b9d2e143 --- /dev/null +++ b/coderd/files.go @@ -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, + }) +} diff --git a/coderd/files_test.go b/coderd/files_test.go new file mode 100644 index 0000000000000..2ffa455df7e81 --- /dev/null +++ b/coderd/files_test.go @@ -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) + }) +} diff --git a/coderd/projectparameter/projectparameter.go b/coderd/projectparameter/projectparameter.go index 7f4d023a8f230..7288c8086c9ca 100644 --- a/coderd/projectparameter/projectparameter.go +++ b/coderd/projectparameter/projectparameter.go @@ -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 } diff --git a/coderd/projectparameter/projectparameter_test.go b/coderd/projectparameter/projectparameter_test.go index dd3fd28d25524..6fb04701c606c 100644 --- a/coderd/projectparameter/projectparameter_test.go +++ b/coderd/projectparameter/projectparameter_test.go @@ -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) + }) } diff --git a/coderd/projects.go b/coderd/projects.go index e9ff4edeaaba5..f50c192e6a006 100644 --- a/coderd/projects.go +++ b/coderd/projects.go @@ -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) } diff --git a/coderd/projects_test.go b/coderd/projects_test.go index 5788f66647543..ee11e2b744ad6 100644 --- a/coderd/projects_test.go +++ b/coderd/projects_test.go @@ -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", diff --git a/coderd/projectversion.go b/coderd/projectversion.go index c2e04e40fce1f..2b603e2e10665 100644 --- a/coderd/projectversion.go +++ b/coderd/projectversion.go @@ -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, diff --git a/coderd/projectversion_test.go b/coderd/projectversion_test.go index 9f9ad77046cbe..467574cfa74e9 100644 --- a/coderd/projectversion_test.go +++ b/coderd/projectversion_test.go @@ -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) }) diff --git a/coderd/provisionerdaemons.go b/coderd/provisionerdaemons.go index 12a2efc61e9a1..0d51966f62a7c 100644 --- a/coderd/provisionerdaemons.go +++ b/coderd/provisionerdaemons.go @@ -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)) diff --git a/coderd/provisionerjoblogs_test.go b/coderd/provisionerjoblogs_test.go index 25a9ff3950d79..1667d6feb74d5 100644 --- a/coderd/provisionerjoblogs_test.go +++ b/coderd/provisionerjoblogs_test.go @@ -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) diff --git a/coderd/provisioners.go b/coderd/provisionerjobs.go similarity index 51% rename from coderd/provisioners.go rename to coderd/provisionerjobs.go index 4e998afd9ede8..2eb4d69c34b7d 100644 --- a/coderd/provisioners.go +++ b/coderd/provisionerjobs.go @@ -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, diff --git a/coderd/provisionerjobs_test.go b/coderd/provisionerjobs_test.go new file mode 100644 index 0000000000000..d16e4e976ba04 --- /dev/null +++ b/coderd/provisionerjobs_test.go @@ -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) + } + }) +} diff --git a/coderd/workspacehistory.go b/coderd/workspacehistory.go index 7f3afd7670901..b94a0e99ff46d 100644 --- a/coderd/workspacehistory.go +++ b/coderd/workspacehistory.go @@ -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, }) } diff --git a/coderd/workspacehistory_test.go b/coderd/workspacehistory_test.go index 39090bcb961cf..7b6ba0e8cab74 100644 --- a/coderd/workspacehistory_test.go +++ b/coderd/workspacehistory_test.go @@ -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) diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index bc170a92ab4b8..06f4023e87499 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -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) diff --git a/codersdk/client.go b/codersdk/client.go index 4bd5a111cb949..de67ba1ae8058 100644 --- a/codersdk/client.go +++ b/codersdk/client.go @@ -51,7 +51,7 @@ func (c *Client) SetSessionToken(token string) error { // request performs an HTTP request with the body provided. // The caller is responsible for closing the response body. -func (c *Client) request(ctx context.Context, method, path string, body interface{}) (*http.Response, error) { +func (c *Client) request(ctx context.Context, method, path string, body interface{}, opts ...func(r *http.Request)) (*http.Response, error) { serverURL, err := c.URL.Parse(path) if err != nil { return nil, xerrors.Errorf("parse url: %w", err) @@ -59,11 +59,16 @@ func (c *Client) request(ctx context.Context, method, path string, body interfac var buf bytes.Buffer if body != nil { - enc := json.NewEncoder(&buf) - enc.SetEscapeHTML(false) - err = enc.Encode(body) - if err != nil { - return nil, xerrors.Errorf("encode body: %w", err) + if data, ok := body.([]byte); ok { + buf = *bytes.NewBuffer(data) + } else { + // Assume JSON if not bytes. + enc := json.NewEncoder(&buf) + enc.SetEscapeHTML(false) + err = enc.Encode(body) + if err != nil { + return nil, xerrors.Errorf("encode body: %w", err) + } } } @@ -74,6 +79,9 @@ func (c *Client) request(ctx context.Context, method, path string, body interfac if body != nil { req.Header.Set("Content-Type", "application/json") } + for _, opt := range opts { + opt(req) + } resp, err := c.httpClient.Do(req) if err != nil { diff --git a/codersdk/files.go b/codersdk/files.go new file mode 100644 index 0000000000000..25c7fcb70cc2b --- /dev/null +++ b/codersdk/files.go @@ -0,0 +1,28 @@ +package codersdk + +import ( + "context" + "encoding/json" + "net/http" + + "github.com/coder/coder/coderd" +) + +const ( + ContentTypeTar = "application/x-tar" +) + +func (c *Client) UploadFile(ctx context.Context, contentType string, content []byte) (coderd.UploadFileResponse, error) { + res, err := c.request(ctx, http.MethodPost, "/api/v2/files", content, func(r *http.Request) { + r.Header.Set("Content-Type", contentType) + }) + if err != nil { + return coderd.UploadFileResponse{}, err + } + defer res.Body.Close() + if res.StatusCode != http.StatusCreated { + return coderd.UploadFileResponse{}, readBodyAsError(res) + } + var resp coderd.UploadFileResponse + return resp, json.NewDecoder(res.Body).Decode(&resp) +} diff --git a/codersdk/files_test.go b/codersdk/files_test.go new file mode 100644 index 0000000000000..88aac04a44794 --- /dev/null +++ b/codersdk/files_test.go @@ -0,0 +1,28 @@ +package codersdk_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/coder/coder/coderd/coderdtest" + "github.com/coder/coder/codersdk" +) + +func TestUpload(t *testing.T) { + t.Parallel() + t.Run("Error", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + _, err := client.UploadFile(context.Background(), "wow", []byte{}) + require.Error(t, err) + }) + t.Run("Upload", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + _ = coderdtest.CreateInitialUser(t, client) + _, err := client.UploadFile(context.Background(), codersdk.ContentTypeTar, []byte{'a'}) + require.NoError(t, err) + }) +} diff --git a/codersdk/projects_test.go b/codersdk/projects_test.go index bf072657c97bd..f106d3a42652c 100644 --- a/codersdk/projects_test.go +++ b/codersdk/projects_test.go @@ -4,6 +4,7 @@ import ( "context" "testing" + "github.com/google/uuid" "github.com/stretchr/testify/require" "github.com/coder/coder/coderd" @@ -42,7 +43,8 @@ func TestProject(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) }) @@ -54,8 +56,8 @@ func TestCreateProject(t *testing.T) { t.Parallel() client := coderdtest.New(t) _, err := client.CreateProject(context.Background(), "org", coderd.CreateProjectRequest{ - Name: "something", - Provisioner: database.ProvisionerTypeEcho, + Name: "something", + VersionImportJobID: uuid.New(), }) require.Error(t, err) }) @@ -64,7 +66,8 @@ func TestCreateProject(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) }) } @@ -81,8 +84,8 @@ func TestProjectVersions(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.ProjectVersions(context.Background(), user.Organization, project.Name) require.NoError(t, err) }) @@ -101,9 +104,9 @@ func TestProjectVersion(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.ProjectVersion(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.ProjectVersion(context.Background(), user.Organization, project.Name, project.ActiveVersionID.String()) require.NoError(t, err) }) } @@ -121,8 +124,12 @@ func TestCreateProjectVersion(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) + _, err := client.CreateProjectVersion(context.Background(), user.Organization, project.Name, coderd.CreateProjectVersionRequest{ + ImportJobID: job.ID, + }) + require.NoError(t, err) }) } @@ -140,10 +147,10 @@ func TestProjectVersionParameters(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) - _, 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) + coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) + _, err := client.ProjectVersionParameters(context.Background(), user.Organization, project.Name, project.ActiveVersionID.String()) require.NoError(t, err) }) } @@ -161,8 +168,8 @@ func TestProjectParameters(t *testing.T) { t.Parallel() 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) _, err := client.ProjectParameters(context.Background(), user.Organization, project.Name) require.NoError(t, err) }) @@ -181,8 +188,8 @@ func TestCreateProjectParameter(t *testing.T) { t.Parallel() 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) _, err := client.CreateProjectParameter(context.Background(), user.Organization, project.Name, coderd.CreateParameterValueRequest{ Name: "example", SourceValue: "source-value", diff --git a/codersdk/provisioners.go b/codersdk/provisioners.go index f2685ccb939ad..1aea254aeba7a 100644 --- a/codersdk/provisioners.go +++ b/codersdk/provisioners.go @@ -59,14 +59,43 @@ func (c *Client) ProvisionerDaemonClient(ctx context.Context) (proto.DRPCProvisi return proto.NewDRPCProvisionerDaemonClient(provisionersdk.Conn(session)), nil } +// CreateProjectVersionImportProvisionerJob creates a job for importing +// the provided project version. +func (c *Client) CreateProjectVersionImportProvisionerJob(ctx context.Context, organization string, req coderd.CreateProjectImportJobRequest) (coderd.ProvisionerJob, error) { + res, err := c.request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/provisioners/jobs/%s/import", organization), req) + if err != nil { + return coderd.ProvisionerJob{}, err + } + if res.StatusCode != http.StatusCreated { + defer res.Body.Close() + return coderd.ProvisionerJob{}, readBodyAsError(res) + } + var job coderd.ProvisionerJob + return job, json.NewDecoder(res.Body).Decode(&job) +} + +// ProvisionerJob returns a job by ID. +func (c *Client) ProvisionerJob(ctx context.Context, organization string, job uuid.UUID) (coderd.ProvisionerJob, error) { + res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/provisioners/jobs/%s/%s", organization, job), nil) + if err != nil { + return coderd.ProvisionerJob{}, nil + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return coderd.ProvisionerJob{}, readBodyAsError(res) + } + var resp coderd.ProvisionerJob + return resp, json.NewDecoder(res.Body).Decode(&resp) +} + // ProvisionerJobLogs returns all logs for workspace history. // To stream logs, use the FollowProvisionerJobLogs function. -func (c *Client) ProvisionerJobLogs(ctx context.Context, jobID uuid.UUID) ([]coderd.ProvisionerJobLog, error) { - return c.ProvisionerJobLogsBetween(ctx, jobID, time.Time{}, time.Time{}) +func (c *Client) ProvisionerJobLogs(ctx context.Context, organization string, jobID uuid.UUID) ([]coderd.ProvisionerJobLog, error) { + return c.ProvisionerJobLogsBetween(ctx, organization, jobID, time.Time{}, time.Time{}) } // ProvisionerJobLogsBetween returns logs between a specific time. -func (c *Client) ProvisionerJobLogsBetween(ctx context.Context, jobID uuid.UUID, after, before time.Time) ([]coderd.ProvisionerJobLog, error) { +func (c *Client) ProvisionerJobLogsBetween(ctx context.Context, organization string, jobID uuid.UUID, after, before time.Time) ([]coderd.ProvisionerJobLog, error) { values := url.Values{} if !after.IsZero() { values["after"] = []string{strconv.FormatInt(after.UTC().UnixMilli(), 10)} @@ -74,7 +103,7 @@ func (c *Client) ProvisionerJobLogsBetween(ctx context.Context, jobID uuid.UUID, if !before.IsZero() { values["before"] = []string{strconv.FormatInt(before.UTC().UnixMilli(), 10)} } - res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/provisioners/jobs/%s/logs?%s", jobID, values.Encode()), nil) + res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/provisioners/jobs/%s/%s/logs?%s", organization, jobID, values.Encode()), nil) if err != nil { return nil, err } @@ -89,12 +118,12 @@ func (c *Client) ProvisionerJobLogsBetween(ctx context.Context, jobID uuid.UUID, // FollowProvisionerJobLogsAfter returns a stream of workspace history logs. // The channel will close when the workspace history job is no longer active. -func (c *Client) FollowProvisionerJobLogsAfter(ctx context.Context, jobID uuid.UUID, after time.Time) (<-chan coderd.ProvisionerJobLog, error) { +func (c *Client) FollowProvisionerJobLogsAfter(ctx context.Context, organization string, jobID uuid.UUID, after time.Time) (<-chan coderd.ProvisionerJobLog, error) { afterQuery := "" if !after.IsZero() { afterQuery = fmt.Sprintf("&after=%d", after.UTC().UnixMilli()) } - res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/provisioners/jobs/%s/logs?follow%s", jobID, afterQuery), nil) + res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/provisioners/jobs/%s/%s/logs?follow%s", organization, jobID, afterQuery), nil) if err != nil { return nil, err } diff --git a/codersdk/provisioners_test.go b/codersdk/provisioners_test.go index 9f57f27aae561..6808dd19a3b78 100644 --- a/codersdk/provisioners_test.go +++ b/codersdk/provisioners_test.go @@ -8,7 +8,6 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/require" - "github.com/coder/coder/coderd" "github.com/coder/coder/coderd/coderdtest" "github.com/coder/coder/database" "github.com/coder/coder/provisioner/echo" @@ -55,7 +54,7 @@ func TestProvisionerJobLogs(t *testing.T) { t.Run("Error", func(t *testing.T) { t.Parallel() client := coderdtest.New(t) - _, err := client.ProvisionerJobLogs(context.Background(), uuid.New()) + _, err := client.ProvisionerJobLogs(context.Background(), "nothing", uuid.New()) require.Error(t, err) }) @@ -64,16 +63,8 @@ func TestProvisionerJobLogs(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) - workspace := coderdtest.CreateWorkspace(t, client, "", project.ID) - history, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{ - ProjectVersionID: version.ID, - Transition: database.WorkspaceTransitionStart, - }) - require.NoError(t, err) - _, err = client.ProvisionerJobLogs(context.Background(), history.Provision.ID) + job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + _, err := client.ProvisionerJobLogs(context.Background(), user.Organization, job.ID) require.NoError(t, err) }) } @@ -83,7 +74,7 @@ func TestFollowProvisionerJobLogsAfter(t *testing.T) { t.Run("Error", func(t *testing.T) { t.Parallel() client := coderdtest.New(t) - _, err := client.FollowProvisionerJobLogsAfter(context.Background(), uuid.New(), time.Time{}) + _, err := client.FollowProvisionerJobLogsAfter(context.Background(), "nothing", uuid.New(), time.Time{}) require.Error(t, err) }) @@ -92,30 +83,22 @@ func TestFollowProvisionerJobLogsAfter(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{ - Parse: echo.ParseComplete, - Provision: []*sdkproto.Provision_Response{{ - Type: &sdkproto.Provision_Response_Log{ + before := database.Now() + job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, &echo.Responses{ + Parse: []*sdkproto.Parse_Response{{ + Type: &sdkproto.Parse_Response_Log{ Log: &sdkproto.Log{ Output: "hello", }, }, }, { - Type: &sdkproto.Provision_Response_Complete{ - Complete: &sdkproto.Provision_Complete{}, + Type: &sdkproto.Parse_Response_Complete{ + Complete: &sdkproto.Parse_Complete{}, }, }}, + Provision: echo.ProvisionComplete, }) - coderdtest.AwaitProjectVersionImported(t, client, user.Organization, project.Name, version.Name) - workspace := coderdtest.CreateWorkspace(t, client, "", project.ID) - after := database.Now() - history, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{ - ProjectVersionID: version.ID, - Transition: database.WorkspaceTransitionStart, - }) - require.NoError(t, err) - logs, err := client.FollowProvisionerJobLogsAfter(context.Background(), history.Provision.ID, after) + logs, err := client.FollowProvisionerJobLogsAfter(context.Background(), user.Organization, job.ID, before) require.NoError(t, err) _, ok := <-logs require.True(t, ok) diff --git a/codersdk/workspaces_test.go b/codersdk/workspaces_test.go index b2a79ff38b596..39ad8eddf4663 100644 --- a/codersdk/workspaces_test.go +++ b/codersdk/workspaces_test.go @@ -42,7 +42,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) _, err := client.WorkspacesByProject(context.Background(), user.Organization, project.Name) require.NoError(t, err) }) @@ -61,7 +62,8 @@ func TestWorkspace(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) @@ -81,7 +83,8 @@ func TestListWorkspaceHistory(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.ListWorkspaceHistory(context.Background(), "", workspace.Name) require.NoError(t, err) @@ -102,12 +105,12 @@ func TestWorkspaceHistory(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, "", project.ID) _, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{ - ProjectVersionID: version.ID, + ProjectVersionID: project.ActiveVersionID, Transition: database.WorkspaceTransitionStart, }) require.NoError(t, err) @@ -127,7 +130,8 @@ func TestCreateWorkspace(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) }) } @@ -146,12 +150,12 @@ func TestCreateWorkspaceHistory(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, "", project.ID) _, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{ - ProjectVersionID: version.ID, + ProjectVersionID: project.ActiveVersionID, Transition: database.WorkspaceTransitionStart, }) require.NoError(t, err) diff --git a/database/databasefake/databasefake.go b/database/databasefake/databasefake.go index 1befa0d153081..99793bc7409a9 100644 --- a/database/databasefake/databasefake.go +++ b/database/databasefake/databasefake.go @@ -19,18 +19,18 @@ func New() database.Store { organizationMembers: make([]database.OrganizationMember, 0), users: make([]database.User, 0), - files: make([]database.File, 0), - parameterValue: make([]database.ParameterValue, 0), - project: make([]database.Project, 0), - projectVersion: make([]database.ProjectVersion, 0), - projectVersionParameter: make([]database.ProjectVersionParameter, 0), - provisionerDaemons: make([]database.ProvisionerDaemon, 0), - provisionerJobs: make([]database.ProvisionerJob, 0), - provisionerJobLog: make([]database.ProvisionerJobLog, 0), - workspace: make([]database.Workspace, 0), - workspaceResource: make([]database.WorkspaceResource, 0), - workspaceHistory: make([]database.WorkspaceHistory, 0), - workspaceAgent: make([]database.WorkspaceAgent, 0), + files: make([]database.File, 0), + parameterValue: make([]database.ParameterValue, 0), + parameterSchema: make([]database.ParameterSchema, 0), + project: make([]database.Project, 0), + projectVersion: make([]database.ProjectVersion, 0), + provisionerDaemons: make([]database.ProvisionerDaemon, 0), + provisionerJobs: make([]database.ProvisionerJob, 0), + provisionerJobLog: make([]database.ProvisionerJobLog, 0), + workspace: make([]database.Workspace, 0), + workspaceResource: make([]database.WorkspaceResource, 0), + workspaceHistory: make([]database.WorkspaceHistory, 0), + workspaceAgent: make([]database.WorkspaceAgent, 0), } } @@ -45,18 +45,18 @@ type fakeQuerier struct { users []database.User // New tables - files []database.File - parameterValue []database.ParameterValue - project []database.Project - projectVersion []database.ProjectVersion - projectVersionParameter []database.ProjectVersionParameter - provisionerDaemons []database.ProvisionerDaemon - provisionerJobs []database.ProvisionerJob - provisionerJobLog []database.ProvisionerJobLog - workspace []database.Workspace - workspaceAgent []database.WorkspaceAgent - workspaceHistory []database.WorkspaceHistory - workspaceResource []database.WorkspaceResource + files []database.File + parameterValue []database.ParameterValue + parameterSchema []database.ParameterSchema + project []database.Project + projectVersion []database.ProjectVersion + provisionerDaemons []database.ProvisionerDaemon + provisionerJobs []database.ProvisionerJob + provisionerJobLog []database.ProvisionerJobLog + workspace []database.Workspace + workspaceAgent []database.WorkspaceAgent + workspaceHistory []database.WorkspaceHistory + workspaceResource []database.WorkspaceResource } // InTx doesn't rollback data properly for in-memory yet. @@ -445,16 +445,16 @@ func (q *fakeQuerier) GetProjectVersionByID(_ context.Context, projectVersionID return database.ProjectVersion{}, sql.ErrNoRows } -func (q *fakeQuerier) GetProjectVersionParametersByVersionID(_ context.Context, projectVersionID uuid.UUID) ([]database.ProjectVersionParameter, error) { +func (q *fakeQuerier) GetParameterSchemasByJobID(_ context.Context, jobID uuid.UUID) ([]database.ParameterSchema, error) { q.mutex.Lock() defer q.mutex.Unlock() - parameters := make([]database.ProjectVersionParameter, 0) - for _, projectParameter := range q.projectVersionParameter { - if projectParameter.ProjectVersionID.String() != projectVersionID.String() { + parameters := make([]database.ParameterSchema, 0) + for _, parameterSchema := range q.parameterSchema { + if parameterSchema.JobID.String() != jobID.String() { continue } - parameters = append(parameters, projectParameter) + parameters = append(parameters, parameterSchema) } if len(parameters) == 0 { return nil, sql.ErrNoRows @@ -590,6 +590,7 @@ func (q *fakeQuerier) InsertFile(_ context.Context, arg database.InsertFileParam file := database.File{ Hash: arg.Hash, CreatedAt: arg.CreatedAt, + CreatedBy: arg.CreatedBy, Mimetype: arg.Mimetype, Data: arg.Data, } @@ -652,13 +653,15 @@ func (q *fakeQuerier) InsertProject(_ context.Context, arg database.InsertProjec q.mutex.Lock() defer q.mutex.Unlock() + //nolint:gosimple project := database.Project{ - ID: arg.ID, - CreatedAt: arg.CreatedAt, - UpdatedAt: arg.UpdatedAt, - OrganizationID: arg.OrganizationID, - Name: arg.Name, - Provisioner: arg.Provisioner, + ID: arg.ID, + CreatedAt: arg.CreatedAt, + UpdatedAt: arg.UpdatedAt, + OrganizationID: arg.OrganizationID, + Name: arg.Name, + Provisioner: arg.Provisioner, + ActiveVersionID: arg.ActiveVersionID, } q.project = append(q.project, project) return project, nil @@ -670,15 +673,13 @@ func (q *fakeQuerier) InsertProjectVersion(_ context.Context, arg database.Inser //nolint:gosimple version := database.ProjectVersion{ - ID: arg.ID, - ProjectID: arg.ProjectID, - CreatedAt: arg.CreatedAt, - UpdatedAt: arg.UpdatedAt, - Name: arg.Name, - Description: arg.Description, - StorageMethod: arg.StorageMethod, - StorageSource: arg.StorageSource, - ImportJobID: arg.ImportJobID, + ID: arg.ID, + ProjectID: arg.ProjectID, + CreatedAt: arg.CreatedAt, + UpdatedAt: arg.UpdatedAt, + Name: arg.Name, + Description: arg.Description, + ImportJobID: arg.ImportJobID, } q.projectVersion = append(q.projectVersion, version) return version, nil @@ -703,15 +704,15 @@ func (q *fakeQuerier) InsertProvisionerJobLogs(_ context.Context, arg database.I return logs, nil } -func (q *fakeQuerier) InsertProjectVersionParameter(_ context.Context, arg database.InsertProjectVersionParameterParams) (database.ProjectVersionParameter, error) { +func (q *fakeQuerier) InsertParameterSchema(_ context.Context, arg database.InsertParameterSchemaParams) (database.ParameterSchema, error) { q.mutex.Lock() defer q.mutex.Unlock() //nolint:gosimple - param := database.ProjectVersionParameter{ + param := database.ParameterSchema{ ID: arg.ID, CreatedAt: arg.CreatedAt, - ProjectVersionID: arg.ProjectVersionID, + JobID: arg.JobID, Name: arg.Name, Description: arg.Description, DefaultSourceScheme: arg.DefaultSourceScheme, @@ -727,7 +728,7 @@ func (q *fakeQuerier) InsertProjectVersionParameter(_ context.Context, arg datab ValidationTypeSystem: arg.ValidationTypeSystem, ValidationValueType: arg.ValidationValueType, } - q.projectVersionParameter = append(q.projectVersionParameter, param) + q.parameterSchema = append(q.parameterSchema, param) return param, nil } @@ -750,13 +751,16 @@ func (q *fakeQuerier) InsertProvisionerJob(_ context.Context, arg database.Inser defer q.mutex.Unlock() job := database.ProvisionerJob{ - ID: arg.ID, - CreatedAt: arg.CreatedAt, - UpdatedAt: arg.UpdatedAt, - InitiatorID: arg.InitiatorID, - Provisioner: arg.Provisioner, - Type: arg.Type, - Input: arg.Input, + ID: arg.ID, + CreatedAt: arg.CreatedAt, + UpdatedAt: arg.UpdatedAt, + OrganizationID: arg.OrganizationID, + InitiatorID: arg.InitiatorID, + Provisioner: arg.Provisioner, + StorageMethod: arg.StorageMethod, + StorageSource: arg.StorageSource, + Type: arg.Type, + Input: arg.Input, } q.provisionerJobs = append(q.provisionerJobs, job) return job, nil diff --git a/database/dump.sql b/database/dump.sql index 6277edf705e9b..c21c963db56f5 100644 --- a/database/dump.sql +++ b/database/dump.sql @@ -42,15 +42,15 @@ CREATE TYPE parameter_type_system AS ENUM ( 'hcl' ); -CREATE TYPE project_storage_method AS ENUM ( - 'inline-archive' -); - CREATE TYPE provisioner_job_type AS ENUM ( - 'project_import', + 'project_version_import', 'workspace_provision' ); +CREATE TYPE provisioner_storage_method AS ENUM ( + 'file' +); + CREATE TYPE provisioner_type AS ENUM ( 'echo', 'terraform' @@ -89,6 +89,7 @@ CREATE TABLE api_keys ( CREATE TABLE file ( hash character varying(32) NOT NULL, created_at timestamp with time zone NOT NULL, + created_by text NOT NULL, mimetype character varying(64) NOT NULL, data bytea NOT NULL ); @@ -120,6 +121,26 @@ CREATE TABLE organizations ( workspace_auto_off boolean DEFAULT false NOT NULL ); +CREATE TABLE parameter_schema ( + id uuid NOT NULL, + created_at timestamp with time zone NOT NULL, + job_id uuid NOT NULL, + name character varying(64) NOT NULL, + description character varying(8192) DEFAULT ''::character varying NOT NULL, + default_source_scheme parameter_source_scheme, + default_source_value text, + allow_override_source boolean NOT NULL, + default_destination_scheme parameter_destination_scheme, + default_destination_value text, + allow_override_destination boolean NOT NULL, + default_refresh text NOT NULL, + redisplay_value boolean NOT NULL, + validation_error character varying(256) NOT NULL, + validation_condition character varying(512) NOT NULL, + validation_type_system parameter_type_system NOT NULL, + validation_value_type character varying(64) NOT NULL +); + CREATE TABLE parameter_value ( id uuid NOT NULL, name character varying(64) NOT NULL, @@ -140,7 +161,7 @@ CREATE TABLE project ( organization_id text NOT NULL, name character varying(64) NOT NULL, provisioner provisioner_type NOT NULL, - active_version_id uuid + active_version_id uuid NOT NULL ); CREATE TABLE project_version ( @@ -150,31 +171,9 @@ CREATE TABLE project_version ( updated_at timestamp with time zone NOT NULL, name character varying(64) NOT NULL, description character varying(1048576) NOT NULL, - storage_method project_storage_method NOT NULL, - storage_source bytea NOT NULL, import_job_id uuid NOT NULL ); -CREATE TABLE project_version_parameter ( - id uuid NOT NULL, - created_at timestamp with time zone NOT NULL, - project_version_id uuid NOT NULL, - name character varying(64) NOT NULL, - description character varying(8192) DEFAULT ''::character varying NOT NULL, - default_source_scheme parameter_source_scheme, - default_source_value text, - allow_override_source boolean NOT NULL, - default_destination_scheme parameter_destination_scheme, - default_destination_value text, - allow_override_destination boolean NOT NULL, - default_refresh text NOT NULL, - redisplay_value boolean NOT NULL, - validation_error character varying(256) NOT NULL, - validation_condition character varying(512) NOT NULL, - validation_type_system parameter_type_system NOT NULL, - validation_value_type character varying(64) NOT NULL -); - CREATE TABLE provisioner_daemon ( id uuid NOT NULL, created_at timestamp with time zone NOT NULL, @@ -191,8 +190,11 @@ CREATE TABLE provisioner_job ( cancelled_at timestamp with time zone, completed_at timestamp with time zone, error text, + organization_id text NOT NULL, initiator_id text NOT NULL, provisioner provisioner_type NOT NULL, + storage_method provisioner_storage_method NOT NULL, + storage_source text NOT NULL, type provisioner_job_type NOT NULL, input jsonb NOT NULL, worker_id uuid @@ -275,6 +277,12 @@ CREATE TABLE workspace_resource ( ALTER TABLE ONLY file ADD CONSTRAINT file_hash_key UNIQUE (hash); +ALTER TABLE ONLY parameter_schema + ADD CONSTRAINT parameter_schema_id_key UNIQUE (id); + +ALTER TABLE ONLY parameter_schema + ADD CONSTRAINT parameter_schema_job_id_name_key UNIQUE (job_id, name); + ALTER TABLE ONLY parameter_value ADD CONSTRAINT parameter_value_id_key UNIQUE (id); @@ -290,12 +298,6 @@ ALTER TABLE ONLY project ALTER TABLE ONLY project_version ADD CONSTRAINT project_version_id_key UNIQUE (id); -ALTER TABLE ONLY project_version_parameter - ADD CONSTRAINT project_version_parameter_id_key UNIQUE (id); - -ALTER TABLE ONLY project_version_parameter - ADD CONSTRAINT project_version_parameter_project_version_id_name_key UNIQUE (project_version_id, name); - ALTER TABLE ONLY project_version ADD CONSTRAINT project_version_project_id_name_key UNIQUE (project_id, name); @@ -335,8 +337,8 @@ ALTER TABLE ONLY workspace_resource ALTER TABLE ONLY workspace_resource ADD CONSTRAINT workspace_resource_workspace_history_id_type_name_key UNIQUE (workspace_history_id, type, name); -ALTER TABLE ONLY project_version_parameter - ADD CONSTRAINT project_version_parameter_project_version_id_fkey FOREIGN KEY (project_version_id) REFERENCES project_version(id) ON DELETE CASCADE; +ALTER TABLE ONLY parameter_schema + ADD CONSTRAINT parameter_schema_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_job(id) ON DELETE CASCADE; ALTER TABLE ONLY project_version ADD CONSTRAINT project_version_project_id_fkey FOREIGN KEY (project_id) REFERENCES project(id); diff --git a/database/migrations/000002_projects.up.sql b/database/migrations/000002_projects.up.sql index cb27bca942138..64426143d94d9 100644 --- a/database/migrations/000002_projects.up.sql +++ b/database/migrations/000002_projects.up.sql @@ -1,7 +1,8 @@ -- Store arbitrary data like project source code or avatars. CREATE TABLE file ( - hash varchar(32) NOT NULL UNIQUE, + hash varchar(64) NOT NULL UNIQUE, created_at timestamptz NOT NULL, + created_by text NOT NULL, mimetype varchar(64) NOT NULL, data bytea NOT NULL ); @@ -20,14 +21,12 @@ CREATE TABLE project ( provisioner provisioner_type NOT NULL, -- Target's a Project Version to use for Workspaces. -- If a Workspace doesn't match this version, it will be prompted to rebuild. - active_version_id uuid, + active_version_id uuid NOT NULL, -- Disallow projects to have the same name under -- the same organization. UNIQUE(organization_id, name) ); -CREATE TYPE project_storage_method AS ENUM ('inline-archive'); - -- Project Versions store historical project data. When a Project Version is imported, -- an "import" job is queued to parse parameters. A Project Version -- can only be used if the import job succeeds. @@ -43,8 +42,6 @@ CREATE TABLE project_version ( -- Extracted from a README.md on import. -- Maximum of 1MB. description varchar(1048576) NOT NULL, - storage_method project_storage_method NOT NULL, - storage_source bytea NOT NULL, -- The import job for a Project Version. This is used -- to detect if an import was successful. import_job_id uuid NOT NULL, @@ -52,49 +49,3 @@ CREATE TABLE project_version ( -- multiple times. UNIQUE(project_id, name) ); - --- Types of parameters the automator supports. -CREATE TYPE parameter_type_system AS ENUM ('none', 'hcl'); - --- Supported schemes for a parameter source. -CREATE TYPE parameter_source_scheme AS ENUM('none', 'data'); - --- Supported schemes for a parameter destination. -CREATE TYPE parameter_destination_scheme AS ENUM('none', 'environment_variable', 'provisioner_variable'); - --- Stores project version parameters parsed on import. --- No secrets are stored here. --- --- All parameter validation occurs server-side to process --- complex validations. --- --- Parameter types, description, and validation will produce --- a UI for users to enter values. --- Needs to be made consistent with the examples below. -CREATE TABLE project_version_parameter ( - id uuid NOT NULL UNIQUE, - created_at timestamptz NOT NULL, - project_version_id uuid NOT NULL REFERENCES project_version(id) ON DELETE CASCADE, - name varchar(64) NOT NULL, - -- 8KB limit - description varchar(8192) NOT NULL DEFAULT '', - -- eg. data://inlinevalue - default_source_scheme parameter_source_scheme, - default_source_value text, - -- Allows the user to override the source. - allow_override_source boolean NOT null, - -- eg. env://SOME_VARIABLE, tfvars://example - default_destination_scheme parameter_destination_scheme, - default_destination_value text, - -- Allows the user to override the destination. - allow_override_destination boolean NOT null, - default_refresh text NOT NULL, - -- Whether the consumer can view the source and destinations. - redisplay_value boolean NOT null, - -- This error would appear in the UI if the condition is not met. - validation_error varchar(256) NOT NULL, - validation_condition varchar(512) NOT NULL, - validation_type_system parameter_type_system NOT NULL, - validation_value_type varchar(64) NOT NULL, - UNIQUE(project_version_id, name) -); diff --git a/database/migrations/000004_jobs.up.sql b/database/migrations/000004_jobs.up.sql index 6588aaa6c9aeb..d2fd0ecdd94cb 100644 --- a/database/migrations/000004_jobs.up.sql +++ b/database/migrations/000004_jobs.up.sql @@ -9,10 +9,12 @@ CREATE TABLE IF NOT EXISTS provisioner_daemon ( ); CREATE TYPE provisioner_job_type AS ENUM ( - 'project_import', + 'project_version_import', 'workspace_provision' ); +CREATE TYPE provisioner_storage_method AS ENUM ('file'); + CREATE TABLE IF NOT EXISTS provisioner_job ( id uuid NOT NULL UNIQUE, created_at timestamptz NOT NULL, @@ -21,8 +23,11 @@ CREATE TABLE IF NOT EXISTS provisioner_job ( cancelled_at timestamptz, completed_at timestamptz, error text, + organization_id text NOT NULL, initiator_id text NOT NULL, provisioner provisioner_type NOT NULL, + storage_method provisioner_storage_method NOT NULL, + storage_source text NOT NULL, type provisioner_job_type NOT NULL, input jsonb NOT NULL, worker_id uuid @@ -57,6 +62,15 @@ CREATE TYPE parameter_scope AS ENUM ( 'workspace' ); +-- Types of parameters the automator supports. +CREATE TYPE parameter_type_system AS ENUM ('none', 'hcl'); + +-- Supported schemes for a parameter source. +CREATE TYPE parameter_source_scheme AS ENUM('none', 'data'); + +-- Supported schemes for a parameter destination. +CREATE TYPE parameter_destination_scheme AS ENUM('none', 'environment_variable', 'provisioner_variable'); + -- Parameters are provided to jobs for provisioning and to workspaces. CREATE TABLE parameter_value ( id uuid NOT NULL UNIQUE, @@ -71,4 +85,39 @@ CREATE TABLE parameter_value ( destination_value text NOT NULL, -- Prevents duplicates for parameters in the same scope. UNIQUE(name, scope, scope_id) -); \ No newline at end of file +); + +-- Stores project version parameters parsed on import. +-- No secrets are stored here. +-- +-- All parameter validation occurs server-side to process +-- complex validations. +-- +-- Parameter types, description, and validation will produce +-- a UI for users to enter values. +-- Needs to be made consistent with the examples below. +CREATE TABLE parameter_schema ( + id uuid NOT NULL UNIQUE, + created_at timestamptz NOT NULL, + job_id uuid NOT NULL REFERENCES provisioner_job(id) ON DELETE CASCADE, + name varchar(64) NOT NULL, + description varchar(8192) NOT NULL DEFAULT '', + default_source_scheme parameter_source_scheme, + default_source_value text, + -- Allows the user to override the source. + allow_override_source boolean NOT null, + -- eg. env://SOME_VARIABLE, tfvars://example + default_destination_scheme parameter_destination_scheme, + default_destination_value text, + -- Allows the user to override the destination. + allow_override_destination boolean NOT null, + default_refresh text NOT NULL, + -- Whether the consumer can view the source and destinations. + redisplay_value boolean NOT null, + -- This error would appear in the UI if the condition is not met. + validation_error varchar(256) NOT NULL, + validation_condition varchar(512) NOT NULL, + validation_type_system parameter_type_system NOT NULL, + validation_value_type varchar(64) NOT NULL, + UNIQUE(job_id, name) +); diff --git a/database/models.go b/database/models.go index 70939cc811572..04500cffd0005 100644 --- a/database/models.go +++ b/database/models.go @@ -151,39 +151,39 @@ func (e *ParameterTypeSystem) Scan(src interface{}) error { return nil } -type ProjectStorageMethod string +type ProvisionerJobType string const ( - ProjectStorageMethodInlineArchive ProjectStorageMethod = "inline-archive" + ProvisionerJobTypeProjectVersionImport ProvisionerJobType = "project_version_import" + ProvisionerJobTypeWorkspaceProvision ProvisionerJobType = "workspace_provision" ) -func (e *ProjectStorageMethod) Scan(src interface{}) error { +func (e *ProvisionerJobType) Scan(src interface{}) error { switch s := src.(type) { case []byte: - *e = ProjectStorageMethod(s) + *e = ProvisionerJobType(s) case string: - *e = ProjectStorageMethod(s) + *e = ProvisionerJobType(s) default: - return fmt.Errorf("unsupported scan type for ProjectStorageMethod: %T", src) + return fmt.Errorf("unsupported scan type for ProvisionerJobType: %T", src) } return nil } -type ProvisionerJobType string +type ProvisionerStorageMethod string const ( - ProvisionerJobTypeProjectImport ProvisionerJobType = "project_import" - ProvisionerJobTypeWorkspaceProvision ProvisionerJobType = "workspace_provision" + ProvisionerStorageMethodFile ProvisionerStorageMethod = "file" ) -func (e *ProvisionerJobType) Scan(src interface{}) error { +func (e *ProvisionerStorageMethod) Scan(src interface{}) error { switch s := src.(type) { case []byte: - *e = ProvisionerJobType(s) + *e = ProvisionerStorageMethod(s) case string: - *e = ProvisionerJobType(s) + *e = ProvisionerStorageMethod(s) default: - return fmt.Errorf("unsupported scan type for ProvisionerJobType: %T", src) + return fmt.Errorf("unsupported scan type for ProvisionerStorageMethod: %T", src) } return nil } @@ -268,6 +268,7 @@ type APIKey struct { type File struct { Hash string `db:"hash" json:"hash"` CreatedAt time.Time `db:"created_at" json:"created_at"` + CreatedBy string `db:"created_by" json:"created_by"` Mimetype string `db:"mimetype" json:"mimetype"` Data []byte `db:"data" json:"data"` } @@ -299,6 +300,26 @@ type OrganizationMember struct { Roles []string `db:"roles" json:"roles"` } +type ParameterSchema struct { + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + JobID uuid.UUID `db:"job_id" json:"job_id"` + Name string `db:"name" json:"name"` + Description string `db:"description" json:"description"` + DefaultSourceScheme ParameterSourceScheme `db:"default_source_scheme" json:"default_source_scheme"` + DefaultSourceValue sql.NullString `db:"default_source_value" json:"default_source_value"` + AllowOverrideSource bool `db:"allow_override_source" json:"allow_override_source"` + DefaultDestinationScheme ParameterDestinationScheme `db:"default_destination_scheme" json:"default_destination_scheme"` + DefaultDestinationValue sql.NullString `db:"default_destination_value" json:"default_destination_value"` + AllowOverrideDestination bool `db:"allow_override_destination" json:"allow_override_destination"` + DefaultRefresh string `db:"default_refresh" json:"default_refresh"` + RedisplayValue bool `db:"redisplay_value" json:"redisplay_value"` + ValidationError string `db:"validation_error" json:"validation_error"` + ValidationCondition string `db:"validation_condition" json:"validation_condition"` + ValidationTypeSystem ParameterTypeSystem `db:"validation_type_system" json:"validation_type_system"` + ValidationValueType string `db:"validation_value_type" json:"validation_value_type"` +} + type ParameterValue struct { ID uuid.UUID `db:"id" json:"id"` Name string `db:"name" json:"name"` @@ -319,39 +340,17 @@ type Project struct { OrganizationID string `db:"organization_id" json:"organization_id"` Name string `db:"name" json:"name"` Provisioner ProvisionerType `db:"provisioner" json:"provisioner"` - ActiveVersionID uuid.NullUUID `db:"active_version_id" json:"active_version_id"` + ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"` } type ProjectVersion struct { - ID uuid.UUID `db:"id" json:"id"` - ProjectID uuid.UUID `db:"project_id" json:"project_id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - Name string `db:"name" json:"name"` - Description string `db:"description" json:"description"` - StorageMethod ProjectStorageMethod `db:"storage_method" json:"storage_method"` - StorageSource []byte `db:"storage_source" json:"storage_source"` - ImportJobID uuid.UUID `db:"import_job_id" json:"import_job_id"` -} - -type ProjectVersionParameter struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - ProjectVersionID uuid.UUID `db:"project_version_id" json:"project_version_id"` - Name string `db:"name" json:"name"` - Description string `db:"description" json:"description"` - DefaultSourceScheme ParameterSourceScheme `db:"default_source_scheme" json:"default_source_scheme"` - DefaultSourceValue sql.NullString `db:"default_source_value" json:"default_source_value"` - AllowOverrideSource bool `db:"allow_override_source" json:"allow_override_source"` - DefaultDestinationScheme ParameterDestinationScheme `db:"default_destination_scheme" json:"default_destination_scheme"` - DefaultDestinationValue sql.NullString `db:"default_destination_value" json:"default_destination_value"` - AllowOverrideDestination bool `db:"allow_override_destination" json:"allow_override_destination"` - DefaultRefresh string `db:"default_refresh" json:"default_refresh"` - RedisplayValue bool `db:"redisplay_value" json:"redisplay_value"` - ValidationError string `db:"validation_error" json:"validation_error"` - ValidationCondition string `db:"validation_condition" json:"validation_condition"` - ValidationTypeSystem ParameterTypeSystem `db:"validation_type_system" json:"validation_type_system"` - ValidationValueType string `db:"validation_value_type" json:"validation_value_type"` + ID uuid.UUID `db:"id" json:"id"` + ProjectID uuid.UUID `db:"project_id" json:"project_id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + Name string `db:"name" json:"name"` + Description string `db:"description" json:"description"` + ImportJobID uuid.UUID `db:"import_job_id" json:"import_job_id"` } type ProvisionerDaemon struct { @@ -363,18 +362,21 @@ type ProvisionerDaemon struct { } type ProvisionerJob struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - StartedAt sql.NullTime `db:"started_at" json:"started_at"` - CancelledAt sql.NullTime `db:"cancelled_at" json:"cancelled_at"` - CompletedAt sql.NullTime `db:"completed_at" json:"completed_at"` - Error sql.NullString `db:"error" json:"error"` - InitiatorID string `db:"initiator_id" json:"initiator_id"` - Provisioner ProvisionerType `db:"provisioner" json:"provisioner"` - Type ProvisionerJobType `db:"type" json:"type"` - Input json.RawMessage `db:"input" json:"input"` - WorkerID uuid.NullUUID `db:"worker_id" json:"worker_id"` + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + StartedAt sql.NullTime `db:"started_at" json:"started_at"` + CancelledAt sql.NullTime `db:"cancelled_at" json:"cancelled_at"` + CompletedAt sql.NullTime `db:"completed_at" json:"completed_at"` + Error sql.NullString `db:"error" json:"error"` + OrganizationID string `db:"organization_id" json:"organization_id"` + InitiatorID string `db:"initiator_id" json:"initiator_id"` + Provisioner ProvisionerType `db:"provisioner" json:"provisioner"` + StorageMethod ProvisionerStorageMethod `db:"storage_method" json:"storage_method"` + StorageSource string `db:"storage_source" json:"storage_source"` + Type ProvisionerJobType `db:"type" json:"type"` + Input json.RawMessage `db:"input" json:"input"` + WorkerID uuid.NullUUID `db:"worker_id" json:"worker_id"` } type ProvisionerJobLog struct { diff --git a/database/querier.go b/database/querier.go index f6051faf521a9..05fc044eeb95b 100644 --- a/database/querier.go +++ b/database/querier.go @@ -16,12 +16,12 @@ type querier interface { GetOrganizationByName(ctx context.Context, name string) (Organization, error) GetOrganizationMemberByUserID(ctx context.Context, arg GetOrganizationMemberByUserIDParams) (OrganizationMember, error) GetOrganizationsByUserID(ctx context.Context, userID string) ([]Organization, error) + GetParameterSchemasByJobID(ctx context.Context, jobID uuid.UUID) ([]ParameterSchema, error) GetParameterValuesByScope(ctx context.Context, arg GetParameterValuesByScopeParams) ([]ParameterValue, error) GetProjectByID(ctx context.Context, id uuid.UUID) (Project, error) GetProjectByOrganizationAndName(ctx context.Context, arg GetProjectByOrganizationAndNameParams) (Project, error) GetProjectVersionByID(ctx context.Context, id uuid.UUID) (ProjectVersion, error) GetProjectVersionByProjectIDAndName(ctx context.Context, arg GetProjectVersionByProjectIDAndNameParams) (ProjectVersion, error) - GetProjectVersionParametersByVersionID(ctx context.Context, projectVersionID uuid.UUID) ([]ProjectVersionParameter, error) GetProjectVersionsByProjectID(ctx context.Context, projectID uuid.UUID) ([]ProjectVersion, error) GetProjectsByOrganizationIDs(ctx context.Context, ids []string) ([]Project, error) GetProvisionerDaemonByID(ctx context.Context, id uuid.UUID) (ProvisionerDaemon, error) @@ -45,10 +45,10 @@ type querier interface { InsertFile(ctx context.Context, arg InsertFileParams) (File, error) InsertOrganization(ctx context.Context, arg InsertOrganizationParams) (Organization, error) InsertOrganizationMember(ctx context.Context, arg InsertOrganizationMemberParams) (OrganizationMember, error) + InsertParameterSchema(ctx context.Context, arg InsertParameterSchemaParams) (ParameterSchema, error) InsertParameterValue(ctx context.Context, arg InsertParameterValueParams) (ParameterValue, error) InsertProject(ctx context.Context, arg InsertProjectParams) (Project, error) InsertProjectVersion(ctx context.Context, arg InsertProjectVersionParams) (ProjectVersion, error) - InsertProjectVersionParameter(ctx context.Context, arg InsertProjectVersionParameterParams) (ProjectVersionParameter, error) InsertProvisionerDaemon(ctx context.Context, arg InsertProvisionerDaemonParams) (ProvisionerDaemon, error) InsertProvisionerJob(ctx context.Context, arg InsertProvisionerJobParams) (ProvisionerJob, error) InsertProvisionerJobLogs(ctx context.Context, arg InsertProvisionerJobLogsParams) ([]ProvisionerJobLog, error) diff --git a/database/query.sql b/database/query.sql index ef27a08136964..26abf1893f36a 100644 --- a/database/query.sql +++ b/database/query.sql @@ -165,13 +165,13 @@ FROM WHERE organization_id = ANY(@ids :: text [ ]); --- name: GetProjectVersionParametersByVersionID :many +-- name: GetParameterSchemasByJobID :many SELECT * FROM - project_version_parameter + parameter_schema WHERE - project_version_id = $1; + job_id = $1; -- name: GetProjectVersionsByProjectID :many SELECT @@ -364,9 +364,9 @@ VALUES -- name: InsertFile :one INSERT INTO - file (hash, created_at, mimetype, data) + file (hash, created_at, created_by, mimetype, data) VALUES - ($1, $2, $3, $4) RETURNING *; + ($1, $2, $3, $4, $5) RETURNING *; -- name: InsertProvisionerJobLogs :many INSERT INTO @@ -422,10 +422,11 @@ INSERT INTO updated_at, organization_id, name, - provisioner + provisioner, + active_version_id ) VALUES - ($1, $2, $3, $4, $5, $6) RETURNING *; + ($1, $2, $3, $4, $5, $6, $7) RETURNING *; -- name: InsertProjectVersion :one INSERT INTO @@ -436,19 +437,17 @@ INSERT INTO updated_at, name, description, - storage_method, - storage_source, import_job_id ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING *; + ($1, $2, $3, $4, $5, $6, $7) RETURNING *; --- name: InsertProjectVersionParameter :one +-- name: InsertParameterSchema :one INSERT INTO - project_version_parameter ( + parameter_schema ( id, created_at, - project_version_id, + job_id, name, description, default_source_scheme, @@ -497,13 +496,16 @@ INSERT INTO id, created_at, updated_at, + organization_id, initiator_id, provisioner, + storage_method, + storage_source, type, input ) VALUES - ($1, $2, $3, $4, $5, $6, $7) RETURNING *; + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING *; -- name: InsertUser :one INSERT INTO diff --git a/database/query.sql.go b/database/query.sql.go index 3b232bb3b1fc1..8b63ca467b94b 100644 --- a/database/query.sql.go +++ b/database/query.sql.go @@ -37,7 +37,7 @@ WHERE SKIP LOCKED LIMIT 1 - ) RETURNING id, created_at, updated_at, started_at, cancelled_at, completed_at, error, initiator_id, provisioner, type, input, worker_id + ) RETURNING id, created_at, updated_at, started_at, cancelled_at, completed_at, error, organization_id, initiator_id, provisioner, storage_method, storage_source, type, input, worker_id ` type AcquireProvisionerJobParams struct { @@ -63,8 +63,11 @@ func (q *sqlQuerier) AcquireProvisionerJob(ctx context.Context, arg AcquireProvi &i.CancelledAt, &i.CompletedAt, &i.Error, + &i.OrganizationID, &i.InitiatorID, &i.Provisioner, + &i.StorageMethod, + &i.StorageSource, &i.Type, &i.Input, &i.WorkerID, @@ -108,7 +111,7 @@ func (q *sqlQuerier) GetAPIKeyByID(ctx context.Context, id string) (APIKey, erro const getFileByHash = `-- name: GetFileByHash :one SELECT - hash, created_at, mimetype, data + hash, created_at, created_by, mimetype, data FROM file WHERE @@ -123,6 +126,7 @@ func (q *sqlQuerier) GetFileByHash(ctx context.Context, hash string) (File, erro err := row.Scan( &i.Hash, &i.CreatedAt, + &i.CreatedBy, &i.Mimetype, &i.Data, ) @@ -265,6 +269,56 @@ func (q *sqlQuerier) GetOrganizationsByUserID(ctx context.Context, userID string return items, nil } +const getParameterSchemasByJobID = `-- name: GetParameterSchemasByJobID :many +SELECT + id, created_at, job_id, name, description, default_source_scheme, default_source_value, allow_override_source, default_destination_scheme, default_destination_value, allow_override_destination, default_refresh, redisplay_value, validation_error, validation_condition, validation_type_system, validation_value_type +FROM + parameter_schema +WHERE + job_id = $1 +` + +func (q *sqlQuerier) GetParameterSchemasByJobID(ctx context.Context, jobID uuid.UUID) ([]ParameterSchema, error) { + rows, err := q.db.QueryContext(ctx, getParameterSchemasByJobID, jobID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []ParameterSchema + for rows.Next() { + var i ParameterSchema + if err := rows.Scan( + &i.ID, + &i.CreatedAt, + &i.JobID, + &i.Name, + &i.Description, + &i.DefaultSourceScheme, + &i.DefaultSourceValue, + &i.AllowOverrideSource, + &i.DefaultDestinationScheme, + &i.DefaultDestinationValue, + &i.AllowOverrideDestination, + &i.DefaultRefresh, + &i.RedisplayValue, + &i.ValidationError, + &i.ValidationCondition, + &i.ValidationTypeSystem, + &i.ValidationValueType, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const getParameterValuesByScope = `-- name: GetParameterValuesByScope :many SELECT id, name, created_at, updated_at, scope, scope_id, source_scheme, source_value, destination_scheme, destination_value @@ -374,7 +428,7 @@ func (q *sqlQuerier) GetProjectByOrganizationAndName(ctx context.Context, arg Ge const getProjectVersionByID = `-- name: GetProjectVersionByID :one SELECT - id, project_id, created_at, updated_at, name, description, storage_method, storage_source, import_job_id + id, project_id, created_at, updated_at, name, description, import_job_id FROM project_version WHERE @@ -391,8 +445,6 @@ func (q *sqlQuerier) GetProjectVersionByID(ctx context.Context, id uuid.UUID) (P &i.UpdatedAt, &i.Name, &i.Description, - &i.StorageMethod, - &i.StorageSource, &i.ImportJobID, ) return i, err @@ -400,7 +452,7 @@ func (q *sqlQuerier) GetProjectVersionByID(ctx context.Context, id uuid.UUID) (P const getProjectVersionByProjectIDAndName = `-- name: GetProjectVersionByProjectIDAndName :one SELECT - id, project_id, created_at, updated_at, name, description, storage_method, storage_source, import_job_id + id, project_id, created_at, updated_at, name, description, import_job_id FROM project_version WHERE @@ -423,66 +475,14 @@ func (q *sqlQuerier) GetProjectVersionByProjectIDAndName(ctx context.Context, ar &i.UpdatedAt, &i.Name, &i.Description, - &i.StorageMethod, - &i.StorageSource, &i.ImportJobID, ) return i, err } -const getProjectVersionParametersByVersionID = `-- name: GetProjectVersionParametersByVersionID :many -SELECT - id, created_at, project_version_id, name, description, default_source_scheme, default_source_value, allow_override_source, default_destination_scheme, default_destination_value, allow_override_destination, default_refresh, redisplay_value, validation_error, validation_condition, validation_type_system, validation_value_type -FROM - project_version_parameter -WHERE - project_version_id = $1 -` - -func (q *sqlQuerier) GetProjectVersionParametersByVersionID(ctx context.Context, projectVersionID uuid.UUID) ([]ProjectVersionParameter, error) { - rows, err := q.db.QueryContext(ctx, getProjectVersionParametersByVersionID, projectVersionID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []ProjectVersionParameter - for rows.Next() { - var i ProjectVersionParameter - if err := rows.Scan( - &i.ID, - &i.CreatedAt, - &i.ProjectVersionID, - &i.Name, - &i.Description, - &i.DefaultSourceScheme, - &i.DefaultSourceValue, - &i.AllowOverrideSource, - &i.DefaultDestinationScheme, - &i.DefaultDestinationValue, - &i.AllowOverrideDestination, - &i.DefaultRefresh, - &i.RedisplayValue, - &i.ValidationError, - &i.ValidationCondition, - &i.ValidationTypeSystem, - &i.ValidationValueType, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Close(); err != nil { - return nil, err - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - const getProjectVersionsByProjectID = `-- name: GetProjectVersionsByProjectID :many SELECT - id, project_id, created_at, updated_at, name, description, storage_method, storage_source, import_job_id + id, project_id, created_at, updated_at, name, description, import_job_id FROM project_version WHERE @@ -505,8 +505,6 @@ func (q *sqlQuerier) GetProjectVersionsByProjectID(ctx context.Context, projectI &i.UpdatedAt, &i.Name, &i.Description, - &i.StorageMethod, - &i.StorageSource, &i.ImportJobID, ); err != nil { return nil, err @@ -622,7 +620,7 @@ func (q *sqlQuerier) GetProvisionerDaemons(ctx context.Context) ([]ProvisionerDa const getProvisionerJobByID = `-- name: GetProvisionerJobByID :one SELECT - id, created_at, updated_at, started_at, cancelled_at, completed_at, error, initiator_id, provisioner, type, input, worker_id + id, created_at, updated_at, started_at, cancelled_at, completed_at, error, organization_id, initiator_id, provisioner, storage_method, storage_source, type, input, worker_id FROM provisioner_job WHERE @@ -640,8 +638,11 @@ func (q *sqlQuerier) GetProvisionerJobByID(ctx context.Context, id uuid.UUID) (P &i.CancelledAt, &i.CompletedAt, &i.Error, + &i.OrganizationID, &i.InitiatorID, &i.Provisioner, + &i.StorageMethod, + &i.StorageSource, &i.Type, &i.Input, &i.WorkerID, @@ -1254,14 +1255,15 @@ func (q *sqlQuerier) InsertAPIKey(ctx context.Context, arg InsertAPIKeyParams) ( const insertFile = `-- name: InsertFile :one INSERT INTO - file (hash, created_at, mimetype, data) + file (hash, created_at, created_by, mimetype, data) VALUES - ($1, $2, $3, $4) RETURNING hash, created_at, mimetype, data + ($1, $2, $3, $4, $5) RETURNING hash, created_at, created_by, mimetype, data ` type InsertFileParams struct { Hash string `db:"hash" json:"hash"` CreatedAt time.Time `db:"created_at" json:"created_at"` + CreatedBy string `db:"created_by" json:"created_by"` Mimetype string `db:"mimetype" json:"mimetype"` Data []byte `db:"data" json:"data"` } @@ -1270,6 +1272,7 @@ func (q *sqlQuerier) InsertFile(ctx context.Context, arg InsertFileParams) (File row := q.db.QueryRowContext(ctx, insertFile, arg.Hash, arg.CreatedAt, + arg.CreatedBy, arg.Mimetype, arg.Data, ) @@ -1277,6 +1280,7 @@ func (q *sqlQuerier) InsertFile(ctx context.Context, arg InsertFileParams) (File err := row.Scan( &i.Hash, &i.CreatedAt, + &i.CreatedBy, &i.Mimetype, &i.Data, ) @@ -1362,6 +1366,112 @@ func (q *sqlQuerier) InsertOrganizationMember(ctx context.Context, arg InsertOrg return i, err } +const insertParameterSchema = `-- name: InsertParameterSchema :one +INSERT INTO + parameter_schema ( + id, + created_at, + job_id, + name, + description, + default_source_scheme, + default_source_value, + allow_override_source, + default_destination_scheme, + default_destination_value, + allow_override_destination, + default_refresh, + redisplay_value, + validation_error, + validation_condition, + validation_type_system, + validation_value_type + ) +VALUES + ( + $1, + $2, + $3, + $4, + $5, + $6, + $7, + $8, + $9, + $10, + $11, + $12, + $13, + $14, + $15, + $16, + $17 + ) RETURNING id, created_at, job_id, name, description, default_source_scheme, default_source_value, allow_override_source, default_destination_scheme, default_destination_value, allow_override_destination, default_refresh, redisplay_value, validation_error, validation_condition, validation_type_system, validation_value_type +` + +type InsertParameterSchemaParams struct { + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + JobID uuid.UUID `db:"job_id" json:"job_id"` + Name string `db:"name" json:"name"` + Description string `db:"description" json:"description"` + DefaultSourceScheme ParameterSourceScheme `db:"default_source_scheme" json:"default_source_scheme"` + DefaultSourceValue sql.NullString `db:"default_source_value" json:"default_source_value"` + AllowOverrideSource bool `db:"allow_override_source" json:"allow_override_source"` + DefaultDestinationScheme ParameterDestinationScheme `db:"default_destination_scheme" json:"default_destination_scheme"` + DefaultDestinationValue sql.NullString `db:"default_destination_value" json:"default_destination_value"` + AllowOverrideDestination bool `db:"allow_override_destination" json:"allow_override_destination"` + DefaultRefresh string `db:"default_refresh" json:"default_refresh"` + RedisplayValue bool `db:"redisplay_value" json:"redisplay_value"` + ValidationError string `db:"validation_error" json:"validation_error"` + ValidationCondition string `db:"validation_condition" json:"validation_condition"` + ValidationTypeSystem ParameterTypeSystem `db:"validation_type_system" json:"validation_type_system"` + ValidationValueType string `db:"validation_value_type" json:"validation_value_type"` +} + +func (q *sqlQuerier) InsertParameterSchema(ctx context.Context, arg InsertParameterSchemaParams) (ParameterSchema, error) { + row := q.db.QueryRowContext(ctx, insertParameterSchema, + arg.ID, + arg.CreatedAt, + arg.JobID, + arg.Name, + arg.Description, + arg.DefaultSourceScheme, + arg.DefaultSourceValue, + arg.AllowOverrideSource, + arg.DefaultDestinationScheme, + arg.DefaultDestinationValue, + arg.AllowOverrideDestination, + arg.DefaultRefresh, + arg.RedisplayValue, + arg.ValidationError, + arg.ValidationCondition, + arg.ValidationTypeSystem, + arg.ValidationValueType, + ) + var i ParameterSchema + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.JobID, + &i.Name, + &i.Description, + &i.DefaultSourceScheme, + &i.DefaultSourceValue, + &i.AllowOverrideSource, + &i.DefaultDestinationScheme, + &i.DefaultDestinationValue, + &i.AllowOverrideDestination, + &i.DefaultRefresh, + &i.RedisplayValue, + &i.ValidationError, + &i.ValidationCondition, + &i.ValidationTypeSystem, + &i.ValidationValueType, + ) + return i, err +} + const insertParameterValue = `-- name: InsertParameterValue :one INSERT INTO parameter_value ( @@ -1430,19 +1540,21 @@ INSERT INTO updated_at, organization_id, name, - provisioner + provisioner, + active_version_id ) VALUES - ($1, $2, $3, $4, $5, $6) RETURNING id, created_at, updated_at, organization_id, name, provisioner, active_version_id + ($1, $2, $3, $4, $5, $6, $7) RETURNING id, created_at, updated_at, organization_id, name, provisioner, active_version_id ` type InsertProjectParams struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - OrganizationID string `db:"organization_id" json:"organization_id"` - Name string `db:"name" json:"name"` - Provisioner ProvisionerType `db:"provisioner" json:"provisioner"` + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + OrganizationID string `db:"organization_id" json:"organization_id"` + Name string `db:"name" json:"name"` + Provisioner ProvisionerType `db:"provisioner" json:"provisioner"` + ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"` } func (q *sqlQuerier) InsertProject(ctx context.Context, arg InsertProjectParams) (Project, error) { @@ -1453,6 +1565,7 @@ func (q *sqlQuerier) InsertProject(ctx context.Context, arg InsertProjectParams) arg.OrganizationID, arg.Name, arg.Provisioner, + arg.ActiveVersionID, ) var i Project err := row.Scan( @@ -1476,24 +1589,20 @@ INSERT INTO updated_at, name, description, - storage_method, - storage_source, import_job_id ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id, project_id, created_at, updated_at, name, description, storage_method, storage_source, import_job_id + ($1, $2, $3, $4, $5, $6, $7) RETURNING id, project_id, created_at, updated_at, name, description, import_job_id ` type InsertProjectVersionParams struct { - ID uuid.UUID `db:"id" json:"id"` - ProjectID uuid.UUID `db:"project_id" json:"project_id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - Name string `db:"name" json:"name"` - Description string `db:"description" json:"description"` - StorageMethod ProjectStorageMethod `db:"storage_method" json:"storage_method"` - StorageSource []byte `db:"storage_source" json:"storage_source"` - ImportJobID uuid.UUID `db:"import_job_id" json:"import_job_id"` + ID uuid.UUID `db:"id" json:"id"` + ProjectID uuid.UUID `db:"project_id" json:"project_id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + Name string `db:"name" json:"name"` + Description string `db:"description" json:"description"` + ImportJobID uuid.UUID `db:"import_job_id" json:"import_job_id"` } func (q *sqlQuerier) InsertProjectVersion(ctx context.Context, arg InsertProjectVersionParams) (ProjectVersion, error) { @@ -1504,8 +1613,6 @@ func (q *sqlQuerier) InsertProjectVersion(ctx context.Context, arg InsertProject arg.UpdatedAt, arg.Name, arg.Description, - arg.StorageMethod, - arg.StorageSource, arg.ImportJobID, ) var i ProjectVersion @@ -1516,119 +1623,11 @@ func (q *sqlQuerier) InsertProjectVersion(ctx context.Context, arg InsertProject &i.UpdatedAt, &i.Name, &i.Description, - &i.StorageMethod, - &i.StorageSource, &i.ImportJobID, ) return i, err } -const insertProjectVersionParameter = `-- name: InsertProjectVersionParameter :one -INSERT INTO - project_version_parameter ( - id, - created_at, - project_version_id, - name, - description, - default_source_scheme, - default_source_value, - allow_override_source, - default_destination_scheme, - default_destination_value, - allow_override_destination, - default_refresh, - redisplay_value, - validation_error, - validation_condition, - validation_type_system, - validation_value_type - ) -VALUES - ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9, - $10, - $11, - $12, - $13, - $14, - $15, - $16, - $17 - ) RETURNING id, created_at, project_version_id, name, description, default_source_scheme, default_source_value, allow_override_source, default_destination_scheme, default_destination_value, allow_override_destination, default_refresh, redisplay_value, validation_error, validation_condition, validation_type_system, validation_value_type -` - -type InsertProjectVersionParameterParams struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - ProjectVersionID uuid.UUID `db:"project_version_id" json:"project_version_id"` - Name string `db:"name" json:"name"` - Description string `db:"description" json:"description"` - DefaultSourceScheme ParameterSourceScheme `db:"default_source_scheme" json:"default_source_scheme"` - DefaultSourceValue sql.NullString `db:"default_source_value" json:"default_source_value"` - AllowOverrideSource bool `db:"allow_override_source" json:"allow_override_source"` - DefaultDestinationScheme ParameterDestinationScheme `db:"default_destination_scheme" json:"default_destination_scheme"` - DefaultDestinationValue sql.NullString `db:"default_destination_value" json:"default_destination_value"` - AllowOverrideDestination bool `db:"allow_override_destination" json:"allow_override_destination"` - DefaultRefresh string `db:"default_refresh" json:"default_refresh"` - RedisplayValue bool `db:"redisplay_value" json:"redisplay_value"` - ValidationError string `db:"validation_error" json:"validation_error"` - ValidationCondition string `db:"validation_condition" json:"validation_condition"` - ValidationTypeSystem ParameterTypeSystem `db:"validation_type_system" json:"validation_type_system"` - ValidationValueType string `db:"validation_value_type" json:"validation_value_type"` -} - -func (q *sqlQuerier) InsertProjectVersionParameter(ctx context.Context, arg InsertProjectVersionParameterParams) (ProjectVersionParameter, error) { - row := q.db.QueryRowContext(ctx, insertProjectVersionParameter, - arg.ID, - arg.CreatedAt, - arg.ProjectVersionID, - arg.Name, - arg.Description, - arg.DefaultSourceScheme, - arg.DefaultSourceValue, - arg.AllowOverrideSource, - arg.DefaultDestinationScheme, - arg.DefaultDestinationValue, - arg.AllowOverrideDestination, - arg.DefaultRefresh, - arg.RedisplayValue, - arg.ValidationError, - arg.ValidationCondition, - arg.ValidationTypeSystem, - arg.ValidationValueType, - ) - var i ProjectVersionParameter - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.ProjectVersionID, - &i.Name, - &i.Description, - &i.DefaultSourceScheme, - &i.DefaultSourceValue, - &i.AllowOverrideSource, - &i.DefaultDestinationScheme, - &i.DefaultDestinationValue, - &i.AllowOverrideDestination, - &i.DefaultRefresh, - &i.RedisplayValue, - &i.ValidationError, - &i.ValidationCondition, - &i.ValidationTypeSystem, - &i.ValidationValueType, - ) - return i, err -} - const insertProvisionerDaemon = `-- name: InsertProvisionerDaemon :one INSERT INTO provisioner_daemon (id, created_at, name, provisioners) @@ -1667,23 +1666,29 @@ INSERT INTO id, created_at, updated_at, + organization_id, initiator_id, provisioner, + storage_method, + storage_source, type, input ) VALUES - ($1, $2, $3, $4, $5, $6, $7) RETURNING id, created_at, updated_at, started_at, cancelled_at, completed_at, error, initiator_id, provisioner, type, input, worker_id + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id, created_at, updated_at, started_at, cancelled_at, completed_at, error, organization_id, initiator_id, provisioner, storage_method, storage_source, type, input, worker_id ` type InsertProvisionerJobParams struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - InitiatorID string `db:"initiator_id" json:"initiator_id"` - Provisioner ProvisionerType `db:"provisioner" json:"provisioner"` - Type ProvisionerJobType `db:"type" json:"type"` - Input json.RawMessage `db:"input" json:"input"` + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + OrganizationID string `db:"organization_id" json:"organization_id"` + InitiatorID string `db:"initiator_id" json:"initiator_id"` + Provisioner ProvisionerType `db:"provisioner" json:"provisioner"` + StorageMethod ProvisionerStorageMethod `db:"storage_method" json:"storage_method"` + StorageSource string `db:"storage_source" json:"storage_source"` + Type ProvisionerJobType `db:"type" json:"type"` + Input json.RawMessage `db:"input" json:"input"` } func (q *sqlQuerier) InsertProvisionerJob(ctx context.Context, arg InsertProvisionerJobParams) (ProvisionerJob, error) { @@ -1691,8 +1696,11 @@ func (q *sqlQuerier) InsertProvisionerJob(ctx context.Context, arg InsertProvisi arg.ID, arg.CreatedAt, arg.UpdatedAt, + arg.OrganizationID, arg.InitiatorID, arg.Provisioner, + arg.StorageMethod, + arg.StorageSource, arg.Type, arg.Input, ) @@ -1705,8 +1713,11 @@ func (q *sqlQuerier) InsertProvisionerJob(ctx context.Context, arg InsertProvisi &i.CancelledAt, &i.CompletedAt, &i.Error, + &i.OrganizationID, &i.InitiatorID, &i.Provisioner, + &i.StorageMethod, + &i.StorageSource, &i.Type, &i.Input, &i.WorkerID, diff --git a/httpmw/projectversionparam.go b/httpmw/projectversionparam.go index 21deceff87825..ca8c47f76fb00 100644 --- a/httpmw/projectversionparam.go +++ b/httpmw/projectversionparam.go @@ -8,6 +8,7 @@ import ( "net/http" "github.com/go-chi/chi/v5" + "github.com/google/uuid" "github.com/coder/coder/database" "github.com/coder/coder/httpapi" @@ -36,10 +37,16 @@ func ExtractProjectVersionParam(db database.Store) func(http.Handler) http.Handl }) return } - projectVersion, err := db.GetProjectVersionByProjectIDAndName(r.Context(), database.GetProjectVersionByProjectIDAndNameParams{ - ProjectID: project.ID, - Name: projectVersionName, - }) + var projectVersion database.ProjectVersion + uuid, err := uuid.Parse(projectVersionName) + if err == nil { + projectVersion, err = db.GetProjectVersionByID(r.Context(), uuid) + } else { + projectVersion, err = db.GetProjectVersionByProjectIDAndName(r.Context(), database.GetProjectVersionByProjectIDAndNameParams{ + ProjectID: project.ID, + Name: projectVersionName, + }) + } if errors.Is(err, sql.ErrNoRows) { httpapi.Write(rw, http.StatusNotFound, httpapi.Response{ Message: fmt.Sprintf("project version %q does not exist", projectVersionName), diff --git a/provisionerd/proto/provisionerd.pb.go b/provisionerd/proto/provisionerd.pb.go index 6a3c51e5716c9..d1cd2548b19fb 100644 --- a/provisionerd/proto/provisionerd.pb.go +++ b/provisionerd/proto/provisionerd.pb.go @@ -638,10 +638,9 @@ type AcquiredJob_ProjectImport struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ProjectName string `protobuf:"bytes,1,opt,name=project_name,json=projectName,proto3" json:"project_name,omitempty"` - ParameterValues []*proto.ParameterValue `protobuf:"bytes,2,rep,name=parameter_values,json=parameterValues,proto3" json:"parameter_values,omitempty"` - SkipParameterSchemas bool `protobuf:"varint,3,opt,name=skip_parameter_schemas,json=skipParameterSchemas,proto3" json:"skip_parameter_schemas,omitempty"` - SkipResources bool `protobuf:"varint,4,opt,name=skip_resources,json=skipResources,proto3" json:"skip_resources,omitempty"` + ParameterValues []*proto.ParameterValue `protobuf:"bytes,1,rep,name=parameter_values,json=parameterValues,proto3" json:"parameter_values,omitempty"` + SkipParameterSchemas bool `protobuf:"varint,2,opt,name=skip_parameter_schemas,json=skipParameterSchemas,proto3" json:"skip_parameter_schemas,omitempty"` + SkipResources bool `protobuf:"varint,3,opt,name=skip_resources,json=skipResources,proto3" json:"skip_resources,omitempty"` } func (x *AcquiredJob_ProjectImport) Reset() { @@ -676,13 +675,6 @@ func (*AcquiredJob_ProjectImport) Descriptor() ([]byte, []int) { return file_provisionerd_proto_provisionerd_proto_rawDescGZIP(), []int{1, 1} } -func (x *AcquiredJob_ProjectImport) GetProjectName() string { - if x != nil { - return x.ProjectName - } - return "" -} - func (x *AcquiredJob_ProjectImport) GetParameterValues() []*proto.ParameterValue { if x != nil { return x.ParameterValues @@ -831,7 +823,7 @@ var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{ 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x1a, 0x26, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x07, 0x0a, - 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x9b, 0x06, 0x0a, 0x0b, 0x41, 0x63, 0x71, 0x75, 0x69, + 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0xf8, 0x05, 0x0a, 0x0b, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, @@ -867,104 +859,102 @@ var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{ 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x1a, 0xd7, 0x01, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6d, - 0x70, 0x6f, 0x72, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x46, 0x0a, 0x10, 0x70, 0x61, 0x72, 0x61, 0x6d, - 0x65, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, - 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0f, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, - 0x34, 0x0a, 0x16, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, - 0x72, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x14, 0x73, 0x6b, 0x69, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, - 0x6b, 0x69, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x42, 0x06, 0x0a, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x22, 0x3b, 0x0a, 0x0c, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x65, - 0x64, 0x4a, 0x6f, 0x62, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, - 0x72, 0x22, 0x71, 0x0a, 0x14, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x65, - 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x31, 0x0a, 0x08, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x26, 0x0a, 0x0f, - 0x64, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x5f, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x6f, 0x70, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x64, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x4f, 0x6e, - 0x53, 0x74, 0x6f, 0x70, 0x22, 0x9e, 0x04, 0x0a, 0x0c, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, - 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x60, 0x0a, 0x13, - 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, - 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x12, 0x77, 0x6f, 0x72, 0x6b, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x51, - 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, - 0x6f, 0x62, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, - 0x48, 0x00, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6d, 0x70, 0x6f, 0x72, - 0x74, 0x1a, 0x5f, 0x0a, 0x12, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x33, 0x0a, - 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x1a, 0xd8, 0x01, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6d, - 0x70, 0x6f, 0x72, 0x74, 0x12, 0x49, 0x0a, 0x11, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, - 0x72, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x10, 0x70, - 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x12, - 0x3e, 0x0a, 0x0f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x74, 0x65, 0x1a, 0xb4, 0x01, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6d, + 0x70, 0x6f, 0x72, 0x74, 0x12, 0x46, 0x0a, 0x10, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, + 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0f, 0x70, 0x61, 0x72, + 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x34, 0x0a, 0x16, + 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x73, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x73, 0x6b, + 0x69, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x6b, 0x69, 0x70, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x22, 0x3b, 0x0a, 0x0c, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x65, 0x64, 0x4a, 0x6f, + 0x62, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x71, + 0x0a, 0x14, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x31, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, - 0x0e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, - 0x3c, 0x0a, 0x0e, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0d, - 0x73, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x42, 0x06, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x9a, 0x01, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x2f, 0x0a, - 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x4c, 0x6f, 0x67, - 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x2b, - 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, - 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, - 0x75, 0x74, 0x22, 0x49, 0x0a, 0x09, 0x4a, 0x6f, 0x62, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, - 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x65, 0x72, 0x64, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x2a, 0x34, 0x0a, - 0x09, 0x4c, 0x6f, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x50, 0x52, - 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x45, 0x4d, 0x4f, 0x4e, - 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, - 0x52, 0x10, 0x01, 0x32, 0x8c, 0x02, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x3c, 0x0a, 0x0a, 0x41, 0x63, 0x71, - 0x75, 0x69, 0x72, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x41, 0x63, 0x71, 0x75, - 0x69, 0x72, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x12, 0x3b, 0x0a, 0x09, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x65, 0x72, 0x64, 0x2e, 0x4a, 0x6f, 0x62, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x1a, 0x13, 0x2e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x28, 0x01, 0x12, 0x3c, 0x0a, 0x09, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4a, 0x6f, - 0x62, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, - 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x12, 0x3e, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, - 0x62, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, - 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x64, 0x65, 0x73, + 0x74, 0x72, 0x6f, 0x79, 0x5f, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x6f, 0x70, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0d, 0x64, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x4f, 0x6e, 0x53, 0x74, 0x6f, + 0x70, 0x22, 0x9e, 0x04, 0x0a, 0x0c, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, + 0x6f, 0x62, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x60, 0x0a, 0x13, 0x77, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, + 0x6f, 0x62, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x51, 0x0a, 0x0e, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, + 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x00, 0x52, + 0x0d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x1a, 0x5f, + 0x0a, 0x12, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x33, 0x0a, 0x09, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x1a, + 0xd8, 0x01, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6d, 0x70, 0x6f, 0x72, + 0x74, 0x12, 0x49, 0x0a, 0x11, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x73, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x65, 0x74, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x10, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x12, 0x3e, 0x0a, 0x0f, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0e, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x0e, + 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0d, 0x73, 0x74, 0x6f, + 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x22, 0x9a, 0x01, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x2f, 0x0a, 0x06, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x4c, 0x6f, 0x67, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x6c, + 0x65, 0x76, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, + 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, + 0x49, 0x0a, 0x09, 0x4a, 0x6f, 0x62, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x15, 0x0a, 0x06, + 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, + 0x62, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, + 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x2a, 0x34, 0x0a, 0x09, 0x4c, 0x6f, + 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x50, 0x52, 0x4f, 0x56, 0x49, + 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x45, 0x4d, 0x4f, 0x4e, 0x10, 0x00, 0x12, + 0x0f, 0x0a, 0x0b, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x52, 0x10, 0x01, + 0x32, 0x8c, 0x02, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, + 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x3c, 0x0a, 0x0a, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, + 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x64, 0x4a, 0x6f, 0x62, 0x12, 0x3b, 0x0a, 0x09, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, + 0x62, 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, + 0x2e, 0x4a, 0x6f, 0x62, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x28, + 0x01, 0x12, 0x3c, 0x0a, 0x09, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4a, 0x6f, 0x62, 0x12, 0x1a, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, + 0x3e, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1a, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, + 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, + 0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, + 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/provisionerd/proto/provisionerd.proto b/provisionerd/proto/provisionerd.proto index 79396cd326c1e..35c7298693f77 100644 --- a/provisionerd/proto/provisionerd.proto +++ b/provisionerd/proto/provisionerd.proto @@ -18,10 +18,9 @@ message AcquiredJob { bytes state = 4; } message ProjectImport { - string project_name = 1; - repeated provisioner.ParameterValue parameter_values = 2; - bool skip_parameter_schemas = 3; - bool skip_resources = 4; + repeated provisioner.ParameterValue parameter_values = 1; + bool skip_parameter_schemas = 2; + bool skip_resources = 3; } string job_id = 1; int64 created_at = 2; diff --git a/provisionerd/provisionerd.go b/provisionerd/provisionerd.go index 82848976e72f6..f771ad9094cd1 100644 --- a/provisionerd/provisionerd.go +++ b/provisionerd/provisionerd.go @@ -320,9 +320,7 @@ func (p *provisionerDaemon) runJob(ctx context.Context, job *proto.AcquiredJob) switch jobType := job.Type.(type) { case *proto.AcquiredJob_ProjectImport_: - p.opts.Logger.Debug(context.Background(), "acquired job is project import", - slog.F("project_name", jobType.ProjectImport.ProjectName), - ) + p.opts.Logger.Debug(context.Background(), "acquired job is project import") p.runProjectImport(ctx, provisioner, job) case *proto.AcquiredJob_WorkspaceProvision_: @@ -466,7 +464,6 @@ func (p *provisionerDaemon) runProjectImportProvision(ctx context.Context, provi p.opts.Logger.Debug(context.Background(), "project import provision job logged", slog.F("level", msgType.Log.Level), slog.F("output", msgType.Log.Output), - slog.F("project_name", job.GetProjectImport().ProjectName), ) err = p.updateStream.Send(&proto.JobUpdate{ @@ -482,7 +479,7 @@ func (p *provisionerDaemon) runProjectImportProvision(ctx context.Context, provi return nil, xerrors.Errorf("send job update: %w", err) } case *sdkproto.Provision_Response_Complete: - p.opts.Logger.Info(context.Background(), "provision successful; marking job as complete", + p.opts.Logger.Info(context.Background(), "parse dry-run provision successful", slog.F("resource_count", len(msgType.Complete.Resources)), slog.F("resources", msgType.Complete.Resources), slog.F("state_length", len(msgType.Complete.State)),