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)), pFad - Phonifier reborn

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

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


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy