From 7167fa0cc5f4f9cd5ac809fd802dc33850ba994a Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 10 Feb 2022 15:55:37 +0000 Subject: [PATCH 01/20] Refactor parameter parsing to return nil values if none computed --- cli/projectcreate.go | 12 +- coderd/coderd.go | 2 +- coderd/parameter/parameter.go | 199 +++++++++++++++ .../parameter_test.go} | 108 ++++---- coderd/parameters.go | 41 +++ coderd/projectparameter/projectparameter.go | 233 ------------------ coderd/projectversion.go | 85 ------- coderd/projectversion_test.go | 58 ----- coderd/provisionerdaemons.go | 49 +++- coderd/provisionerjobs.go | 38 +++ coderd/provisionerjobs_test.go | 39 +++ codersdk/projects.go | 14 -- codersdk/projects_test.go | 22 -- codersdk/provisioners.go | 14 ++ codersdk/provisioners_test.go | 7 + templates/null/main.tf | 5 + 16 files changed, 441 insertions(+), 485 deletions(-) create mode 100644 coderd/parameter/parameter.go rename coderd/{projectparameter/projectparameter_test.go => parameter/parameter_test.go} (66%) delete mode 100644 coderd/projectparameter/projectparameter.go create mode 100644 templates/null/main.tf diff --git a/cli/projectcreate.go b/cli/projectcreate.go index eb5219cb816cb..4cde2024053a4 100644 --- a/cli/projectcreate.go +++ b/cli/projectcreate.go @@ -3,7 +3,6 @@ package cli import ( "archive/tar" "bytes" - "context" "fmt" "io" "os" @@ -91,7 +90,7 @@ func projectCreate() *cobra.Command { } spin.Stop() - logs, err := client.FollowProvisionerJobLogsAfter(context.Background(), organization.Name, job.ID, time.Time{}) + logs, err := client.FollowProvisionerJobLogsAfter(cmd.Context(), organization.Name, job.ID, time.Time{}) if err != nil { return err } @@ -103,6 +102,15 @@ func projectCreate() *cobra.Command { _, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s %s\n", color.HiGreenString("[parse]"), log.Output) } + schemas, err := client.ProvisionerJobParameterSchemas(cmd.Context(), organization.Name, job.ID) + if err != nil { + return err + } + + for _, schema := range schemas { + fmt.Printf("Schema: %+v\n", schema) + } + _, _ = fmt.Fprintf(cmd.OutOrStdout(), "Create project %q!\n", name) return nil }, diff --git a/coderd/coderd.go b/coderd/coderd.go index fcc6530c6811c..f0df2fdb6d119 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -73,7 +73,6 @@ func New(options *Options) http.Handler { r.Route("/{projectversion}", func(r chi.Router) { r.Use(httpmw.ExtractProjectVersionParam(api.Database)) r.Get("/", api.projectVersionByOrganizationAndName) - r.Get("/parameters", api.projectVersionParametersByOrganizationAndName) }) }) }) @@ -122,6 +121,7 @@ func New(options *Options) http.Handler { r.Route("/{provisionerjob}", func(r chi.Router) { r.Use(httpmw.ExtractProvisionerJobParam(options.Database)) r.Get("/", api.provisionerJobByOrganization) + r.Get("/parameters", api.provisionerJobParametersByID) r.Get("/logs", api.provisionerJobLogsByID) }) }) diff --git a/coderd/parameter/parameter.go b/coderd/parameter/parameter.go new file mode 100644 index 0000000000000..d8c7fe73778fc --- /dev/null +++ b/coderd/parameter/parameter.go @@ -0,0 +1,199 @@ +package parameter + +import ( + "context" + "database/sql" + "errors" + + "github.com/google/uuid" + "golang.org/x/xerrors" + + "github.com/coder/coder/database" +) + +// Scope targets identifiers to pull parameters from. +type Scope struct { + ImportJobID uuid.UUID + OrganizationID string + UserID string + ProjectID uuid.NullUUID + WorkspaceID uuid.NullUUID +} + +type Value struct { + DestinationScheme database.ParameterDestinationScheme + Name string + Value string + // Default is whether the default value from the schema + // was consumed. + Default bool + Scope database.ParameterScope + ScopeID string +} + +type Computed struct { + Schema database.ParameterSchema + // Value is nil when no value was computed for the schema. + Value *Value +} + +// 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, additional ...database.ParameterValue) ([]Computed, error) { + compute := &compute{ + db: db, + parametersByName: map[string]Computed{}, + } + + // All parameters for the import job ID! + parameterSchemas, err := db.GetParameterSchemasByJobID(ctx, scope.ImportJobID) + if errors.Is(err, sql.ErrNoRows) { + err = nil + } + if err != nil { + return nil, xerrors.Errorf("get project parameters: %w", err) + } + for _, parameterSchema := range parameterSchemas { + compute.parametersByName[parameterSchema.Name] = Computed{ + Schema: parameterSchema, + } + } + + // Organization parameters come first! + err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{ + Scope: database.ParameterScopeOrganization, + ScopeID: scope.OrganizationID, + }) + if err != nil { + return nil, err + } + + // Default project parameter values come second! + for _, parameterSchema := range parameterSchemas { + if !parameterSchema.DefaultSourceValue.Valid { + continue + } + if !parameterSchema.DefaultDestinationValue.Valid { + continue + } + + switch parameterSchema.DefaultSourceScheme { + case database.ParameterSourceSchemeData: + compute.parametersByName[parameterSchema.Name] = Computed{ + Value: &Value{ + DestinationScheme: parameterSchema.DefaultDestinationScheme, + Name: parameterSchema.DefaultDestinationValue.String, + Value: parameterSchema.DefaultSourceValue.String, + Default: true, + Scope: database.ParameterScopeProject, + ScopeID: scope.ProjectID.UUID.String(), + }, + Schema: parameterSchema, + } + default: + return nil, xerrors.Errorf("unsupported source scheme for project version parameter %q: %q", parameterSchema.Name, string(parameterSchema.DefaultSourceScheme)) + } + } + + 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 + } + } + + // User parameters come fourth! + err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{ + Scope: database.ParameterScopeUser, + ScopeID: scope.UserID, + }) + if err != nil { + return nil, err + } + + if scope.WorkspaceID.Valid { + // Workspace parameters come last! + err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{ + Scope: database.ParameterScopeWorkspace, + ScopeID: scope.WorkspaceID.UUID.String(), + }) + if err != nil { + return nil, err + } + } + + for _, parameterValue := range additional { + err = compute.injectSingle(parameterValue) + if err != nil { + return nil, xerrors.Errorf("inject %q: %w", parameterValue.Name, err) + } + } + + values := make([]Computed, 0, len(compute.parametersByName)) + for _, value := range compute.parametersByName { + values = append(values, value) + } + return values, nil +} + +type compute struct { + db database.Store + parametersByName map[string]Computed +} + +// Validates and computes the value for parameters; setting the value on "parameterByName". +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 + } + if err != nil { + return xerrors.Errorf("get %s parameters: %w", scopeParams.Scope, err) + } + + for _, scopedParameter := range scopedParameters { + 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 { + computed, hasComputed := c.parametersByName[scopedParameter.Name] + if !hasComputed { + // Don't inject parameters that aren't defined by the project. + return nil + } + + if computed.Value != nil { + // 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 !computed.Schema.AllowOverrideSource && scopedParameter.Scope != database.ParameterScopeProject { + return nil + } + } + + switch scopedParameter.SourceScheme { + case database.ParameterSourceSchemeData: + computed.Value = &Value{ + DestinationScheme: scopedParameter.DestinationScheme, + Name: scopedParameter.SourceValue, + Value: scopedParameter.DestinationValue, + Scope: scopedParameter.Scope, + ScopeID: scopedParameter.ScopeID, + Default: false, + } + c.parametersByName[scopedParameter.Name] = computed + default: + return xerrors.Errorf("unsupported source scheme: %q", string(computed.Schema.DefaultSourceScheme)) + } + return nil +} diff --git a/coderd/projectparameter/projectparameter_test.go b/coderd/parameter/parameter_test.go similarity index 66% rename from coderd/projectparameter/projectparameter_test.go rename to coderd/parameter/parameter_test.go index 6fb04701c606c..aa064683c64f3 100644 --- a/coderd/projectparameter/projectparameter_test.go +++ b/coderd/parameter/parameter_test.go @@ -1,4 +1,4 @@ -package projectparameter_test +package parameter_test import ( "context" @@ -8,17 +8,16 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/require" - "github.com/coder/coder/coderd/projectparameter" + "github.com/coder/coder/coderd/parameter" "github.com/coder/coder/cryptorand" "github.com/coder/coder/database" "github.com/coder/coder/database/databasefake" - "github.com/coder/coder/provisionersdk/proto" ) func TestCompute(t *testing.T) { t.Parallel() - generateScope := func() projectparameter.Scope { - return projectparameter.Scope{ + generateScope := func() parameter.Scope { + return parameter.Scope{ ImportJobID: uuid.New(), OrganizationID: uuid.NewString(), ProjectID: uuid.NullUUID{ @@ -29,19 +28,16 @@ func TestCompute(t *testing.T) { UUID: uuid.New(), Valid: true, }, - UserID: sql.NullString{ - String: uuid.NewString(), - Valid: true, - }, + UserID: uuid.NewString(), } } - type projectParameterOptions struct { + type parameterOptions struct { AllowOverrideSource bool AllowOverrideDestination bool DefaultDestinationScheme database.ParameterDestinationScheme ImportJobID uuid.UUID } - generateProjectParameter := func(t *testing.T, db database.Store, opts projectParameterOptions) database.ParameterSchema { + generateParameter := func(t *testing.T, db database.Store, opts parameterOptions) database.ParameterSchema { if opts.DefaultDestinationScheme == "" { opts.DefaultDestinationScheme = database.ParameterDestinationSchemeEnvironmentVariable } @@ -76,50 +72,48 @@ func TestCompute(t *testing.T) { t.Parallel() db := databasefake.New() scope := generateScope() - parameter, err := db.InsertParameterSchema(context.Background(), database.InsertParameterSchemaParams{ + parameterSchema, err := db.InsertParameterSchema(context.Background(), database.InsertParameterSchemaParams{ ID: uuid.New(), JobID: scope.ImportJobID, Name: "hey", }) require.NoError(t, err) - - _, err = projectparameter.Compute(context.Background(), db, scope) - var noValueErr projectparameter.NoValueError - require.ErrorAs(t, err, &noValueErr) - require.Equal(t, parameter.ID.String(), noValueErr.ParameterID.String()) - require.Equal(t, parameter.Name, noValueErr.ParameterName) + computed, err := parameter.Compute(context.Background(), db, scope) + require.NoError(t, err) + require.Equal(t, parameterSchema.ID.String(), computed[0].Schema.ID.String()) + require.Nil(t, computed[0].Value) }) t.Run("UseDefaultProjectValue", func(t *testing.T) { t.Parallel() db := databasefake.New() scope := generateScope() - parameter := generateProjectParameter(t, db, projectParameterOptions{ + parameterSchema := generateParameter(t, db, parameterOptions{ ImportJobID: scope.ImportJobID, DefaultDestinationScheme: database.ParameterDestinationSchemeProvisionerVariable, }) - values, err := projectparameter.Compute(context.Background(), db, scope) - require.NoError(t, err) - require.Len(t, values, 1) - value := values[0] - require.True(t, value.DefaultValue) - require.Equal(t, database.ParameterScopeProject, value.Scope) - 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) + computed, err := parameter.Compute(context.Background(), db, scope) + require.NoError(t, err) + require.Len(t, computed, 1) + computedValue := computed[0] + require.True(t, computedValue.Value.Default) + require.Equal(t, database.ParameterScopeProject, computedValue.Value.Scope) + require.Equal(t, scope.ProjectID.UUID.String(), computedValue.Value.ScopeID) + require.Equal(t, computedValue.Value.Name, parameterSchema.DefaultDestinationValue.String) + require.Equal(t, computedValue.Value.DestinationScheme, database.ParameterDestinationSchemeProvisionerVariable) + require.Equal(t, computedValue.Value.Value, parameterSchema.DefaultSourceValue.String) }) t.Run("OverrideOrganizationWithProjectDefault", func(t *testing.T) { t.Parallel() db := databasefake.New() scope := generateScope() - parameter := generateProjectParameter(t, db, projectParameterOptions{ + parameterSchema := generateParameter(t, db, parameterOptions{ ImportJobID: scope.ImportJobID, }) _, err := db.InsertParameterValue(context.Background(), database.InsertParameterValueParams{ ID: uuid.New(), - Name: parameter.Name, + Name: parameterSchema.Name, Scope: database.ParameterScopeOrganization, ScopeID: scope.OrganizationID, SourceScheme: database.ParameterSourceSchemeData, @@ -129,23 +123,23 @@ func TestCompute(t *testing.T) { }) require.NoError(t, err) - values, err := projectparameter.Compute(context.Background(), db, scope) + computed, err := parameter.Compute(context.Background(), db, scope) require.NoError(t, err) - require.Len(t, values, 1) - require.Equal(t, true, values[0].DefaultValue) - require.Equal(t, parameter.DefaultSourceValue.String, values[0].Proto.Value) + require.Len(t, computed, 1) + require.Equal(t, true, computed[0].Value.Default) + require.Equal(t, parameterSchema.DefaultSourceValue.String, computed[0].Value.Value) }) t.Run("ProjectOverridesProjectDefault", func(t *testing.T) { t.Parallel() db := databasefake.New() scope := generateScope() - parameter := generateProjectParameter(t, db, projectParameterOptions{ + parameterSchema := generateParameter(t, db, parameterOptions{ ImportJobID: scope.ImportJobID, }) value, err := db.InsertParameterValue(context.Background(), database.InsertParameterValueParams{ ID: uuid.New(), - Name: parameter.Name, + Name: parameterSchema.Name, Scope: database.ParameterScopeProject, ScopeID: scope.ProjectID.UUID.String(), SourceScheme: database.ParameterSourceSchemeData, @@ -155,23 +149,23 @@ func TestCompute(t *testing.T) { }) require.NoError(t, err) - values, err := projectparameter.Compute(context.Background(), db, scope) + computed, err := parameter.Compute(context.Background(), db, scope) require.NoError(t, err) - require.Len(t, values, 1) - require.Equal(t, false, values[0].DefaultValue) - require.Equal(t, value.DestinationValue, values[0].Proto.Value) + require.Len(t, computed, 1) + require.Equal(t, false, computed[0].Value.Default) + require.Equal(t, value.DestinationValue, computed[0].Value.Value) }) t.Run("WorkspaceCannotOverwriteProjectDefault", func(t *testing.T) { t.Parallel() db := databasefake.New() scope := generateScope() - parameter := generateProjectParameter(t, db, projectParameterOptions{ + parameterSchema := generateParameter(t, db, parameterOptions{ ImportJobID: scope.ImportJobID, }) _, err := db.InsertParameterValue(context.Background(), database.InsertParameterValueParams{ ID: uuid.New(), - Name: parameter.Name, + Name: parameterSchema.Name, Scope: database.ParameterScopeWorkspace, ScopeID: scope.WorkspaceID.UUID.String(), SourceScheme: database.ParameterSourceSchemeData, @@ -181,23 +175,23 @@ func TestCompute(t *testing.T) { }) require.NoError(t, err) - values, err := projectparameter.Compute(context.Background(), db, scope) + computed, err := parameter.Compute(context.Background(), db, scope) require.NoError(t, err) - require.Len(t, values, 1) - require.Equal(t, true, values[0].DefaultValue) + require.Len(t, computed, 1) + require.Equal(t, true, computed[0].Value.Default) }) t.Run("WorkspaceOverwriteProjectDefault", func(t *testing.T) { t.Parallel() db := databasefake.New() scope := generateScope() - parameter := generateProjectParameter(t, db, projectParameterOptions{ + parameterSchema := generateParameter(t, db, parameterOptions{ AllowOverrideSource: true, ImportJobID: scope.ImportJobID, }) _, err := db.InsertParameterValue(context.Background(), database.InsertParameterValueParams{ ID: uuid.New(), - Name: parameter.Name, + Name: parameterSchema.Name, Scope: database.ParameterScopeWorkspace, ScopeID: scope.WorkspaceID.UUID.String(), SourceScheme: database.ParameterSourceSchemeData, @@ -207,23 +201,23 @@ func TestCompute(t *testing.T) { }) require.NoError(t, err) - values, err := projectparameter.Compute(context.Background(), db, scope) + computed, err := parameter.Compute(context.Background(), db, scope) require.NoError(t, err) - require.Len(t, values, 1) - require.Equal(t, false, values[0].DefaultValue) + require.Len(t, computed, 1) + require.Equal(t, false, computed[0].Value.Default) }) t.Run("AdditionalOverwriteWorkspace", func(t *testing.T) { t.Parallel() db := databasefake.New() scope := generateScope() - parameter := generateProjectParameter(t, db, projectParameterOptions{ + parameterSchema := generateParameter(t, db, parameterOptions{ AllowOverrideSource: true, ImportJobID: scope.ImportJobID, }) _, err := db.InsertParameterValue(context.Background(), database.InsertParameterValueParams{ ID: uuid.New(), - Name: parameter.Name, + Name: parameterSchema.Name, Scope: database.ParameterScopeWorkspace, ScopeID: scope.WorkspaceID.UUID.String(), SourceScheme: database.ParameterSourceSchemeData, @@ -233,8 +227,8 @@ func TestCompute(t *testing.T) { }) require.NoError(t, err) - values, err := projectparameter.Compute(context.Background(), db, scope, database.ParameterValue{ - Name: parameter.Name, + computed, err := parameter.Compute(context.Background(), db, scope, database.ParameterValue{ + Name: parameterSchema.Name, Scope: database.ParameterScopeUser, SourceScheme: database.ParameterSourceSchemeData, SourceValue: "nop", @@ -242,7 +236,7 @@ func TestCompute(t *testing.T) { DestinationValue: "testing", }) require.NoError(t, err) - require.Len(t, values, 1) - require.Equal(t, "testing", values[0].Proto.Value) + require.Len(t, computed, 1) + require.Equal(t, "testing", computed[0].Value.Value) }) } diff --git a/coderd/parameters.go b/coderd/parameters.go index b3aa1d4967bd9..b7672a22031f9 100644 --- a/coderd/parameters.go +++ b/coderd/parameters.go @@ -23,6 +23,26 @@ type CreateParameterValueRequest struct { DestinationValue string `json:"destination_value" validate:"required"` } +// ParameterSchema represents a parameter parsed from project version source. +type ParameterSchema struct { + ID uuid.UUID `json:"id"` + CreatedAt time.Time `json:"created_at"` + Name string `json:"name"` + Description string `json:"description,omitempty"` + DefaultSourceScheme database.ParameterSourceScheme `json:"default_source_scheme,omitempty"` + DefaultSourceValue string `json:"default_source_value,omitempty"` + AllowOverrideSource bool `json:"allow_override_source"` + DefaultDestinationScheme database.ParameterDestinationScheme `json:"default_destination_scheme,omitempty"` + DefaultDestinationValue string `json:"default_destination_value,omitempty"` + AllowOverrideDestination bool `json:"allow_override_destination"` + DefaultRefresh string `json:"default_refresh"` + RedisplayValue bool `json:"redisplay_value"` + ValidationError string `json:"validation_error,omitempty"` + ValidationCondition string `json:"validation_condition,omitempty"` + ValidationTypeSystem database.ParameterTypeSystem `json:"validation_type_system,omitempty"` + ValidationValueType string `json:"validation_value_type,omitempty"` +} + // ParameterValue represents a set value for the scope. type ParameterValue struct { ID uuid.UUID `json:"id"` @@ -92,6 +112,27 @@ func parametersForScope(rw http.ResponseWriter, r *http.Request, db database.Sto render.JSON(rw, r, apiParameterValues) } +func convertParameterSchema(parameter database.ParameterSchema) ParameterSchema { + return ParameterSchema{ + ID: parameter.ID, + CreatedAt: parameter.CreatedAt, + Name: parameter.Name, + Description: parameter.Description, + DefaultSourceScheme: parameter.DefaultSourceScheme, + DefaultSourceValue: parameter.DefaultSourceValue.String, + AllowOverrideSource: parameter.AllowOverrideSource, + DefaultDestinationScheme: parameter.DefaultDestinationScheme, + DefaultDestinationValue: parameter.DefaultDestinationValue.String, + AllowOverrideDestination: parameter.AllowOverrideDestination, + DefaultRefresh: parameter.DefaultRefresh, + RedisplayValue: parameter.RedisplayValue, + ValidationError: parameter.ValidationError, + ValidationCondition: parameter.ValidationCondition, + ValidationTypeSystem: parameter.ValidationTypeSystem, + ValidationValueType: parameter.ValidationValueType, + } +} + func convertParameterValue(parameterValue database.ParameterValue) ParameterValue { return ParameterValue{ ID: parameterValue.ID, diff --git a/coderd/projectparameter/projectparameter.go b/coderd/projectparameter/projectparameter.go deleted file mode 100644 index 7288c8086c9ca..0000000000000 --- a/coderd/projectparameter/projectparameter.go +++ /dev/null @@ -1,233 +0,0 @@ -package projectparameter - -import ( - "context" - "database/sql" - "errors" - "fmt" - - "github.com/google/uuid" - "golang.org/x/xerrors" - - "github.com/coder/coder/database" - "github.com/coder/coder/provisionersdk/proto" -) - -// Scope targets identifiers to pull parameters from. -type Scope struct { - ImportJobID uuid.UUID - OrganizationID string - ProjectID uuid.NullUUID - UserID sql.NullString - WorkspaceID uuid.NullUUID -} - -// Value represents a computed parameter. -type Value struct { - Proto *proto.ParameterValue - // DefaultValue is whether a default value for the scope - // was consumed. This can only be true for projects. - DefaultValue bool - Scope database.ParameterScope - ScopeID string -} - -// 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, additional ...database.ParameterValue) ([]Value, error) { - compute := &compute{ - db: db, - computedParameterByName: map[string]Value{}, - parameterSchemasByName: map[string]database.ParameterSchema{}, - } - - // All parameters for the import job ID! - parameterSchemas, err := db.GetParameterSchemasByJobID(ctx, scope.ImportJobID) - if errors.Is(err, sql.ErrNoRows) { - err = nil - } - if err != nil { - return nil, xerrors.Errorf("get project parameters: %w", err) - } - for _, projectVersionParameter := range parameterSchemas { - compute.parameterSchemasByName[projectVersionParameter.Name] = projectVersionParameter - } - - // Organization parameters come first! - err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{ - Scope: database.ParameterScopeOrganization, - ScopeID: scope.OrganizationID, - }) - if err != nil { - return nil, err - } - - // Default project parameter values come second! - for _, projectVersionParameter := range parameterSchemas { - if !projectVersionParameter.DefaultSourceValue.Valid { - continue - } - if !projectVersionParameter.DefaultDestinationValue.Valid { - continue - } - - destinationScheme, err := convertDestinationScheme(projectVersionParameter.DefaultDestinationScheme) - if err != nil { - return nil, xerrors.Errorf("convert default destination scheme for project version parameter %q: %w", projectVersionParameter.Name, err) - } - - switch projectVersionParameter.DefaultSourceScheme { - case database.ParameterSourceSchemeData: - compute.computedParameterByName[projectVersionParameter.Name] = Value{ - Proto: &proto.ParameterValue{ - DestinationScheme: destinationScheme, - Name: projectVersionParameter.DefaultDestinationValue.String, - Value: projectVersionParameter.DefaultSourceValue.String, - }, - DefaultValue: true, - Scope: database.ParameterScopeProject, - ScopeID: scope.ProjectID.UUID.String(), - } - default: - return nil, xerrors.Errorf("unsupported source scheme for project version parameter %q: %q", projectVersionParameter.Name, string(projectVersionParameter.DefaultSourceScheme)) - } - } - - 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.injectScope(ctx, database.GetParameterValuesByScopeParams{ - Scope: database.ParameterScopeUser, - ScopeID: scope.UserID.String, - }) - if err != nil { - return nil, err - } - } - - if scope.WorkspaceID.Valid { - // Workspace parameters come last! - err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{ - Scope: database.ParameterScopeWorkspace, - ScopeID: scope.WorkspaceID.UUID.String(), - }) - if err != nil { - return nil, err - } - } - - 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 - } - return nil, NoValueError{ - ParameterID: projectVersionParameter.ID, - ParameterName: projectVersionParameter.Name, - } - } - - values := make([]Value, 0, len(compute.computedParameterByName)) - for _, value := range compute.computedParameterByName { - values = append(values, value) - } - return values, nil -} - -type compute struct { - 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) injectScope(ctx context.Context, scopeParams database.GetParameterValuesByScopeParams) error { - scopedParameters, err := c.db.GetParameterValuesByScope(ctx, scopeParams) - if errors.Is(err, sql.ErrNoRows) { - err = nil - } - if err != nil { - return xerrors.Errorf("get %s parameters: %w", scopeParams.Scope, err) - } - - for _, scopedParameter := range scopedParameters { - 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 !parameterSchema.AllowOverrideSource && scopedParameter.Scope != database.ParameterScopeProject { - return nil - } - } - } - - destinationScheme, err := convertDestinationScheme(scopedParameter.DestinationScheme) - if err != nil { - return xerrors.Errorf("convert destination scheme: %w", err) - } - - 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 -} - -// Converts the database destination scheme to the protobuf version. -func convertDestinationScheme(scheme database.ParameterDestinationScheme) (proto.ParameterDestination_Scheme, error) { - switch scheme { - case database.ParameterDestinationSchemeEnvironmentVariable: - return proto.ParameterDestination_ENVIRONMENT_VARIABLE, nil - case database.ParameterDestinationSchemeProvisionerVariable: - return proto.ParameterDestination_PROVISIONER_VARIABLE, nil - default: - return 0, xerrors.Errorf("unsupported destination scheme: %q", scheme) - } -} - -type NoValueError struct { - ParameterID uuid.UUID - ParameterName string -} - -func (e NoValueError) Error() string { - return fmt.Sprintf("no value for parameter %q found", e.ParameterName) -} diff --git a/coderd/projectversion.go b/coderd/projectversion.go index 2b603e2e10665..c1b3ecdd29ace 100644 --- a/coderd/projectversion.go +++ b/coderd/projectversion.go @@ -26,26 +26,6 @@ type ProjectVersion struct { 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"` - Name string `json:"name"` - Description string `json:"description,omitempty"` - DefaultSourceScheme database.ParameterSourceScheme `json:"default_source_scheme,omitempty"` - DefaultSourceValue string `json:"default_source_value,omitempty"` - AllowOverrideSource bool `json:"allow_override_source"` - DefaultDestinationScheme database.ParameterDestinationScheme `json:"default_destination_scheme,omitempty"` - DefaultDestinationValue string `json:"default_destination_value,omitempty"` - AllowOverrideDestination bool `json:"allow_override_destination"` - DefaultRefresh string `json:"default_refresh"` - RedisplayValue bool `json:"redisplay_value"` - ValidationError string `json:"validation_error,omitempty"` - ValidationCondition string `json:"validation_condition,omitempty"` - ValidationTypeSystem database.ParameterTypeSystem `json:"validation_type_system,omitempty"` - ValidationValueType string `json:"validation_value_type,omitempty"` -} - // CreateProjectVersionRequest enables callers to create a new Project Version. type CreateProjectVersionRequest struct { ImportJobID uuid.UUID `json:"import_job_id" validate:"required"` @@ -121,50 +101,6 @@ func (api *api) postProjectVersionByOrganization(rw http.ResponseWriter, r *http render.JSON(rw, r, convertProjectVersion(projectVersion)) } -func (api *api) projectVersionParametersByOrganizationAndName(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 - } - apiJob := convertProvisionerJob(job) - if !apiJob.Status.Completed() { - httpapi.Write(rw, http.StatusPreconditionRequired, httpapi.Response{ - Message: fmt.Sprintf("import job hasn't completed: %s", apiJob.Status), - }) - return - } - if apiJob.Status != ProvisionerJobStatusSucceeded { - httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ - Message: "import job wasn't successful. no parameters were parsed", - }) - return - } - - parameters, err := api.Database.GetParameterSchemasByJobID(r.Context(), projectVersion.ImportJobID) - if errors.Is(err, sql.ErrNoRows) { - err = nil - parameters = []database.ParameterSchema{} - } - if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: fmt.Sprintf("get project parameters: %s", err), - }) - return - } - - apiParameters := make([]ProjectVersionParameter, 0, len(parameters)) - for _, parameter := range parameters { - apiParameters = append(apiParameters, convertProjectParameter(parameter)) - } - - render.Status(r, http.StatusOK) - render.JSON(rw, r, apiParameters) -} - func convertProjectVersion(version database.ProjectVersion) ProjectVersion { return ProjectVersion{ ID: version.ID, @@ -175,24 +111,3 @@ func convertProjectVersion(version database.ProjectVersion) ProjectVersion { ImportJobID: version.ImportJobID, } } - -func convertProjectParameter(parameter database.ParameterSchema) ProjectVersionParameter { - return ProjectVersionParameter{ - ID: parameter.ID, - CreatedAt: parameter.CreatedAt, - Name: parameter.Name, - Description: parameter.Description, - DefaultSourceScheme: parameter.DefaultSourceScheme, - DefaultSourceValue: parameter.DefaultSourceValue.String, - AllowOverrideSource: parameter.AllowOverrideSource, - DefaultDestinationScheme: parameter.DefaultDestinationScheme, - DefaultDestinationValue: parameter.DefaultDestinationValue.String, - AllowOverrideDestination: parameter.AllowOverrideDestination, - DefaultRefresh: parameter.DefaultRefresh, - RedisplayValue: parameter.RedisplayValue, - ValidationError: parameter.ValidationError, - ValidationCondition: parameter.ValidationCondition, - ValidationTypeSystem: parameter.ValidationTypeSystem, - ValidationValueType: parameter.ValidationValueType, - } -} diff --git a/coderd/projectversion_test.go b/coderd/projectversion_test.go index 467574cfa74e9..a842f3ec277cf 100644 --- a/coderd/projectversion_test.go +++ b/coderd/projectversion_test.go @@ -2,16 +2,12 @@ package coderd_test import ( "context" - "net/http" "testing" "github.com/stretchr/testify/require" "github.com/coder/coder/coderd" "github.com/coder/coder/coderd/coderdtest" - "github.com/coder/coder/codersdk" - "github.com/coder/coder/provisioner/echo" - "github.com/coder/coder/provisionersdk/proto" ) func TestProjectVersionsByOrganization(t *testing.T) { @@ -56,57 +52,3 @@ func TestPostProjectVersionByOrganization(t *testing.T) { require.NoError(t, err) }) } - -func TestProjectVersionParametersByOrganizationAndName(t *testing.T) { - t.Parallel() - t.Run("NotImported", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t) - user := coderdtest.CreateInitialUser(t, client) - 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()) - }) - - t.Run("FailedImport", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t) - user := coderdtest.CreateInitialUser(t, client) - _ = coderdtest.NewProvisionerDaemon(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, &echo.Responses{ - Provision: []*proto.Provision_Response{{}}, - }) - 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()) - }) - t.Run("List", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t) - user := coderdtest.CreateInitialUser(t, client) - _ = coderdtest.NewProvisionerDaemon(t, client) - 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{{ - Name: "example", - }}, - }, - }, - }}, - Provision: echo.ProvisionComplete, - }) - 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 0d51966f62a7c..7d2dbcb005903 100644 --- a/coderd/provisionerdaemons.go +++ b/coderd/provisionerdaemons.go @@ -22,7 +22,7 @@ import ( "cdr.dev/slog" - "github.com/coder/coder/coderd/projectparameter" + "github.com/coder/coder/coderd/parameter" "github.com/coder/coder/database" "github.com/coder/coder/httpapi" "github.com/coder/coder/provisionerd/proto" @@ -211,17 +211,14 @@ 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{ + parameters, err := parameter.Compute(ctx, server.Database, parameter.Scope{ ImportJobID: projectVersion.ImportJobID, OrganizationID: organization.ID, ProjectID: uuid.NullUUID{ UUID: project.ID, Valid: true, }, - UserID: sql.NullString{ - String: user.ID, - Valid: true, - }, + UserID: user.ID, WorkspaceID: uuid.NullUUID{ UUID: workspace.ID, Valid: true, @@ -233,7 +230,11 @@ func (server *provisionerdServer) AcquireJob(ctx context.Context, _ *proto.Empty // Convert parameters to the protobuf type. protoParameters := make([]*sdkproto.ParameterValue, 0, len(parameters)) for _, parameter := range parameters { - protoParameters = append(protoParameters, parameter.Proto) + converted, err := convertComputedParameter(parameter) + if err != nil { + return nil, failJob(fmt.Sprintf("convert parameter: %s", err)) + } + protoParameters = append(protoParameters, converted) } protoJob.Type = &proto.AcquiredJob_WorkspaceProvision_{ @@ -252,17 +253,14 @@ 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{ + parameters, err := parameter.Compute(ctx, server.Database, parameter.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, - }, + UserID: user.ID, }, input.AdditionalParameters...) if err != nil { return nil, failJob(fmt.Sprintf("compute parameters: %s", err)) @@ -270,7 +268,11 @@ func (server *provisionerdServer) AcquireJob(ctx context.Context, _ *proto.Empty // Convert parameters to the protobuf type. protoParameters := make([]*sdkproto.ParameterValue, 0, len(parameters)) for _, parameter := range parameters { - protoParameters = append(protoParameters, parameter.Proto) + converted, err := convertComputedParameter(parameter) + if err != nil { + return nil, failJob(fmt.Sprintf("convert parameter: %s", err)) + } + protoParameters = append(protoParameters, converted) } protoJob.Type = &proto.AcquiredJob_ProjectImport_{ @@ -614,3 +616,24 @@ func convertLogSource(logSource proto.LogSource) (database.LogSource, error) { return database.LogSource(""), xerrors.Errorf("unknown log source: %d", logSource) } } + +func convertComputedParameter(param parameter.Computed) (*sdkproto.ParameterValue, error) { + if param.Value == nil { + return nil, xerrors.Errorf("no value for computed parameter: %s", param.Schema.Name) + } + scheme := sdkproto.ParameterDestination_ENVIRONMENT_VARIABLE + switch param.Value.DestinationScheme { + case database.ParameterDestinationSchemeEnvironmentVariable: + scheme = sdkproto.ParameterDestination_ENVIRONMENT_VARIABLE + case database.ParameterDestinationSchemeProvisionerVariable: + scheme = sdkproto.ParameterDestination_PROVISIONER_VARIABLE + default: + return nil, xerrors.Errorf("unrecognized destination scheme: %q", param.Value.DestinationScheme) + } + + return &sdkproto.ParameterValue{ + DestinationScheme: scheme, + Name: param.Value.Name, + Value: param.Value.Value, + }, nil +} diff --git a/coderd/provisionerjobs.go b/coderd/provisionerjobs.go index 2eb4d69c34b7d..32e811ac89364 100644 --- a/coderd/provisionerjobs.go +++ b/coderd/provisionerjobs.go @@ -11,6 +11,7 @@ import ( "github.com/go-chi/render" "github.com/google/uuid" + "github.com/coder/coder/coderd/parameter" "github.com/coder/coder/database" "github.com/coder/coder/httpapi" "github.com/coder/coder/httpmw" @@ -44,6 +45,8 @@ type ProvisionerJob struct { WorkerID *uuid.UUID `json:"worker_id,omitempty"` } +type ComputedParameter struct{} + type CreateProjectImportJobRequest struct { StorageMethod database.ProvisionerStorageMethod `json:"storage_method" validate:"oneof=file,required"` StorageSource string `json:"storage_source" validate:"required"` @@ -118,6 +121,41 @@ func (api *api) postProvisionerImportJobByOrganization(rw http.ResponseWriter, r render.JSON(rw, r, convertProvisionerJob(job)) } +// Return parsed parameter schemas for a job. +func (api *api) provisionerJobParametersByID(rw http.ResponseWriter, r *http.Request) { + apiKey := httpmw.APIKey(r) + job := httpmw.ProvisionerJobParam(r) + if convertProvisionerJob(job).Status != ProvisionerJobStatusSucceeded { + httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + Message: fmt.Sprintf("Job is in state %q! Must be %q.", convertProvisionerJob(job).Status, ProvisionerJobStatusSucceeded), + }) + return + } + + parameter.Compute(r.Context(), api.Database, parameter.Scope{ + ImportJobID: job.ID, + OrganizationID: job.OrganizationID, + UserID: apiKey.UserID, + }) + + schemas, err := api.Database.GetParameterSchemasByJobID(r.Context(), job.ID) + if errors.Is(err, sql.ErrNoRows) { + err = nil + } + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: fmt.Sprintf("get parameter schemas: %s", err), + }) + return + } + apiSchemas := make([]ParameterSchema, 0, len(schemas)) + for _, parameter := range schemas { + apiSchemas = append(apiSchemas, convertParameterSchema(parameter)) + } + render.Status(r, http.StatusOK) + render.JSON(rw, r, apiSchemas) +} + func convertProvisionerJob(provisionerJob database.ProvisionerJob) ProvisionerJob { job := ProvisionerJob{ ID: provisionerJob.ID, diff --git a/coderd/provisionerjobs_test.go b/coderd/provisionerjobs_test.go index d16e4e976ba04..e16d1ad993e79 100644 --- a/coderd/provisionerjobs_test.go +++ b/coderd/provisionerjobs_test.go @@ -2,12 +2,14 @@ package coderd_test import ( "context" + "net/http" "testing" "time" "github.com/stretchr/testify/require" "github.com/coder/coder/coderd/coderdtest" + "github.com/coder/coder/codersdk" "github.com/coder/coder/provisioner/echo" "github.com/coder/coder/provisionersdk/proto" ) @@ -50,3 +52,40 @@ func TestPostProvisionerImportJobByOrganization(t *testing.T) { } }) } + +func TestProvisionerJobParametersByID(t *testing.T) { + t.Parallel() + t.Run("NotImported", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + user := coderdtest.CreateInitialUser(t, client) + job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + _, err := client.ProvisionerJobParameterSchemas(context.Background(), user.Organization, job.ID) + var apiErr *codersdk.Error + require.ErrorAs(t, err, &apiErr) + require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) + }) + + t.Run("List", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + user := coderdtest.CreateInitialUser(t, client) + _ = coderdtest.NewProvisionerDaemon(t, client) + 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{{ + Name: "example", + }}, + }, + }, + }}, + Provision: echo.ProvisionComplete, + }) + coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) + params, err := client.ProvisionerJobParameterSchemas(context.Background(), user.Organization, job.ID) + require.NoError(t, err) + require.Len(t, params, 1) + }) +} diff --git a/codersdk/projects.go b/codersdk/projects.go index b58825778c922..3e627ae571ed4 100644 --- a/codersdk/projects.go +++ b/codersdk/projects.go @@ -99,20 +99,6 @@ func (c *Client) CreateProjectVersion(ctx context.Context, organization, project return projectVersion, json.NewDecoder(res.Body).Decode(&projectVersion) } -// ProjectVersionParameters returns project parameters for a version by name. -func (c *Client) ProjectVersionParameters(ctx context.Context, organization, project, version string) ([]coderd.ProjectVersionParameter, error) { - res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/projects/%s/%s/versions/%s/parameters", organization, project, version), nil) - if err != nil { - return nil, err - } - defer res.Body.Close() - if res.StatusCode != http.StatusOK { - return nil, readBodyAsError(res) - } - var params []coderd.ProjectVersionParameter - return params, json.NewDecoder(res.Body).Decode(¶ms) -} - // ProjectParameters returns parameters scoped to a project. func (c *Client) ProjectParameters(ctx context.Context, organization, project string) ([]coderd.ParameterValue, error) { res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/projects/%s/%s/parameters", organization, project), nil) diff --git a/codersdk/projects_test.go b/codersdk/projects_test.go index f106d3a42652c..9fd765c7f138d 100644 --- a/codersdk/projects_test.go +++ b/codersdk/projects_test.go @@ -133,28 +133,6 @@ func TestCreateProjectVersion(t *testing.T) { }) } -func TestProjectVersionParameters(t *testing.T) { - t.Parallel() - t.Run("Error", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t) - _, err := client.ProjectVersionParameters(context.Background(), "some", "project", "version") - require.Error(t, err) - }) - - t.Run("List", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t) - user := coderdtest.CreateInitialUser(t, client) - coderdtest.NewProvisionerDaemon(t, client) - 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) - }) -} - func TestProjectParameters(t *testing.T) { t.Parallel() t.Run("Error", func(t *testing.T) { diff --git a/codersdk/provisioners.go b/codersdk/provisioners.go index 1aea254aeba7a..d9aea0471d90f 100644 --- a/codersdk/provisioners.go +++ b/codersdk/provisioners.go @@ -151,3 +151,17 @@ func (c *Client) FollowProvisionerJobLogsAfter(ctx context.Context, organization }() return logs, nil } + +// ProvisionerJobParameterSchemas returns project parameters for a version by name. +func (c *Client) ProvisionerJobParameterSchemas(ctx context.Context, organization string, job uuid.UUID) ([]coderd.ParameterSchema, error) { + res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/provisioners/jobs/%s/%s/parameters", organization, job), nil) + if err != nil { + return nil, err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return nil, readBodyAsError(res) + } + var params []coderd.ParameterSchema + return params, json.NewDecoder(res.Body).Decode(¶ms) +} diff --git a/codersdk/provisioners_test.go b/codersdk/provisioners_test.go index 6808dd19a3b78..60c3ad49f8b9d 100644 --- a/codersdk/provisioners_test.go +++ b/codersdk/provisioners_test.go @@ -106,3 +106,10 @@ func TestFollowProvisionerJobLogsAfter(t *testing.T) { require.False(t, ok) }) } + +func TestProvisionerJobParameterSchemas(t *testing.T) { + t.Parallel() + t.Run("Error", func(t *testing.T) { + + }) +} diff --git a/templates/null/main.tf b/templates/null/main.tf new file mode 100644 index 0000000000000..9bb3f2042e2a4 --- /dev/null +++ b/templates/null/main.tf @@ -0,0 +1,5 @@ +variable "bananas" { + description = "hello!" +} + +resource "null_resource" "example" {} From 65380db9dca036e1b82ac7a34b2b042a48608374 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 10 Feb 2022 19:37:07 +0000 Subject: [PATCH 02/20] Refactor parameter to allow for hiding redisplay --- coderd/parameter/compute.go | 210 +++++++++++++++++++++++++++++++ coderd/parameter/compute_test.go | 204 ++++++++++++++++++++++++++++++ 2 files changed, 414 insertions(+) create mode 100644 coderd/parameter/compute.go create mode 100644 coderd/parameter/compute_test.go diff --git a/coderd/parameter/compute.go b/coderd/parameter/compute.go new file mode 100644 index 0000000000000..493d72edf9ee9 --- /dev/null +++ b/coderd/parameter/compute.go @@ -0,0 +1,210 @@ +package parameter + +import ( + "context" + "database/sql" + "errors" + + "github.com/google/uuid" + "golang.org/x/xerrors" + + "github.com/coder/coder/database" +) + +// ComputeScope targets identifiers to pull parameters from. +type ComputeScope struct { + ProjectImportJobID uuid.UUID + OrganizationID string + UserID string + ProjectID uuid.NullUUID + WorkspaceID uuid.NullUUID +} + +type ComputeOptions struct { + // HideRedisplayValues removes the value from parameters that + // come from schemas with RedisplayValue set to false. + HideRedisplayValues bool +} + +// ComputedValue represents a computed parameter value. +type ComputedValue struct { + database.ParameterValue + SchemaID uuid.UUID `json:"schema_id"` + DefaultSourceValue bool `json:"default_source_value"` +} + +// 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 schemas provided. +func Compute(ctx context.Context, db database.Store, scope ComputeScope, options *ComputeOptions) ([]ComputedValue, error) { + if options == nil { + options = &ComputeOptions{} + } + compute := &compute{ + options: options, + db: db, + computedParameterByName: map[string]ComputedValue{}, + parameterSchemasByName: map[string]database.ParameterSchema{}, + } + + // All parameters for the import job ID! + parameterSchemas, err := db.GetParameterSchemasByJobID(ctx, scope.ProjectImportJobID) + if errors.Is(err, sql.ErrNoRows) { + err = nil + } + if err != nil { + return nil, xerrors.Errorf("get project parameters: %w", err) + } + for _, parameterSchema := range parameterSchemas { + compute.parameterSchemasByName[parameterSchema.Name] = parameterSchema + } + + // Organization parameters come first! + err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{ + Scope: database.ParameterScopeOrganization, + ScopeID: scope.OrganizationID, + }) + if err != nil { + return nil, err + } + + // Job parameters come second! + err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{ + Scope: database.ParameterScopeImportJob, + ScopeID: scope.ProjectImportJobID.String(), + }) + if err != nil { + return nil, err + } + + // Default project parameter values come second! + for _, parameterSchema := range parameterSchemas { + if parameterSchema.DefaultSourceScheme == database.ParameterSourceSchemeNone { + continue + } + if _, ok := compute.computedParameterByName[parameterSchema.Name]; ok { + // We already have a value! No need to use th default. + continue + } + + switch parameterSchema.DefaultSourceScheme { + case database.ParameterSourceSchemeData: + // Inject a default value scoped to the import job ID. + // This doesn't need to be inserted into the database, + // because it's a dynamic value associated with the schema. + err = compute.injectSingle(database.ParameterValue{ + ID: uuid.New(), + CreatedAt: database.Now(), + UpdatedAt: database.Now(), + SourceScheme: database.ParameterSourceSchemeData, + Name: parameterSchema.Name, + DestinationScheme: parameterSchema.DefaultDestinationScheme, + SourceValue: parameterSchema.DefaultSourceValue, + Scope: database.ParameterScopeImportJob, + ScopeID: scope.ProjectImportJobID.String(), + }, true) + if err != nil { + return nil, xerrors.Errorf("insert default value: %w", err) + } + default: + return nil, xerrors.Errorf("unsupported source scheme for project version parameter %q: %q", parameterSchema.Name, string(parameterSchema.DefaultSourceScheme)) + } + } + + 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 + } + } + + // User parameters come fourth! + err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{ + Scope: database.ParameterScopeUser, + ScopeID: scope.UserID, + }) + if err != nil { + return nil, err + } + + if scope.WorkspaceID.Valid { + // Workspace parameters come last! + err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{ + Scope: database.ParameterScopeWorkspace, + ScopeID: scope.WorkspaceID.UUID.String(), + }) + if err != nil { + return nil, err + } + } + + values := make([]ComputedValue, 0, len(compute.computedParameterByName)) + for _, value := range compute.computedParameterByName { + values = append(values, value) + } + return values, nil +} + +type compute struct { + options *ComputeOptions + db database.Store + computedParameterByName map[string]ComputedValue + parameterSchemasByName map[string]database.ParameterSchema +} + +// Validates and computes the value for parameters; setting the value on "parameterByName". +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 + } + if err != nil { + return xerrors.Errorf("get %s parameters: %w", scopeParams.Scope, err) + } + + for _, scopedParameter := range scopedParameters { + err = c.injectSingle(scopedParameter, false) + if err != nil { + return xerrors.Errorf("inject single %q: %w", scopedParameter.Name, err) + } + } + return nil +} + +func (c *compute) injectSingle(scopedParameter database.ParameterValue, defaultValue bool) error { + parameterSchema, hasParameterSchema := c.parameterSchemasByName[scopedParameter.Name] + if !hasParameterSchema { + // Don't inject parameters that aren't defined by the project. + return nil + } + + _, hasParameterValue := c.computedParameterByName[scopedParameter.Name] + if hasParameterValue { + if !parameterSchema.AllowOverrideSource && + // Users and workspaces cannot override anything on a project! + (scopedParameter.Scope == database.ParameterScopeUser || + scopedParameter.Scope == database.ParameterScopeWorkspace) { + return nil + } + } + + switch scopedParameter.SourceScheme { + case database.ParameterSourceSchemeData: + value := ComputedValue{ + ParameterValue: scopedParameter, + SchemaID: parameterSchema.ID, + DefaultSourceValue: defaultValue, + } + if c.options.HideRedisplayValues && !parameterSchema.RedisplayValue { + value.SourceValue = "" + } + c.computedParameterByName[scopedParameter.Name] = value + default: + return xerrors.Errorf("unsupported source scheme: %q", string(parameterSchema.DefaultSourceScheme)) + } + return nil +} diff --git a/coderd/parameter/compute_test.go b/coderd/parameter/compute_test.go new file mode 100644 index 0000000000000..c616c48f815e4 --- /dev/null +++ b/coderd/parameter/compute_test.go @@ -0,0 +1,204 @@ +package parameter_test + +import ( + "context" + "testing" + + "github.com/google/uuid" + "github.com/stretchr/testify/require" + + "github.com/coder/coder/coderd/parameter" + "github.com/coder/coder/cryptorand" + "github.com/coder/coder/database" + "github.com/coder/coder/database/databasefake" +) + +func TestCompute(t *testing.T) { + t.Parallel() + generateScope := func() parameter.ComputeScope { + return parameter.ComputeScope{ + ProjectImportJobID: uuid.New(), + OrganizationID: uuid.NewString(), + ProjectID: uuid.NullUUID{ + UUID: uuid.New(), + Valid: true, + }, + WorkspaceID: uuid.NullUUID{ + UUID: uuid.New(), + Valid: true, + }, + UserID: uuid.NewString(), + } + } + type parameterOptions struct { + AllowOverrideSource bool + AllowOverrideDestination bool + DefaultDestinationScheme database.ParameterDestinationScheme + ProjectImportJobID uuid.UUID + } + generateParameter := func(t *testing.T, db database.Store, opts parameterOptions) database.ParameterSchema { + if opts.DefaultDestinationScheme == "" { + opts.DefaultDestinationScheme = database.ParameterDestinationSchemeEnvironmentVariable + } + name, err := cryptorand.String(8) + require.NoError(t, err) + sourceValue, err := cryptorand.String(8) + require.NoError(t, err) + param, err := db.InsertParameterSchema(context.Background(), database.InsertParameterSchemaParams{ + ID: uuid.New(), + Name: name, + JobID: opts.ProjectImportJobID, + DefaultSourceScheme: database.ParameterSourceSchemeData, + DefaultSourceValue: sourceValue, + AllowOverrideSource: opts.AllowOverrideSource, + AllowOverrideDestination: opts.AllowOverrideDestination, + DefaultDestinationScheme: opts.DefaultDestinationScheme, + }) + require.NoError(t, err) + return param + } + + t.Run("NoValue", func(t *testing.T) { + t.Parallel() + db := databasefake.New() + scope := generateScope() + _, err := db.InsertParameterSchema(context.Background(), database.InsertParameterSchemaParams{ + ID: uuid.New(), + JobID: scope.ProjectImportJobID, + Name: "hey", + DefaultSourceScheme: database.ParameterSourceSchemeNone, + }) + require.NoError(t, err) + computed, err := parameter.Compute(context.Background(), db, scope, nil) + require.NoError(t, err) + require.Len(t, computed, 0) + }) + + t.Run("UseDefaultProjectValue", func(t *testing.T) { + t.Parallel() + db := databasefake.New() + scope := generateScope() + parameterSchema := generateParameter(t, db, parameterOptions{ + ProjectImportJobID: scope.ProjectImportJobID, + DefaultDestinationScheme: database.ParameterDestinationSchemeProvisionerVariable, + }) + computed, err := parameter.Compute(context.Background(), db, scope, nil) + require.NoError(t, err) + require.Len(t, computed, 1) + computedValue := computed[0] + require.True(t, computedValue.DefaultSourceValue) + require.Equal(t, database.ParameterScopeImportJob, computedValue.Scope) + require.Equal(t, scope.ProjectImportJobID.String(), computedValue.ScopeID) + require.Equal(t, computedValue.SourceValue, parameterSchema.DefaultSourceValue) + }) + + t.Run("OverrideOrganizationWithImportJob", func(t *testing.T) { + t.Parallel() + db := databasefake.New() + scope := generateScope() + parameterSchema := generateParameter(t, db, parameterOptions{ + ProjectImportJobID: scope.ProjectImportJobID, + }) + _, err := db.InsertParameterValue(context.Background(), database.InsertParameterValueParams{ + ID: uuid.New(), + Name: parameterSchema.Name, + Scope: database.ParameterScopeOrganization, + ScopeID: scope.OrganizationID, + SourceScheme: database.ParameterSourceSchemeData, + SourceValue: "firstnop", + DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable, + }) + require.NoError(t, err) + + value, err := db.InsertParameterValue(context.Background(), database.InsertParameterValueParams{ + ID: uuid.New(), + Name: parameterSchema.Name, + Scope: database.ParameterScopeImportJob, + ScopeID: scope.ProjectImportJobID.String(), + SourceScheme: database.ParameterSourceSchemeData, + SourceValue: "secondnop", + DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable, + }) + require.NoError(t, err) + + computed, err := parameter.Compute(context.Background(), db, scope, nil) + require.NoError(t, err) + require.Len(t, computed, 1) + require.Equal(t, false, computed[0].DefaultSourceValue) + require.Equal(t, value.SourceValue, computed[0].SourceValue) + }) + + t.Run("ProjectOverridesProjectDefault", func(t *testing.T) { + t.Parallel() + db := databasefake.New() + scope := generateScope() + parameterSchema := generateParameter(t, db, parameterOptions{ + ProjectImportJobID: scope.ProjectImportJobID, + }) + value, err := db.InsertParameterValue(context.Background(), database.InsertParameterValueParams{ + ID: uuid.New(), + Name: parameterSchema.Name, + Scope: database.ParameterScopeProject, + ScopeID: scope.ProjectID.UUID.String(), + SourceScheme: database.ParameterSourceSchemeData, + SourceValue: "nop", + DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable, + }) + require.NoError(t, err) + + computed, err := parameter.Compute(context.Background(), db, scope, nil) + require.NoError(t, err) + require.Len(t, computed, 1) + require.Equal(t, false, computed[0].DefaultSourceValue) + require.Equal(t, value.SourceValue, computed[0].SourceValue) + }) + + t.Run("WorkspaceCannotOverwriteProjectDefault", func(t *testing.T) { + t.Parallel() + db := databasefake.New() + scope := generateScope() + parameterSchema := generateParameter(t, db, parameterOptions{ + ProjectImportJobID: scope.ProjectImportJobID, + }) + _, err := db.InsertParameterValue(context.Background(), database.InsertParameterValueParams{ + ID: uuid.New(), + Name: parameterSchema.Name, + Scope: database.ParameterScopeWorkspace, + ScopeID: scope.WorkspaceID.UUID.String(), + SourceScheme: database.ParameterSourceSchemeData, + SourceValue: "nop", + DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable, + }) + require.NoError(t, err) + + computed, err := parameter.Compute(context.Background(), db, scope, nil) + require.NoError(t, err) + require.Len(t, computed, 1) + require.Equal(t, true, computed[0].DefaultSourceValue) + }) + + t.Run("WorkspaceOverwriteProjectDefault", func(t *testing.T) { + t.Parallel() + db := databasefake.New() + scope := generateScope() + parameterSchema := generateParameter(t, db, parameterOptions{ + AllowOverrideSource: true, + ProjectImportJobID: scope.ProjectImportJobID, + }) + _, err := db.InsertParameterValue(context.Background(), database.InsertParameterValueParams{ + ID: uuid.New(), + Name: parameterSchema.Name, + Scope: database.ParameterScopeWorkspace, + ScopeID: scope.WorkspaceID.UUID.String(), + SourceScheme: database.ParameterSourceSchemeData, + SourceValue: "nop", + DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable, + }) + require.NoError(t, err) + + computed, err := parameter.Compute(context.Background(), db, scope, nil) + require.NoError(t, err) + require.Len(t, computed, 1) + require.Equal(t, false, computed[0].DefaultSourceValue) + }) +} From 94eb484655a33d5e5d6899886330e84c83eae9c3 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 10 Feb 2022 20:05:31 +0000 Subject: [PATCH 03/20] Refactor parameters to enable schema matching --- cli/clitest/clitest.go | 19 ++ cli/projectcreate.go | 93 ++++++++-- cli/projectcreate_test.go | 42 +++++ cli/projects.go | 5 +- coderd/coderd.go | 3 +- coderd/parameter/compute_test.go | 18 ++ coderd/parameter/parameter.go | 199 -------------------- coderd/parameter/parameter_test.go | 242 ------------------------- coderd/parameters.go | 91 +++------- coderd/projects_test.go | 2 - coderd/provisionerdaemons.go | 47 ++--- coderd/provisionerjobs.go | 65 +++++-- coderd/provisionerjobs_test.go | 79 +++++++- codersdk/projects_test.go | 1 - codersdk/provisioners.go | 18 +- database/databasefake/databasefake.go | 2 - database/dump.sql | 9 +- database/migrations/000004_jobs.up.sql | 36 ++-- database/models.go | 5 +- database/query.sql | 9 +- database/query.sql.go | 25 +-- go.mod | 1 + go.sum | 2 + provisioner/terraform/parse.go | 2 +- provisioner/terraform/parse_test.go | 9 +- 25 files changed, 390 insertions(+), 634 deletions(-) create mode 100644 cli/projectcreate_test.go delete mode 100644 coderd/parameter/parameter.go delete mode 100644 coderd/parameter/parameter_test.go diff --git a/cli/clitest/clitest.go b/cli/clitest/clitest.go index 7166843e93dac..dc390485b0eff 100644 --- a/cli/clitest/clitest.go +++ b/cli/clitest/clitest.go @@ -2,13 +2,18 @@ package clitest import ( "bufio" + "context" "io" "testing" "github.com/spf13/cobra" + "github.com/stretchr/testify/require" "github.com/coder/coder/cli" "github.com/coder/coder/cli/config" + "github.com/coder/coder/coderd" + "github.com/coder/coder/coderd/coderdtest" + "github.com/coder/coder/codersdk" ) func New(t *testing.T, args ...string) (*cobra.Command, config.Root) { @@ -19,6 +24,20 @@ func New(t *testing.T, args ...string) (*cobra.Command, config.Root) { return cmd, root } +func CreateInitialUser(t *testing.T, client *codersdk.Client, root config.Root) coderd.CreateInitialUserRequest { + user := coderdtest.CreateInitialUser(t, client) + resp, err := client.LoginWithPassword(context.Background(), coderd.LoginWithPasswordRequest{ + Email: user.Email, + Password: user.Password, + }) + require.NoError(t, err) + err = root.Session().Write(resp.SessionToken) + require.NoError(t, err) + err = root.URL().Write(client.URL.String()) + require.NoError(t, err) + return user +} + func StdoutLogs(t *testing.T) io.Writer { reader, writer := io.Pipe() scanner := bufio.NewScanner(reader) diff --git a/cli/projectcreate.go b/cli/projectcreate.go index 4cde2024053a4..48b0ea3040548 100644 --- a/cli/projectcreate.go +++ b/cli/projectcreate.go @@ -21,7 +21,10 @@ import ( ) func projectCreate() *cobra.Command { - return &cobra.Command{ + var ( + directory string + ) + cmd := &cobra.Command{ Use: "create", Short: "Create a project from the current directory", RunE: func(cmd *cobra.Command, args []string) error { @@ -33,23 +36,17 @@ func projectCreate() *cobra.Command { if err != nil { return err } - - workingDir, err := os.Getwd() - if err != nil { - return err - } - _, err = runPrompt(cmd, &promptui.Prompt{ Default: "y", IsConfirm: true, - Label: fmt.Sprintf("Set up %s in your organization?", color.New(color.FgHiCyan).Sprintf("%q", workingDir)), + Label: fmt.Sprintf("Set up %s in your organization?", color.New(color.FgHiCyan).Sprintf("%q", directory)), }) if err != nil { return err } name, err := runPrompt(cmd, &promptui.Prompt{ - Default: filepath.Base(workingDir), + Default: filepath.Base(directory), Label: "What's your project's name?", Validate: func(s string) error { _, err = client.Project(cmd.Context(), organization.Name, s) @@ -63,12 +60,12 @@ func projectCreate() *cobra.Command { return err } - spin := spinner.New(spinner.CharSets[0], 50*time.Millisecond) + spin := spinner.New(spinner.CharSets[0], 25*time.Millisecond) spin.Suffix = " Uploading current directory..." spin.Start() defer spin.Stop() - bytes, err := tarDirectory(workingDir) + bytes, err := tarDirectory(directory) if err != nil { return err } @@ -84,6 +81,12 @@ func projectCreate() *cobra.Command { Provisioner: database.ProvisionerTypeTerraform, // SkipResources on first import to detect variables defined by the project. SkipResources: true, + // ParameterValues: []coderd.CreateParameterValueRequest{{ + // Name: "aws_access_key", + // SourceValue: "tomato", + // SourceScheme: database.ParameterSourceSchemeData, + // DestinationScheme: database.ParameterDestinationSchemeProvisionerVariable, + // }}, }) if err != nil { return err @@ -102,19 +105,85 @@ func projectCreate() *cobra.Command { _, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s %s\n", color.HiGreenString("[parse]"), log.Output) } + _, _ = fmt.Fprintf(cmd.OutOrStdout(), "Parsed project source... displaying parameters:") + schemas, err := client.ProvisionerJobParameterSchemas(cmd.Context(), organization.Name, job.ID) if err != nil { return err } + values, err := client.ProvisionerJobParameterValues(cmd.Context(), organization.Name, job.ID) + if err != nil { + return err + } + valueBySchemaID := map[string]coderd.ComputedParameterValue{} + for _, value := range values { + valueBySchemaID[value.SchemaID.String()] = value + } + for _, schema := range schemas { - fmt.Printf("Schema: %+v\n", schema) + if value, ok := valueBySchemaID[schema.ID.String()]; ok { + fmt.Printf("Value for: %s %s\n", value.Name, value.SourceValue) + continue + } + fmt.Printf("No value for: %s\n", schema.Name) } + // schemas, err := client.ProvisionerJobParameterSchemas(cmd.Context(), organization.Name, job.ID) + // if err != nil { + // return err + // } + // _, _ = fmt.Fprintf(cmd.OutOrStdout(), "\n %s\n\n", color.HiBlackString("Parameters")) + + // for _, param := range params { + // if param.Value == nil { + // _, _ = fmt.Fprintf(cmd.OutOrStdout(), " %s = must be set\n", color.HiRedString(param.Schema.Name)) + // continue + // } + // value := param.Value.DestinationValue + // if !param.Schema.RedisplayValue { + // value = "" + // } + // output := fmt.Sprintf(" %s = %s", color.HiGreenString(param.Value.SourceValue), color.CyanString(value)) + // param.Value.DefaultSourceValue = false + // param.Value.Scope = database.ParameterScopeOrganization + // param.Value.ScopeID = organization.ID + // if param.Value.DefaultSourceValue { + // output += " (default value)" + // } else { + // output += fmt.Sprintf(" (inherited from %s)", param.Value.Scope) + // } + // root := treeprint.NewWithRoot(output) + // root.AddNode(color.HiBlackString("Description") + "\n" + param.Schema.Description) + // fmt.Fprintln(cmd.OutOrStdout(), strings.Join(strings.Split(root.String(), "\n"), "\n ")) + // } + + // for _, param := range params { + // if param.Value != nil { + // continue + // } + + // value, err := runPrompt(cmd, &promptui.Prompt{ + // Label: "Specify value for " + color.HiCyanString(param.Schema.Name), + // Validate: func(s string) error { + // // param.Schema.Vali + // return nil + // }, + // }) + // if err != nil { + // continue + // } + // fmt.Printf(": %s\n", value) + // } + _, _ = fmt.Fprintf(cmd.OutOrStdout(), "Create project %q!\n", name) return nil }, } + currentDirectory, _ := os.Getwd() + cmd.Flags().StringVarP(&directory, "directory", "d", currentDirectory, "Specify the directory to create from") + + return cmd } func tarDirectory(directory string) ([]byte, error) { diff --git a/cli/projectcreate_test.go b/cli/projectcreate_test.go new file mode 100644 index 0000000000000..1d4a83288ddbf --- /dev/null +++ b/cli/projectcreate_test.go @@ -0,0 +1,42 @@ +package cli_test + +import ( + "testing" + + "github.com/Netflix/go-expect" + "github.com/coder/coder/cli/clitest" + "github.com/coder/coder/coderd/coderdtest" + "github.com/stretchr/testify/require" +) + +func TestProjectCreate(t *testing.T) { + t.Parallel() + t.Run("InitialUserTTY", func(t *testing.T) { + t.Parallel() + console, err := expect.NewConsole(expect.WithStdout(clitest.StdoutLogs(t))) + require.NoError(t, err) + client := coderdtest.New(t) + directory := t.TempDir() + cmd, root := clitest.New(t, "projects", "create", "--directory", directory) + _ = clitest.CreateInitialUser(t, client, root) + cmd.SetIn(console.Tty()) + cmd.SetOut(console.Tty()) + go func() { + err := cmd.Execute() + require.NoError(t, err) + }() + + matches := []string{ + "organization?", "y", + "name?", "", + } + for i := 0; i < len(matches); i += 2 { + match := matches[i] + value := matches[i+1] + _, err = console.ExpectString(match) + require.NoError(t, err) + _, err = console.SendLine(value) + require.NoError(t, err) + } + }) +} diff --git a/cli/projects.go b/cli/projects.go index 07d68c0155967..e0963459a6ed6 100644 --- a/cli/projects.go +++ b/cli/projects.go @@ -7,8 +7,9 @@ import ( func projects() *cobra.Command { cmd := &cobra.Command{ - Use: "projects", - Long: "Testing something", + Use: "projects", + Aliases: []string{"project"}, + Long: "Testing something", Example: ` - Create a project for developers to create workspaces diff --git a/coderd/coderd.go b/coderd/coderd.go index f0df2fdb6d119..3bd7ff54404de 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -121,7 +121,8 @@ func New(options *Options) http.Handler { r.Route("/{provisionerjob}", func(r chi.Router) { r.Use(httpmw.ExtractProvisionerJobParam(options.Database)) r.Get("/", api.provisionerJobByOrganization) - r.Get("/parameters", api.provisionerJobParametersByID) + r.Get("/schemas", api.provisionerJobParameterSchemasByID) + r.Get("/computed", api.provisionerJobComputedParametersByID) r.Get("/logs", api.provisionerJobLogsByID) }) }) diff --git a/coderd/parameter/compute_test.go b/coderd/parameter/compute_test.go index c616c48f815e4..660be359975a5 100644 --- a/coderd/parameter/compute_test.go +++ b/coderd/parameter/compute_test.go @@ -201,4 +201,22 @@ func TestCompute(t *testing.T) { require.Len(t, computed, 1) require.Equal(t, false, computed[0].DefaultSourceValue) }) + + t.Run("HideRedisplay", func(t *testing.T) { + t.Parallel() + db := databasefake.New() + scope := generateScope() + _ = generateParameter(t, db, parameterOptions{ + ProjectImportJobID: scope.ProjectImportJobID, + DefaultDestinationScheme: database.ParameterDestinationSchemeProvisionerVariable, + }) + computed, err := parameter.Compute(context.Background(), db, scope, ¶meter.ComputeOptions{ + HideRedisplayValues: true, + }) + require.NoError(t, err) + require.Len(t, computed, 1) + computedValue := computed[0] + require.True(t, computedValue.DefaultSourceValue) + require.Equal(t, computedValue.SourceValue, "") + }) } diff --git a/coderd/parameter/parameter.go b/coderd/parameter/parameter.go deleted file mode 100644 index d8c7fe73778fc..0000000000000 --- a/coderd/parameter/parameter.go +++ /dev/null @@ -1,199 +0,0 @@ -package parameter - -import ( - "context" - "database/sql" - "errors" - - "github.com/google/uuid" - "golang.org/x/xerrors" - - "github.com/coder/coder/database" -) - -// Scope targets identifiers to pull parameters from. -type Scope struct { - ImportJobID uuid.UUID - OrganizationID string - UserID string - ProjectID uuid.NullUUID - WorkspaceID uuid.NullUUID -} - -type Value struct { - DestinationScheme database.ParameterDestinationScheme - Name string - Value string - // Default is whether the default value from the schema - // was consumed. - Default bool - Scope database.ParameterScope - ScopeID string -} - -type Computed struct { - Schema database.ParameterSchema - // Value is nil when no value was computed for the schema. - Value *Value -} - -// 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, additional ...database.ParameterValue) ([]Computed, error) { - compute := &compute{ - db: db, - parametersByName: map[string]Computed{}, - } - - // All parameters for the import job ID! - parameterSchemas, err := db.GetParameterSchemasByJobID(ctx, scope.ImportJobID) - if errors.Is(err, sql.ErrNoRows) { - err = nil - } - if err != nil { - return nil, xerrors.Errorf("get project parameters: %w", err) - } - for _, parameterSchema := range parameterSchemas { - compute.parametersByName[parameterSchema.Name] = Computed{ - Schema: parameterSchema, - } - } - - // Organization parameters come first! - err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{ - Scope: database.ParameterScopeOrganization, - ScopeID: scope.OrganizationID, - }) - if err != nil { - return nil, err - } - - // Default project parameter values come second! - for _, parameterSchema := range parameterSchemas { - if !parameterSchema.DefaultSourceValue.Valid { - continue - } - if !parameterSchema.DefaultDestinationValue.Valid { - continue - } - - switch parameterSchema.DefaultSourceScheme { - case database.ParameterSourceSchemeData: - compute.parametersByName[parameterSchema.Name] = Computed{ - Value: &Value{ - DestinationScheme: parameterSchema.DefaultDestinationScheme, - Name: parameterSchema.DefaultDestinationValue.String, - Value: parameterSchema.DefaultSourceValue.String, - Default: true, - Scope: database.ParameterScopeProject, - ScopeID: scope.ProjectID.UUID.String(), - }, - Schema: parameterSchema, - } - default: - return nil, xerrors.Errorf("unsupported source scheme for project version parameter %q: %q", parameterSchema.Name, string(parameterSchema.DefaultSourceScheme)) - } - } - - 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 - } - } - - // User parameters come fourth! - err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{ - Scope: database.ParameterScopeUser, - ScopeID: scope.UserID, - }) - if err != nil { - return nil, err - } - - if scope.WorkspaceID.Valid { - // Workspace parameters come last! - err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{ - Scope: database.ParameterScopeWorkspace, - ScopeID: scope.WorkspaceID.UUID.String(), - }) - if err != nil { - return nil, err - } - } - - for _, parameterValue := range additional { - err = compute.injectSingle(parameterValue) - if err != nil { - return nil, xerrors.Errorf("inject %q: %w", parameterValue.Name, err) - } - } - - values := make([]Computed, 0, len(compute.parametersByName)) - for _, value := range compute.parametersByName { - values = append(values, value) - } - return values, nil -} - -type compute struct { - db database.Store - parametersByName map[string]Computed -} - -// Validates and computes the value for parameters; setting the value on "parameterByName". -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 - } - if err != nil { - return xerrors.Errorf("get %s parameters: %w", scopeParams.Scope, err) - } - - for _, scopedParameter := range scopedParameters { - 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 { - computed, hasComputed := c.parametersByName[scopedParameter.Name] - if !hasComputed { - // Don't inject parameters that aren't defined by the project. - return nil - } - - if computed.Value != nil { - // 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 !computed.Schema.AllowOverrideSource && scopedParameter.Scope != database.ParameterScopeProject { - return nil - } - } - - switch scopedParameter.SourceScheme { - case database.ParameterSourceSchemeData: - computed.Value = &Value{ - DestinationScheme: scopedParameter.DestinationScheme, - Name: scopedParameter.SourceValue, - Value: scopedParameter.DestinationValue, - Scope: scopedParameter.Scope, - ScopeID: scopedParameter.ScopeID, - Default: false, - } - c.parametersByName[scopedParameter.Name] = computed - default: - return xerrors.Errorf("unsupported source scheme: %q", string(computed.Schema.DefaultSourceScheme)) - } - return nil -} diff --git a/coderd/parameter/parameter_test.go b/coderd/parameter/parameter_test.go deleted file mode 100644 index aa064683c64f3..0000000000000 --- a/coderd/parameter/parameter_test.go +++ /dev/null @@ -1,242 +0,0 @@ -package parameter_test - -import ( - "context" - "database/sql" - "testing" - - "github.com/google/uuid" - "github.com/stretchr/testify/require" - - "github.com/coder/coder/coderd/parameter" - "github.com/coder/coder/cryptorand" - "github.com/coder/coder/database" - "github.com/coder/coder/database/databasefake" -) - -func TestCompute(t *testing.T) { - t.Parallel() - generateScope := func() parameter.Scope { - return parameter.Scope{ - ImportJobID: uuid.New(), - OrganizationID: uuid.NewString(), - ProjectID: uuid.NullUUID{ - UUID: uuid.New(), - Valid: true, - }, - WorkspaceID: uuid.NullUUID{ - UUID: uuid.New(), - Valid: true, - }, - UserID: uuid.NewString(), - } - } - type parameterOptions struct { - AllowOverrideSource bool - AllowOverrideDestination bool - DefaultDestinationScheme database.ParameterDestinationScheme - ImportJobID uuid.UUID - } - generateParameter := func(t *testing.T, db database.Store, opts parameterOptions) database.ParameterSchema { - if opts.DefaultDestinationScheme == "" { - opts.DefaultDestinationScheme = database.ParameterDestinationSchemeEnvironmentVariable - } - name, err := cryptorand.String(8) - require.NoError(t, err) - sourceValue, err := cryptorand.String(8) - require.NoError(t, err) - destinationValue, err := cryptorand.String(8) - require.NoError(t, err) - param, err := db.InsertParameterSchema(context.Background(), database.InsertParameterSchemaParams{ - ID: uuid.New(), - Name: name, - JobID: opts.ImportJobID, - DefaultSourceScheme: database.ParameterSourceSchemeData, - DefaultSourceValue: sql.NullString{ - String: sourceValue, - Valid: true, - }, - DefaultDestinationValue: sql.NullString{ - String: destinationValue, - Valid: true, - }, - AllowOverrideSource: opts.AllowOverrideSource, - AllowOverrideDestination: opts.AllowOverrideDestination, - DefaultDestinationScheme: opts.DefaultDestinationScheme, - }) - require.NoError(t, err) - return param - } - - t.Run("NoValue", func(t *testing.T) { - t.Parallel() - db := databasefake.New() - scope := generateScope() - parameterSchema, err := db.InsertParameterSchema(context.Background(), database.InsertParameterSchemaParams{ - ID: uuid.New(), - JobID: scope.ImportJobID, - Name: "hey", - }) - require.NoError(t, err) - computed, err := parameter.Compute(context.Background(), db, scope) - require.NoError(t, err) - require.Equal(t, parameterSchema.ID.String(), computed[0].Schema.ID.String()) - require.Nil(t, computed[0].Value) - }) - - t.Run("UseDefaultProjectValue", func(t *testing.T) { - t.Parallel() - db := databasefake.New() - scope := generateScope() - parameterSchema := generateParameter(t, db, parameterOptions{ - ImportJobID: scope.ImportJobID, - DefaultDestinationScheme: database.ParameterDestinationSchemeProvisionerVariable, - }) - computed, err := parameter.Compute(context.Background(), db, scope) - require.NoError(t, err) - require.Len(t, computed, 1) - computedValue := computed[0] - require.True(t, computedValue.Value.Default) - require.Equal(t, database.ParameterScopeProject, computedValue.Value.Scope) - require.Equal(t, scope.ProjectID.UUID.String(), computedValue.Value.ScopeID) - require.Equal(t, computedValue.Value.Name, parameterSchema.DefaultDestinationValue.String) - require.Equal(t, computedValue.Value.DestinationScheme, database.ParameterDestinationSchemeProvisionerVariable) - require.Equal(t, computedValue.Value.Value, parameterSchema.DefaultSourceValue.String) - }) - - t.Run("OverrideOrganizationWithProjectDefault", func(t *testing.T) { - t.Parallel() - db := databasefake.New() - scope := generateScope() - parameterSchema := generateParameter(t, db, parameterOptions{ - ImportJobID: scope.ImportJobID, - }) - _, err := db.InsertParameterValue(context.Background(), database.InsertParameterValueParams{ - ID: uuid.New(), - Name: parameterSchema.Name, - Scope: database.ParameterScopeOrganization, - ScopeID: scope.OrganizationID, - SourceScheme: database.ParameterSourceSchemeData, - SourceValue: "nop", - DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable, - DestinationValue: "organizationvalue", - }) - require.NoError(t, err) - - computed, err := parameter.Compute(context.Background(), db, scope) - require.NoError(t, err) - require.Len(t, computed, 1) - require.Equal(t, true, computed[0].Value.Default) - require.Equal(t, parameterSchema.DefaultSourceValue.String, computed[0].Value.Value) - }) - - t.Run("ProjectOverridesProjectDefault", func(t *testing.T) { - t.Parallel() - db := databasefake.New() - scope := generateScope() - parameterSchema := generateParameter(t, db, parameterOptions{ - ImportJobID: scope.ImportJobID, - }) - value, err := db.InsertParameterValue(context.Background(), database.InsertParameterValueParams{ - ID: uuid.New(), - Name: parameterSchema.Name, - Scope: database.ParameterScopeProject, - ScopeID: scope.ProjectID.UUID.String(), - SourceScheme: database.ParameterSourceSchemeData, - SourceValue: "nop", - DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable, - DestinationValue: "projectvalue", - }) - require.NoError(t, err) - - computed, err := parameter.Compute(context.Background(), db, scope) - require.NoError(t, err) - require.Len(t, computed, 1) - require.Equal(t, false, computed[0].Value.Default) - require.Equal(t, value.DestinationValue, computed[0].Value.Value) - }) - - t.Run("WorkspaceCannotOverwriteProjectDefault", func(t *testing.T) { - t.Parallel() - db := databasefake.New() - scope := generateScope() - parameterSchema := generateParameter(t, db, parameterOptions{ - ImportJobID: scope.ImportJobID, - }) - _, err := db.InsertParameterValue(context.Background(), database.InsertParameterValueParams{ - ID: uuid.New(), - Name: parameterSchema.Name, - Scope: database.ParameterScopeWorkspace, - ScopeID: scope.WorkspaceID.UUID.String(), - SourceScheme: database.ParameterSourceSchemeData, - SourceValue: "nop", - DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable, - DestinationValue: "projectvalue", - }) - require.NoError(t, err) - - computed, err := parameter.Compute(context.Background(), db, scope) - require.NoError(t, err) - require.Len(t, computed, 1) - require.Equal(t, true, computed[0].Value.Default) - }) - - t.Run("WorkspaceOverwriteProjectDefault", func(t *testing.T) { - t.Parallel() - db := databasefake.New() - scope := generateScope() - parameterSchema := generateParameter(t, db, parameterOptions{ - AllowOverrideSource: true, - ImportJobID: scope.ImportJobID, - }) - _, err := db.InsertParameterValue(context.Background(), database.InsertParameterValueParams{ - ID: uuid.New(), - Name: parameterSchema.Name, - Scope: database.ParameterScopeWorkspace, - ScopeID: scope.WorkspaceID.UUID.String(), - SourceScheme: database.ParameterSourceSchemeData, - SourceValue: "nop", - DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable, - DestinationValue: "projectvalue", - }) - require.NoError(t, err) - - computed, err := parameter.Compute(context.Background(), db, scope) - require.NoError(t, err) - require.Len(t, computed, 1) - require.Equal(t, false, computed[0].Value.Default) - }) - - t.Run("AdditionalOverwriteWorkspace", func(t *testing.T) { - t.Parallel() - db := databasefake.New() - scope := generateScope() - parameterSchema := generateParameter(t, db, parameterOptions{ - AllowOverrideSource: true, - ImportJobID: scope.ImportJobID, - }) - _, err := db.InsertParameterValue(context.Background(), database.InsertParameterValueParams{ - ID: uuid.New(), - Name: parameterSchema.Name, - Scope: database.ParameterScopeWorkspace, - ScopeID: scope.WorkspaceID.UUID.String(), - SourceScheme: database.ParameterSourceSchemeData, - SourceValue: "nop", - DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable, - DestinationValue: "projectvalue", - }) - require.NoError(t, err) - - computed, err := parameter.Compute(context.Background(), db, scope, database.ParameterValue{ - Name: parameterSchema.Name, - Scope: database.ParameterScopeUser, - SourceScheme: database.ParameterSourceSchemeData, - SourceValue: "nop", - DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable, - DestinationValue: "testing", - }) - require.NoError(t, err) - require.Len(t, computed, 1) - require.Equal(t, "testing", computed[0].Value.Value) - }) -} diff --git a/coderd/parameters.go b/coderd/parameters.go index b7672a22031f9..0df7f112aadda 100644 --- a/coderd/parameters.go +++ b/coderd/parameters.go @@ -5,55 +5,30 @@ import ( "errors" "fmt" "net/http" - "time" "github.com/go-chi/render" "github.com/google/uuid" + "github.com/coder/coder/coderd/parameter" "github.com/coder/coder/database" "github.com/coder/coder/httpapi" ) +// ParameterSchema represents a parameter parsed from project version source. +type ParameterSchema database.ParameterSchema + +// ParameterValue represents a set value for the scope. +type ParameterValue database.ParameterValue + +// ComputedParameterValue represents a computed parameter value. +type ComputedParameterValue parameter.ComputedValue + // CreateParameterValueRequest is used to create a new parameter value for a scope. type CreateParameterValueRequest struct { Name string `json:"name" validate:"required"` SourceValue string `json:"source_value" validate:"required"` SourceScheme database.ParameterSourceScheme `json:"source_scheme" validate:"oneof=data,required"` DestinationScheme database.ParameterDestinationScheme `json:"destination_scheme" validate:"oneof=environment_variable provisioner_variable,required"` - DestinationValue string `json:"destination_value" validate:"required"` -} - -// ParameterSchema represents a parameter parsed from project version source. -type ParameterSchema struct { - ID uuid.UUID `json:"id"` - CreatedAt time.Time `json:"created_at"` - Name string `json:"name"` - Description string `json:"description,omitempty"` - DefaultSourceScheme database.ParameterSourceScheme `json:"default_source_scheme,omitempty"` - DefaultSourceValue string `json:"default_source_value,omitempty"` - AllowOverrideSource bool `json:"allow_override_source"` - DefaultDestinationScheme database.ParameterDestinationScheme `json:"default_destination_scheme,omitempty"` - DefaultDestinationValue string `json:"default_destination_value,omitempty"` - AllowOverrideDestination bool `json:"allow_override_destination"` - DefaultRefresh string `json:"default_refresh"` - RedisplayValue bool `json:"redisplay_value"` - ValidationError string `json:"validation_error,omitempty"` - ValidationCondition string `json:"validation_condition,omitempty"` - ValidationTypeSystem database.ParameterTypeSystem `json:"validation_type_system,omitempty"` - ValidationValueType string `json:"validation_value_type,omitempty"` -} - -// ParameterValue represents a set value for the scope. -type ParameterValue struct { - ID uuid.UUID `json:"id"` - Name string `json:"name"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - Scope database.ParameterScope `json:"scope"` - ScopeID string `json:"scope_id"` - SourceScheme database.ParameterSourceScheme `json:"source_scheme"` - DestinationScheme database.ParameterDestinationScheme `json:"destination_scheme"` - DestinationValue string `json:"destination_value"` } // Abstracts creating parameters into a single request/response format. @@ -74,7 +49,6 @@ func postParameterValueForScope(rw http.ResponseWriter, r *http.Request, db data SourceScheme: createRequest.SourceScheme, SourceValue: createRequest.SourceValue, DestinationScheme: createRequest.DestinationScheme, - DestinationValue: createRequest.DestinationValue, }) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ @@ -112,37 +86,24 @@ func parametersForScope(rw http.ResponseWriter, r *http.Request, db database.Sto render.JSON(rw, r, apiParameterValues) } -func convertParameterSchema(parameter database.ParameterSchema) ParameterSchema { - return ParameterSchema{ - ID: parameter.ID, - CreatedAt: parameter.CreatedAt, - Name: parameter.Name, - Description: parameter.Description, - DefaultSourceScheme: parameter.DefaultSourceScheme, - DefaultSourceValue: parameter.DefaultSourceValue.String, - AllowOverrideSource: parameter.AllowOverrideSource, - DefaultDestinationScheme: parameter.DefaultDestinationScheme, - DefaultDestinationValue: parameter.DefaultDestinationValue.String, - AllowOverrideDestination: parameter.AllowOverrideDestination, - DefaultRefresh: parameter.DefaultRefresh, - RedisplayValue: parameter.RedisplayValue, - ValidationError: parameter.ValidationError, - ValidationCondition: parameter.ValidationCondition, - ValidationTypeSystem: parameter.ValidationTypeSystem, - ValidationValueType: parameter.ValidationValueType, +// Returns parameters for a specific scope. +func computedParametersForScope(rw http.ResponseWriter, r *http.Request, db database.Store, scope parameter.ComputeScope) { + values, err := parameter.Compute(r.Context(), db, scope, ¶meter.ComputeOptions{ + // We *never* want to send the client secret parameter values. + HideRedisplayValues: true, + }) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: fmt.Sprintf("compute values: %s", err), + }) + return } + + render.Status(r, http.StatusOK) + render.JSON(rw, r, values) } func convertParameterValue(parameterValue database.ParameterValue) ParameterValue { - return ParameterValue{ - ID: parameterValue.ID, - Name: parameterValue.Name, - CreatedAt: parameterValue.CreatedAt, - UpdatedAt: parameterValue.UpdatedAt, - Scope: parameterValue.Scope, - ScopeID: parameterValue.ScopeID, - SourceScheme: parameterValue.SourceScheme, - DestinationScheme: parameterValue.DestinationScheme, - DestinationValue: parameterValue.DestinationValue, - } + parameterValue.SourceValue = "" + return ParameterValue(parameterValue) } diff --git a/coderd/projects_test.go b/coderd/projects_test.go index ee11e2b744ad6..42efedb939f83 100644 --- a/coderd/projects_test.go +++ b/coderd/projects_test.go @@ -114,7 +114,6 @@ func TestPostParametersByProject(t *testing.T) { SourceValue: "tomato", SourceScheme: database.ParameterSourceSchemeData, DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable, - DestinationValue: "moo", }) require.NoError(t, err) }) @@ -144,7 +143,6 @@ func TestParametersByProject(t *testing.T) { SourceValue: "source-value", SourceScheme: database.ParameterSourceSchemeData, DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable, - DestinationValue: "destination-value", }) require.NoError(t, err) params, err := client.ProjectParameters(context.Background(), user.Organization, project.Name) diff --git a/coderd/provisionerdaemons.go b/coderd/provisionerdaemons.go index 7d2dbcb005903..1103dec88966c 100644 --- a/coderd/provisionerdaemons.go +++ b/coderd/provisionerdaemons.go @@ -114,9 +114,8 @@ 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"` + SkipParameterSchemas bool `json:"skip_parameter_schemas"` + SkipResources bool `json:"skip_resources"` } // Implementation of the provisioner daemon protobuf server. @@ -211,9 +210,9 @@ func (server *provisionerdServer) AcquireJob(ctx context.Context, _ *proto.Empty } // Compute parameters for the workspace to consume. - parameters, err := parameter.Compute(ctx, server.Database, parameter.Scope{ - ImportJobID: projectVersion.ImportJobID, - OrganizationID: organization.ID, + parameters, err := parameter.Compute(ctx, server.Database, parameter.ComputeScope{ + ProjectImportJobID: projectVersion.ImportJobID, + OrganizationID: organization.ID, ProjectID: uuid.NullUUID{ UUID: project.ID, Valid: true, @@ -223,14 +222,14 @@ func (server *provisionerdServer) AcquireJob(ctx context.Context, _ *proto.Empty UUID: workspace.ID, Valid: true, }, - }) + }, nil) if err != nil { 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 { - converted, err := convertComputedParameter(parameter) + converted, err := convertComputedParameterValue(parameter) if err != nil { return nil, failJob(fmt.Sprintf("convert parameter: %s", err)) } @@ -253,22 +252,22 @@ func (server *provisionerdServer) AcquireJob(ctx context.Context, _ *proto.Empty } // Compute parameters for the workspace to consume. - parameters, err := parameter.Compute(ctx, server.Database, parameter.Scope{ - ImportJobID: job.ID, - OrganizationID: input.OrganizationID, + parameters, err := parameter.Compute(ctx, server.Database, parameter.ComputeScope{ + ProjectImportJobID: job.ID, + OrganizationID: input.OrganizationID, ProjectID: uuid.NullUUID{ UUID: input.ProjectID, Valid: input.ProjectID.String() != uuid.Nil.String(), }, UserID: user.ID, - }, input.AdditionalParameters...) + }, nil) if err != nil { 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 { - converted, err := convertComputedParameter(parameter) + converted, err := convertComputedParameterValue(parameter) if err != nil { return nil, failJob(fmt.Sprintf("convert parameter: %s", err)) } @@ -448,10 +447,7 @@ func (server *provisionerdServer) CompleteJob(ctx context.Context, completed *pr return nil, xerrors.Errorf("convert parameter source scheme: %w", err) } parameterSchema.DefaultSourceScheme = parameterSourceScheme - parameterSchema.DefaultSourceValue = sql.NullString{ - String: protoParameter.DefaultSource.Value, - Valid: protoParameter.DefaultSource.Value != "", - } + parameterSchema.DefaultSourceValue = protoParameter.DefaultSource.Value } // It's possible a parameter doesn't define a default destination! @@ -461,10 +457,6 @@ func (server *provisionerdServer) CompleteJob(ctx context.Context, completed *pr return nil, xerrors.Errorf("convert parameter destination scheme: %w", err) } parameterSchema.DefaultDestinationScheme = parameterDestinationScheme - parameterSchema.DefaultDestinationValue = sql.NullString{ - String: protoParameter.DefaultDestination.Value, - Valid: protoParameter.DefaultDestination.Value != "", - } } parameterSchemas = append(parameterSchemas, parameterSchema) @@ -617,23 +609,20 @@ func convertLogSource(logSource proto.LogSource) (database.LogSource, error) { } } -func convertComputedParameter(param parameter.Computed) (*sdkproto.ParameterValue, error) { - if param.Value == nil { - return nil, xerrors.Errorf("no value for computed parameter: %s", param.Schema.Name) - } +func convertComputedParameterValue(param parameter.ComputedValue) (*sdkproto.ParameterValue, error) { scheme := sdkproto.ParameterDestination_ENVIRONMENT_VARIABLE - switch param.Value.DestinationScheme { + switch param.DestinationScheme { case database.ParameterDestinationSchemeEnvironmentVariable: scheme = sdkproto.ParameterDestination_ENVIRONMENT_VARIABLE case database.ParameterDestinationSchemeProvisionerVariable: scheme = sdkproto.ParameterDestination_PROVISIONER_VARIABLE default: - return nil, xerrors.Errorf("unrecognized destination scheme: %q", param.Value.DestinationScheme) + return nil, xerrors.Errorf("unrecognized destination scheme: %q", param.DestinationScheme) } return &sdkproto.ParameterValue{ DestinationScheme: scheme, - Name: param.Value.Name, - Value: param.Value.Value, + Name: param.Name, + Value: param.SourceValue, }, nil } diff --git a/coderd/provisionerjobs.go b/coderd/provisionerjobs.go index 32e811ac89364..09e38880ae172 100644 --- a/coderd/provisionerjobs.go +++ b/coderd/provisionerjobs.go @@ -45,16 +45,14 @@ type ProvisionerJob struct { WorkerID *uuid.UUID `json:"worker_id,omitempty"` } -type ComputedParameter struct{} - 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"` + ParameterValues []CreateParameterValueRequest `json:"parameter_values"` + SkipParameterSchemas bool `json:"skip_parameter_schemas"` + SkipResources bool `json:"skip_resources"` } func (*api) provisionerJobByOrganization(rw http.ResponseWriter, r *http.Request) { @@ -98,8 +96,29 @@ func (api *api) postProvisionerImportJobByOrganization(rw http.ResponseWriter, r return } + jobID := uuid.New() + for _, parameterValue := range req.ParameterValues { + _, err = api.Database.InsertParameterValue(r.Context(), database.InsertParameterValueParams{ + ID: uuid.New(), + Name: parameterValue.Name, + CreatedAt: database.Now(), + UpdatedAt: database.Now(), + Scope: database.ParameterScopeImportJob, + ScopeID: jobID.String(), + SourceScheme: parameterValue.SourceScheme, + SourceValue: parameterValue.SourceValue, + DestinationScheme: parameterValue.DestinationScheme, + }) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: fmt.Sprintf("insert parameter value: %s", err), + }) + return + } + } + job, err := api.Database.InsertProvisionerJob(r.Context(), database.InsertProvisionerJobParams{ - ID: uuid.New(), + ID: jobID, CreatedAt: database.Now(), UpdatedAt: database.Now(), OrganizationID: organization.ID, @@ -122,8 +141,7 @@ func (api *api) postProvisionerImportJobByOrganization(rw http.ResponseWriter, r } // Return parsed parameter schemas for a job. -func (api *api) provisionerJobParametersByID(rw http.ResponseWriter, r *http.Request) { - apiKey := httpmw.APIKey(r) +func (api *api) provisionerJobParameterSchemasByID(rw http.ResponseWriter, r *http.Request) { job := httpmw.ProvisionerJobParam(r) if convertProvisionerJob(job).Status != ProvisionerJobStatusSucceeded { httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ @@ -132,28 +150,35 @@ func (api *api) provisionerJobParametersByID(rw http.ResponseWriter, r *http.Req return } - parameter.Compute(r.Context(), api.Database, parameter.Scope{ - ImportJobID: job.ID, - OrganizationID: job.OrganizationID, - UserID: apiKey.UserID, - }) - schemas, err := api.Database.GetParameterSchemasByJobID(r.Context(), job.ID) if errors.Is(err, sql.ErrNoRows) { err = nil } if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: fmt.Sprintf("get parameter schemas: %s", err), + Message: fmt.Sprintf("list parameter schemas: %s", err), }) return } - apiSchemas := make([]ParameterSchema, 0, len(schemas)) - for _, parameter := range schemas { - apiSchemas = append(apiSchemas, convertParameterSchema(parameter)) - } + render.Status(r, http.StatusOK) - render.JSON(rw, r, apiSchemas) + render.JSON(rw, r, schemas) +} + +func (api *api) provisionerJobComputedParametersByID(rw http.ResponseWriter, r *http.Request) { + apiKey := httpmw.APIKey(r) + job := httpmw.ProvisionerJobParam(r) + if convertProvisionerJob(job).Status != ProvisionerJobStatusSucceeded { + httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + Message: fmt.Sprintf("Job is in state %q! Must be %q.", convertProvisionerJob(job).Status, ProvisionerJobStatusSucceeded), + }) + return + } + computedParametersForScope(rw, r, api.Database, parameter.ComputeScope{ + ProjectImportJobID: job.ID, + OrganizationID: job.OrganizationID, + UserID: apiKey.UserID, + }) } func convertProvisionerJob(provisionerJob database.ProvisionerJob) ProvisionerJob { diff --git a/coderd/provisionerjobs_test.go b/coderd/provisionerjobs_test.go index e16d1ad993e79..0ea1e1397a7aa 100644 --- a/coderd/provisionerjobs_test.go +++ b/coderd/provisionerjobs_test.go @@ -8,8 +8,10 @@ import ( "github.com/stretchr/testify/require" + "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" ) @@ -51,6 +53,45 @@ func TestPostProvisionerImportJobByOrganization(t *testing.T) { t.Log(log.Output) } }) + + t.Run("CreateWithParameters", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + user := coderdtest.CreateInitialUser(t, client) + _ = coderdtest.NewProvisionerDaemon(t, client) + data, err := echo.Tar(&echo.Responses{ + Parse: []*proto.Parse_Response{{ + Type: &proto.Parse_Response_Complete{ + Complete: &proto.Parse_Complete{ + ParameterSchemas: []*proto.ParameterSchema{{ + Name: "test", + RedisplayValue: true, + }}, + }, + }, + }}, + }) + require.NoError(t, err) + file, err := client.UploadFile(context.Background(), codersdk.ContentTypeTar, data) + require.NoError(t, err) + job, err := client.CreateProjectVersionImportProvisionerJob(context.Background(), user.Organization, coderd.CreateProjectImportJobRequest{ + StorageSource: file.Hash, + StorageMethod: database.ProvisionerStorageMethodFile, + Provisioner: database.ProvisionerTypeEcho, + ParameterValues: []coderd.CreateParameterValueRequest{{ + Name: "test", + SourceValue: "somevalue", + SourceScheme: database.ParameterSourceSchemeData, + DestinationScheme: database.ParameterDestinationSchemeProvisionerVariable, + }}, + SkipResources: true, + }) + require.NoError(t, err) + coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) + values, err := client.ProvisionerJobParameterValues(context.Background(), user.Organization, job.ID) + require.NoError(t, err) + require.Equal(t, "somevalue", values[0].SourceValue) + }) } func TestProvisionerJobParametersByID(t *testing.T) { @@ -60,7 +101,7 @@ func TestProvisionerJobParametersByID(t *testing.T) { client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) - _, err := client.ProvisionerJobParameterSchemas(context.Background(), user.Organization, job.ID) + _, err := client.ProvisionerJobParameterValues(context.Background(), user.Organization, job.ID) var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) @@ -84,8 +125,42 @@ func TestProvisionerJobParametersByID(t *testing.T) { Provision: echo.ProvisionComplete, }) coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) - params, err := client.ProvisionerJobParameterSchemas(context.Background(), user.Organization, job.ID) + params, err := client.ProvisionerJobParameterValues(context.Background(), user.Organization, job.ID) + require.NoError(t, err) + require.Len(t, params, 0) + }) + + t.Run("ListNoRedisplay", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + user := coderdtest.CreateInitialUser(t, client) + _ = coderdtest.NewProvisionerDaemon(t, client) + 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{{ + Name: "example", + DefaultSource: &proto.ParameterSource{ + Scheme: proto.ParameterSource_DATA, + Value: "tomato", + }, + DefaultDestination: &proto.ParameterDestination{ + Scheme: proto.ParameterDestination_PROVISIONER_VARIABLE, + Value: "example", + }, + RedisplayValue: false, + }}, + }, + }, + }}, + Provision: echo.ProvisionComplete, + }) + coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) + params, err := client.ProvisionerJobParameterValues(context.Background(), user.Organization, job.ID) require.NoError(t, err) require.Len(t, params, 1) + require.NotNil(t, params[0]) + require.Equal(t, params[0].SourceValue, "") }) } diff --git a/codersdk/projects_test.go b/codersdk/projects_test.go index 9fd765c7f138d..b280887ec84ae 100644 --- a/codersdk/projects_test.go +++ b/codersdk/projects_test.go @@ -173,7 +173,6 @@ func TestCreateProjectParameter(t *testing.T) { SourceValue: "source-value", SourceScheme: database.ParameterSourceSchemeData, DestinationScheme: database.ParameterDestinationSchemeEnvironmentVariable, - DestinationValue: "destination-value", }) require.NoError(t, err) }) diff --git a/codersdk/provisioners.go b/codersdk/provisioners.go index d9aea0471d90f..ddb84b9b34688 100644 --- a/codersdk/provisioners.go +++ b/codersdk/provisioners.go @@ -152,9 +152,9 @@ func (c *Client) FollowProvisionerJobLogsAfter(ctx context.Context, organization return logs, nil } -// ProvisionerJobParameterSchemas returns project parameters for a version by name. +// ProvisionerJobParameters returns computed project parameters for a job by ID. func (c *Client) ProvisionerJobParameterSchemas(ctx context.Context, organization string, job uuid.UUID) ([]coderd.ParameterSchema, error) { - res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/provisioners/jobs/%s/%s/parameters", organization, job), nil) + res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/provisioners/jobs/%s/%s/schemas", organization, job), nil) if err != nil { return nil, err } @@ -165,3 +165,17 @@ func (c *Client) ProvisionerJobParameterSchemas(ctx context.Context, organizatio var params []coderd.ParameterSchema return params, json.NewDecoder(res.Body).Decode(¶ms) } + +// ProvisionerJobParameterValues returns computed parameters for a provisioner job. +func (c *Client) ProvisionerJobParameterValues(ctx context.Context, organization string, job uuid.UUID) ([]coderd.ComputedParameterValue, error) { + res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/provisioners/jobs/%s/%s/computed", organization, job), nil) + if err != nil { + return nil, err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return nil, readBodyAsError(res) + } + var params []coderd.ComputedParameterValue + return params, json.NewDecoder(res.Body).Decode(¶ms) +} diff --git a/database/databasefake/databasefake.go b/database/databasefake/databasefake.go index 99793bc7409a9..9d53254f7af0f 100644 --- a/database/databasefake/databasefake.go +++ b/database/databasefake/databasefake.go @@ -643,7 +643,6 @@ func (q *fakeQuerier) InsertParameterValue(_ context.Context, arg database.Inser SourceScheme: arg.SourceScheme, SourceValue: arg.SourceValue, DestinationScheme: arg.DestinationScheme, - DestinationValue: arg.DestinationValue, } q.parameterValue = append(q.parameterValue, parameterValue) return parameterValue, nil @@ -719,7 +718,6 @@ func (q *fakeQuerier) InsertParameterSchema(_ context.Context, arg database.Inse DefaultSourceValue: arg.DefaultSourceValue, AllowOverrideSource: arg.AllowOverrideSource, DefaultDestinationScheme: arg.DefaultDestinationScheme, - DefaultDestinationValue: arg.DefaultDestinationValue, AllowOverrideDestination: arg.AllowOverrideDestination, DefaultRefresh: arg.DefaultRefresh, RedisplayValue: arg.RedisplayValue, diff --git a/database/dump.sql b/database/dump.sql index c21c963db56f5..0fde40950ad7e 100644 --- a/database/dump.sql +++ b/database/dump.sql @@ -28,6 +28,7 @@ CREATE TYPE parameter_destination_scheme AS ENUM ( CREATE TYPE parameter_scope AS ENUM ( 'organization', 'project', + 'import_job', 'user', 'workspace' ); @@ -87,7 +88,7 @@ CREATE TABLE api_keys ( ); CREATE TABLE file ( - hash character varying(32) NOT NULL, + hash character varying(64) NOT NULL, created_at timestamp with time zone NOT NULL, created_by text NOT NULL, mimetype character varying(64) NOT NULL, @@ -128,10 +129,9 @@ CREATE TABLE parameter_schema ( 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, + default_source_value text NOT NULL, 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, @@ -150,8 +150,7 @@ CREATE TABLE parameter_value ( scope_id text NOT NULL, source_scheme parameter_source_scheme NOT NULL, source_value text NOT NULL, - destination_scheme parameter_destination_scheme NOT NULL, - destination_value text NOT NULL + destination_scheme parameter_destination_scheme NOT NULL ); CREATE TABLE project ( diff --git a/database/migrations/000004_jobs.up.sql b/database/migrations/000004_jobs.up.sql index d2fd0ecdd94cb..436847e55d1fa 100644 --- a/database/migrations/000004_jobs.up.sql +++ b/database/migrations/000004_jobs.up.sql @@ -58,6 +58,7 @@ CREATE TABLE IF NOT EXISTS provisioner_job_log ( CREATE TYPE parameter_scope AS ENUM ( 'organization', 'project', + 'import_job', 'user', 'workspace' ); @@ -71,22 +72,6 @@ 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, - name varchar(64) NOT NULL, - created_at timestamptz NOT NULL, - updated_at timestamptz NOT NULL, - scope parameter_scope NOT NULL, - scope_id text NOT NULL, - source_scheme parameter_source_scheme NOT NULL, - source_value text NOT NULL, - destination_scheme parameter_destination_scheme NOT NULL, - destination_value text NOT NULL, - -- Prevents duplicates for parameters in the same scope. - UNIQUE(name, scope, scope_id) -); - -- Stores project version parameters parsed on import. -- No secrets are stored here. -- @@ -103,12 +88,10 @@ CREATE TABLE parameter_schema ( name varchar(64) NOT NULL, description varchar(8192) NOT NULL DEFAULT '', default_source_scheme parameter_source_scheme, - default_source_value text, + default_source_value text NOT NULL, -- 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, @@ -121,3 +104,18 @@ CREATE TABLE parameter_schema ( validation_value_type varchar(64) NOT NULL, UNIQUE(job_id, name) ); + +-- Parameters are provided to jobs for provisioning and to workspaces. +CREATE TABLE parameter_value ( + id uuid NOT NULL UNIQUE, + name varchar(64) NOT NULL, + created_at timestamptz NOT NULL, + updated_at timestamptz NOT NULL, + scope parameter_scope NOT NULL, + scope_id text NOT NULL, + source_scheme parameter_source_scheme NOT NULL, + source_value text NOT NULL, + destination_scheme parameter_destination_scheme NOT NULL, + -- Prevents duplicates for parameters in the same scope. + UNIQUE(name, scope, scope_id) +); diff --git a/database/models.go b/database/models.go index 04500cffd0005..ba4b57bbf0ed9 100644 --- a/database/models.go +++ b/database/models.go @@ -97,6 +97,7 @@ type ParameterScope string const ( ParameterScopeOrganization ParameterScope = "organization" ParameterScopeProject ParameterScope = "project" + ParameterScopeImportJob ParameterScope = "import_job" ParameterScopeUser ParameterScope = "user" ParameterScopeWorkspace ParameterScope = "workspace" ) @@ -307,10 +308,9 @@ type ParameterSchema struct { 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"` + DefaultSourceValue string `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"` @@ -330,7 +330,6 @@ type ParameterValue struct { SourceScheme ParameterSourceScheme `db:"source_scheme" json:"source_scheme"` SourceValue string `db:"source_value" json:"source_value"` DestinationScheme ParameterDestinationScheme `db:"destination_scheme" json:"destination_scheme"` - DestinationValue string `db:"destination_value" json:"destination_value"` } type Project struct { diff --git a/database/query.sql b/database/query.sql index 26abf1893f36a..0330df32aba12 100644 --- a/database/query.sql +++ b/database/query.sql @@ -408,11 +408,10 @@ INSERT INTO scope_id, source_scheme, source_value, - destination_scheme, - destination_value + destination_scheme ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING *; + ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING *; -- name: InsertProject :one INSERT INTO @@ -454,7 +453,6 @@ INSERT INTO default_source_value, allow_override_source, default_destination_scheme, - default_destination_value, allow_override_destination, default_refresh, redisplay_value, @@ -480,8 +478,7 @@ VALUES $13, $14, $15, - $16, - $17 + $16 ) RETURNING *; -- name: InsertProvisionerDaemon :one diff --git a/database/query.sql.go b/database/query.sql.go index 8b63ca467b94b..ed3bd76e7649c 100644 --- a/database/query.sql.go +++ b/database/query.sql.go @@ -271,7 +271,7 @@ func (q *sqlQuerier) GetOrganizationsByUserID(ctx context.Context, userID string 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 + id, created_at, job_id, name, description, default_source_scheme, default_source_value, allow_override_source, default_destination_scheme, allow_override_destination, default_refresh, redisplay_value, validation_error, validation_condition, validation_type_system, validation_value_type FROM parameter_schema WHERE @@ -297,7 +297,6 @@ func (q *sqlQuerier) GetParameterSchemasByJobID(ctx context.Context, jobID uuid. &i.DefaultSourceValue, &i.AllowOverrideSource, &i.DefaultDestinationScheme, - &i.DefaultDestinationValue, &i.AllowOverrideDestination, &i.DefaultRefresh, &i.RedisplayValue, @@ -321,7 +320,7 @@ func (q *sqlQuerier) GetParameterSchemasByJobID(ctx context.Context, jobID uuid. const getParameterValuesByScope = `-- name: GetParameterValuesByScope :many SELECT - id, name, created_at, updated_at, scope, scope_id, source_scheme, source_value, destination_scheme, destination_value + id, name, created_at, updated_at, scope, scope_id, source_scheme, source_value, destination_scheme FROM parameter_value WHERE @@ -353,7 +352,6 @@ func (q *sqlQuerier) GetParameterValuesByScope(ctx context.Context, arg GetParam &i.SourceScheme, &i.SourceValue, &i.DestinationScheme, - &i.DestinationValue, ); err != nil { return nil, err } @@ -1378,7 +1376,6 @@ INSERT INTO default_source_value, allow_override_source, default_destination_scheme, - default_destination_value, allow_override_destination, default_refresh, redisplay_value, @@ -1404,9 +1401,8 @@ VALUES $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 + $16 + ) RETURNING id, created_at, job_id, name, description, default_source_scheme, default_source_value, allow_override_source, default_destination_scheme, allow_override_destination, default_refresh, redisplay_value, validation_error, validation_condition, validation_type_system, validation_value_type ` type InsertParameterSchemaParams struct { @@ -1416,10 +1412,9 @@ type InsertParameterSchemaParams struct { 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"` + DefaultSourceValue string `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"` @@ -1440,7 +1435,6 @@ func (q *sqlQuerier) InsertParameterSchema(ctx context.Context, arg InsertParame arg.DefaultSourceValue, arg.AllowOverrideSource, arg.DefaultDestinationScheme, - arg.DefaultDestinationValue, arg.AllowOverrideDestination, arg.DefaultRefresh, arg.RedisplayValue, @@ -1460,7 +1454,6 @@ func (q *sqlQuerier) InsertParameterSchema(ctx context.Context, arg InsertParame &i.DefaultSourceValue, &i.AllowOverrideSource, &i.DefaultDestinationScheme, - &i.DefaultDestinationValue, &i.AllowOverrideDestination, &i.DefaultRefresh, &i.RedisplayValue, @@ -1483,11 +1476,10 @@ INSERT INTO scope_id, source_scheme, source_value, - destination_scheme, - destination_value + destination_scheme ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id, name, created_at, updated_at, scope, scope_id, source_scheme, source_value, destination_scheme, destination_value + ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id, name, created_at, updated_at, scope, scope_id, source_scheme, source_value, destination_scheme ` type InsertParameterValueParams struct { @@ -1500,7 +1492,6 @@ type InsertParameterValueParams struct { SourceScheme ParameterSourceScheme `db:"source_scheme" json:"source_scheme"` SourceValue string `db:"source_value" json:"source_value"` DestinationScheme ParameterDestinationScheme `db:"destination_scheme" json:"destination_scheme"` - DestinationValue string `db:"destination_value" json:"destination_value"` } func (q *sqlQuerier) InsertParameterValue(ctx context.Context, arg InsertParameterValueParams) (ParameterValue, error) { @@ -1514,7 +1505,6 @@ func (q *sqlQuerier) InsertParameterValue(ctx context.Context, arg InsertParamet arg.SourceScheme, arg.SourceValue, arg.DestinationScheme, - arg.DestinationValue, ) var i ParameterValue err := row.Scan( @@ -1527,7 +1517,6 @@ func (q *sqlQuerier) InsertParameterValue(ctx context.Context, arg InsertParamet &i.SourceScheme, &i.SourceValue, &i.DestinationScheme, - &i.DestinationValue, ) return i, err } diff --git a/go.mod b/go.mod index ffd3165824c4a..f84913c114df6 100644 --- a/go.mod +++ b/go.mod @@ -42,6 +42,7 @@ require ( github.com/spf13/cobra v1.3.0 github.com/stretchr/testify v1.7.0 github.com/unrolled/secure v1.0.9 + github.com/xlab/treeprint v1.1.0 go.uber.org/atomic v1.9.0 go.uber.org/goleak v1.1.12 golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 diff --git a/go.sum b/go.sum index d14ed9f0057d8..f39b53bdea85f 100644 --- a/go.sum +++ b/go.sum @@ -1240,6 +1240,8 @@ github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= +github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/provisioner/terraform/parse.go b/provisioner/terraform/parse.go index c0ab765c1bf67..24f4943c5bff5 100644 --- a/provisioner/terraform/parse.go +++ b/provisioner/terraform/parse.go @@ -44,7 +44,7 @@ func convertVariableToParameter(variable *tfconfig.Variable) (*proto.ParameterSc schema := &proto.ParameterSchema{ Name: variable.Name, Description: variable.Description, - RedisplayValue: variable.Sensitive, + RedisplayValue: !variable.Sensitive, ValidationValueType: variable.Type, } diff --git a/provisioner/terraform/parse_test.go b/provisioner/terraform/parse_test.go index f1221de20e705..2a4ce6a8b2c8e 100644 --- a/provisioner/terraform/parse_test.go +++ b/provisioner/terraform/parse_test.go @@ -52,8 +52,9 @@ func TestParse(t *testing.T) { Type: &proto.Parse_Response_Complete{ Complete: &proto.Parse_Complete{ ParameterSchemas: []*proto.ParameterSchema{{ - Name: "A", - Description: "Testing!", + Name: "A", + RedisplayValue: true, + Description: "Testing!", }}, }, }, @@ -69,7 +70,8 @@ func TestParse(t *testing.T) { Type: &proto.Parse_Response_Complete{ Complete: &proto.Parse_Complete{ ParameterSchemas: []*proto.ParameterSchema{{ - Name: "A", + Name: "A", + RedisplayValue: true, DefaultSource: &proto.ParameterSource{ Scheme: proto.ParameterSource_DATA, Value: "\"wow\"", @@ -96,6 +98,7 @@ func TestParse(t *testing.T) { Complete: &proto.Parse_Complete{ ParameterSchemas: []*proto.ParameterSchema{{ Name: "A", + RedisplayValue: true, ValidationCondition: `var.A == "value"`, ValidationTypeSystem: proto.ParameterSchema_HCL, }, From a6ce22db9c657b99322e9f3de348fef1909ff231 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 10 Feb 2022 20:43:13 +0000 Subject: [PATCH 04/20] Refactor provisionerd to dynamically update parameter schemas --- coderd/provisionerdaemons.go | 193 +++++------ coderd/provisionerjobs.go | 16 +- coderd/provisionerjobs_test.go | 6 +- provisionerd/proto/provisionerd.pb.go | 372 +++++++++++---------- provisionerd/proto/provisionerd.proto | 25 +- provisionerd/proto/provisionerd_drpc.pb.go | 70 +--- provisionerd/provisionerd.go | 99 +++--- provisionerd/provisionerd_test.go | 48 +-- 8 files changed, 390 insertions(+), 439 deletions(-) diff --git a/coderd/provisionerdaemons.go b/coderd/provisionerdaemons.go index 1103dec88966c..f84da2ebcb8b8 100644 --- a/coderd/provisionerdaemons.go +++ b/coderd/provisionerdaemons.go @@ -113,9 +113,6 @@ type workspaceProvisionJob struct { type projectVersionImportJob struct { OrganizationID string `json:"organization_id"` ProjectID uuid.UUID `json:"project_id"` - - SkipParameterSchemas bool `json:"skip_parameter_schemas"` - SkipResources bool `json:"skip_resources"` } // Implementation of the provisioner daemon protobuf server. @@ -276,9 +273,7 @@ func (server *provisionerdServer) AcquireJob(ctx context.Context, _ *proto.Empty protoJob.Type = &proto.AcquiredJob_ProjectImport_{ ProjectImport: &proto.AcquiredJob_ProjectImport{ - ParameterValues: protoParameters, - SkipParameterSchemas: input.SkipParameterSchemas, - SkipResources: input.SkipResources, + ParameterValues: protoParameters, }, } } @@ -324,35 +319,89 @@ func (server *provisionerdServer) UpdateJob(stream proto.DRPCProvisionerDaemon_U if err != nil { return xerrors.Errorf("update job: %w", err) } - insertParams := database.InsertProvisionerJobLogsParams{ - JobID: parsedID, - } - for _, log := range update.Logs { - logLevel, err := convertLogLevel(log.Level) + if len(update.Logs) > 0 { + insertParams := database.InsertProvisionerJobLogsParams{ + JobID: parsedID, + } + for _, log := range update.Logs { + logLevel, err := convertLogLevel(log.Level) + if err != nil { + return xerrors.Errorf("convert log level: %w", err) + } + logSource, err := convertLogSource(log.Source) + if err != nil { + return xerrors.Errorf("convert log source: %w", err) + } + insertParams.ID = append(insertParams.ID, uuid.New()) + insertParams.CreatedAt = append(insertParams.CreatedAt, time.UnixMilli(log.CreatedAt)) + insertParams.Level = append(insertParams.Level, logLevel) + insertParams.Source = append(insertParams.Source, logSource) + insertParams.Output = append(insertParams.Output, log.Output) + } + logs, err := server.Database.InsertProvisionerJobLogs(context.Background(), insertParams) if err != nil { - return xerrors.Errorf("convert log level: %w", err) + return xerrors.Errorf("insert job logs: %w", err) } - logSource, err := convertLogSource(log.Source) + data, err := json.Marshal(logs) if err != nil { - return xerrors.Errorf("convert log source: %w", err) + return xerrors.Errorf("marshal job log: %w", err) + } + err = server.Pubsub.Publish(provisionerJobLogsChannel(parsedID), data) + if err != nil { + return xerrors.Errorf("publish job log: %w", err) } - insertParams.ID = append(insertParams.ID, uuid.New()) - insertParams.CreatedAt = append(insertParams.CreatedAt, time.UnixMilli(log.CreatedAt)) - insertParams.Level = append(insertParams.Level, logLevel) - insertParams.Source = append(insertParams.Source, logSource) - insertParams.Output = append(insertParams.Output, log.Output) - } - logs, err := server.Database.InsertProvisionerJobLogs(context.Background(), insertParams) - if err != nil { - return xerrors.Errorf("insert job logs: %w", err) - } - data, err := json.Marshal(logs) - if err != nil { - return xerrors.Errorf("marshal job log: %w", err) } - err = server.Pubsub.Publish(provisionerJobLogsChannel(parsedID), data) - if err != nil { - return xerrors.Errorf("publish job log: %w", err) + + if update.GetProjectImport() != nil { + // Validate that all parameters send from the provisioner daemon + // follow the protocol. + parameterSchemas := make([]database.InsertParameterSchemaParams, 0, len(update.GetProjectImport().ParameterSchemas)) + for _, protoParameter := range update.GetProjectImport().ParameterSchemas { + validationTypeSystem, err := convertValidationTypeSystem(protoParameter.ValidationTypeSystem) + if err != nil { + return xerrors.Errorf("convert validation type system for %q: %w", protoParameter.Name, err) + } + + parameterSchema := database.InsertParameterSchemaParams{ + ID: uuid.New(), + CreatedAt: database.Now(), + JobID: job.ID, + Name: protoParameter.Name, + Description: protoParameter.Description, + RedisplayValue: protoParameter.RedisplayValue, + ValidationError: protoParameter.ValidationError, + ValidationCondition: protoParameter.ValidationCondition, + ValidationValueType: protoParameter.ValidationValueType, + ValidationTypeSystem: validationTypeSystem, + + DefaultSourceScheme: database.ParameterSourceSchemeNone, + DefaultDestinationScheme: database.ParameterDestinationSchemeNone, + + AllowOverrideDestination: protoParameter.AllowOverrideDestination, + AllowOverrideSource: protoParameter.AllowOverrideSource, + } + + // It's possible a parameter doesn't define a default source! + if protoParameter.DefaultSource != nil { + parameterSourceScheme, err := convertParameterSourceScheme(protoParameter.DefaultSource.Scheme) + if err != nil { + return xerrors.Errorf("convert parameter source scheme: %w", err) + } + parameterSchema.DefaultSourceScheme = parameterSourceScheme + parameterSchema.DefaultSourceValue = protoParameter.DefaultSource.Value + } + + // It's possible a parameter doesn't define a default destination! + if protoParameter.DefaultDestination != nil { + parameterDestinationScheme, err := convertParameterDestinationScheme(protoParameter.DefaultDestination.Scheme) + if err != nil { + return xerrors.Errorf("convert parameter destination scheme: %w", err) + } + parameterSchema.DefaultDestinationScheme = parameterDestinationScheme + } + + parameterSchemas = append(parameterSchemas, parameterSchema) + } } } } @@ -412,80 +461,18 @@ func (server *provisionerdServer) CompleteJob(ctx context.Context, completed *pr return nil, xerrors.Errorf("unmarshal job data: %w", err) } - // Validate that all parameters send from the provisioner daemon - // follow the protocol. - 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) - } - - parameterSchema := database.InsertParameterSchemaParams{ - ID: uuid.New(), - CreatedAt: database.Now(), - JobID: job.ID, - Name: protoParameter.Name, - Description: protoParameter.Description, - RedisplayValue: protoParameter.RedisplayValue, - ValidationError: protoParameter.ValidationError, - ValidationCondition: protoParameter.ValidationCondition, - ValidationValueType: protoParameter.ValidationValueType, - ValidationTypeSystem: validationTypeSystem, - - DefaultSourceScheme: database.ParameterSourceSchemeNone, - DefaultDestinationScheme: database.ParameterDestinationSchemeNone, - - AllowOverrideDestination: protoParameter.AllowOverrideDestination, - AllowOverrideSource: protoParameter.AllowOverrideSource, - } - - // It's possible a parameter doesn't define a default source! - if protoParameter.DefaultSource != nil { - parameterSourceScheme, err := convertParameterSourceScheme(protoParameter.DefaultSource.Scheme) - if err != nil { - return nil, xerrors.Errorf("convert parameter source scheme: %w", err) - } - parameterSchema.DefaultSourceScheme = parameterSourceScheme - parameterSchema.DefaultSourceValue = protoParameter.DefaultSource.Value - } - - // It's possible a parameter doesn't define a default destination! - if protoParameter.DefaultDestination != nil { - parameterDestinationScheme, err := convertParameterDestinationScheme(protoParameter.DefaultDestination.Scheme) - if err != nil { - return nil, xerrors.Errorf("convert parameter destination scheme: %w", err) - } - parameterSchema.DefaultDestinationScheme = parameterDestinationScheme - } - - parameterSchemas = append(parameterSchemas, parameterSchema) - } - - // This must occur in a transaction in case of failure. - err = server.Database.InTx(func(db database.Store) error { - err = db.UpdateProvisionerJobWithCompleteByID(ctx, database.UpdateProvisionerJobWithCompleteByIDParams{ - ID: jobID, - UpdatedAt: database.Now(), - CompletedAt: sql.NullTime{ - Time: database.Now(), - Valid: true, - }, - }) - if err != nil { - return xerrors.Errorf("update provisioner job: %w", err) - } - // This could be a bulk-insert operation to improve performance. - // See the "InsertWorkspaceHistoryLogs" query. - for _, parameterSchema := range parameterSchemas { - _, err = db.InsertParameterSchema(ctx, parameterSchema) - if err != nil { - 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)) - return nil + err = server.Database.UpdateProvisionerJobWithCompleteByID(ctx, database.UpdateProvisionerJobWithCompleteByIDParams{ + ID: jobID, + UpdatedAt: database.Now(), + CompletedAt: sql.NullTime{ + Time: database.Now(), + Valid: true, + }, }) + if err != nil { + return nil, xerrors.Errorf("update provisioner job: %w", err) + } + server.Logger.Debug(ctx, "marked import job as completed", slog.F("job_id", jobID)) if err != nil { return nil, xerrors.Errorf("complete job: %w", err) } diff --git a/coderd/provisionerjobs.go b/coderd/provisionerjobs.go index 09e38880ae172..d5a446e0d57ad 100644 --- a/coderd/provisionerjobs.go +++ b/coderd/provisionerjobs.go @@ -46,13 +46,10 @@ type ProvisionerJob struct { } 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"` - - ParameterValues []CreateParameterValueRequest `json:"parameter_values"` - SkipParameterSchemas bool `json:"skip_parameter_schemas"` - SkipResources bool `json:"skip_resources"` + 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"` + ParameterValues []CreateParameterValueRequest `json:"parameter_values"` } func (*api) provisionerJobByOrganization(rw http.ResponseWriter, r *http.Request) { @@ -84,10 +81,7 @@ func (api *api) postProvisionerImportJobByOrganization(rw http.ResponseWriter, r } input, err := json.Marshal(projectVersionImportJob{ - // AdditionalParameters: req.AdditionalParameters, - OrganizationID: organization.ID, - SkipParameterSchemas: req.SkipParameterSchemas, - SkipResources: req.SkipResources, + OrganizationID: organization.ID, }) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ diff --git a/coderd/provisionerjobs_test.go b/coderd/provisionerjobs_test.go index 0ea1e1397a7aa..89f90dbf0c07e 100644 --- a/coderd/provisionerjobs_test.go +++ b/coderd/provisionerjobs_test.go @@ -2,6 +2,7 @@ package coderd_test import ( "context" + "fmt" "net/http" "testing" "time" @@ -70,6 +71,7 @@ func TestPostProvisionerImportJobByOrganization(t *testing.T) { }, }, }}, + Provision: echo.ProvisionComplete, }) require.NoError(t, err) file, err := client.UploadFile(context.Background(), codersdk.ContentTypeTar, data) @@ -84,10 +86,10 @@ func TestPostProvisionerImportJobByOrganization(t *testing.T) { SourceScheme: database.ParameterSourceSchemeData, DestinationScheme: database.ParameterDestinationSchemeProvisionerVariable, }}, - SkipResources: true, }) require.NoError(t, err) - coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) + job = coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) + fmt.Printf("Job %+v\n", job) values, err := client.ProvisionerJobParameterValues(context.Background(), user.Organization, job.ID) require.NoError(t, err) require.Equal(t, "somevalue", values[0].SourceValue) diff --git a/provisionerd/proto/provisionerd.pb.go b/provisionerd/proto/provisionerd.pb.go index d1cd2548b19fb..0322a2f163b05 100644 --- a/provisionerd/proto/provisionerd.pb.go +++ b/provisionerd/proto/provisionerd.pb.go @@ -504,20 +504,19 @@ func (x *Log) GetOutput() string { return "" } -// JobUpdate represents an update to a job. -// There may be no log output, but this message -// should still be sent periodically as a heartbeat. -type JobUpdate struct { +// This message should be sent periodically as a heartbeat. +type UpdateJobRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - JobId string `protobuf:"bytes,1,opt,name=job_id,json=jobId,proto3" json:"job_id,omitempty"` - Logs []*Log `protobuf:"bytes,2,rep,name=logs,proto3" json:"logs,omitempty"` + JobId string `protobuf:"bytes,1,opt,name=job_id,json=jobId,proto3" json:"job_id,omitempty"` + Logs []*Log `protobuf:"bytes,2,rep,name=logs,proto3" json:"logs,omitempty"` + ParameterSchemas []*proto.ParameterSchema `protobuf:"bytes,3,rep,name=parameter_schemas,json=parameterSchemas,proto3" json:"parameter_schemas,omitempty"` } -func (x *JobUpdate) Reset() { - *x = JobUpdate{} +func (x *UpdateJobRequest) Reset() { + *x = UpdateJobRequest{} if protoimpl.UnsafeEnabled { mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -525,13 +524,13 @@ func (x *JobUpdate) Reset() { } } -func (x *JobUpdate) String() string { +func (x *UpdateJobRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*JobUpdate) ProtoMessage() {} +func (*UpdateJobRequest) ProtoMessage() {} -func (x *JobUpdate) ProtoReflect() protoreflect.Message { +func (x *UpdateJobRequest) ProtoReflect() protoreflect.Message { mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -543,25 +542,81 @@ func (x *JobUpdate) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use JobUpdate.ProtoReflect.Descriptor instead. -func (*JobUpdate) Descriptor() ([]byte, []int) { +// Deprecated: Use UpdateJobRequest.ProtoReflect.Descriptor instead. +func (*UpdateJobRequest) Descriptor() ([]byte, []int) { return file_provisionerd_proto_provisionerd_proto_rawDescGZIP(), []int{6} } -func (x *JobUpdate) GetJobId() string { +func (x *UpdateJobRequest) GetJobId() string { if x != nil { return x.JobId } return "" } -func (x *JobUpdate) GetLogs() []*Log { +func (x *UpdateJobRequest) GetLogs() []*Log { if x != nil { return x.Logs } return nil } +func (x *UpdateJobRequest) GetParameterSchemas() []*proto.ParameterSchema { + if x != nil { + return x.ParameterSchemas + } + return nil +} + +type UpdateJobResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // If parameter schemas are sent, the job will respond + // with resolved parameter values. + ParameterValues []*proto.ParameterValue `protobuf:"bytes,1,rep,name=parameter_values,json=parameterValues,proto3" json:"parameter_values,omitempty"` +} + +func (x *UpdateJobResponse) Reset() { + *x = UpdateJobResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateJobResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateJobResponse) ProtoMessage() {} + +func (x *UpdateJobResponse) ProtoReflect() protoreflect.Message { + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateJobResponse.ProtoReflect.Descriptor instead. +func (*UpdateJobResponse) Descriptor() ([]byte, []int) { + return file_provisionerd_proto_provisionerd_proto_rawDescGZIP(), []int{7} +} + +func (x *UpdateJobResponse) GetParameterValues() []*proto.ParameterValue { + if x != nil { + return x.ParameterValues + } + return nil +} + type AcquiredJob_WorkspaceProvision struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -576,7 +631,7 @@ type AcquiredJob_WorkspaceProvision struct { func (x *AcquiredJob_WorkspaceProvision) Reset() { *x = AcquiredJob_WorkspaceProvision{} if protoimpl.UnsafeEnabled { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[7] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -589,7 +644,7 @@ func (x *AcquiredJob_WorkspaceProvision) String() string { func (*AcquiredJob_WorkspaceProvision) ProtoMessage() {} func (x *AcquiredJob_WorkspaceProvision) ProtoReflect() protoreflect.Message { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[7] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -637,16 +692,12 @@ type AcquiredJob_ProjectImport struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - - 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() { *x = AcquiredJob_ProjectImport{} if protoimpl.UnsafeEnabled { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[8] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -659,7 +710,7 @@ func (x *AcquiredJob_ProjectImport) String() string { func (*AcquiredJob_ProjectImport) ProtoMessage() {} func (x *AcquiredJob_ProjectImport) ProtoReflect() protoreflect.Message { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[8] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -675,27 +726,6 @@ func (*AcquiredJob_ProjectImport) Descriptor() ([]byte, []int) { return file_provisionerd_proto_provisionerd_proto_rawDescGZIP(), []int{1, 1} } -func (x *AcquiredJob_ProjectImport) GetParameterValues() []*proto.ParameterValue { - if x != nil { - return x.ParameterValues - } - return nil -} - -func (x *AcquiredJob_ProjectImport) GetSkipParameterSchemas() bool { - if x != nil { - return x.SkipParameterSchemas - } - return false -} - -func (x *AcquiredJob_ProjectImport) GetSkipResources() bool { - if x != nil { - return x.SkipResources - } - return false -} - type CompletedJob_WorkspaceProvision struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -708,7 +738,7 @@ type CompletedJob_WorkspaceProvision struct { func (x *CompletedJob_WorkspaceProvision) Reset() { *x = CompletedJob_WorkspaceProvision{} if protoimpl.UnsafeEnabled { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[9] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -721,7 +751,7 @@ func (x *CompletedJob_WorkspaceProvision) String() string { func (*CompletedJob_WorkspaceProvision) ProtoMessage() {} func (x *CompletedJob_WorkspaceProvision) ProtoReflect() protoreflect.Message { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[9] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -756,15 +786,14 @@ type CompletedJob_ProjectImport struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ParameterSchemas []*proto.ParameterSchema `protobuf:"bytes,1,rep,name=parameter_schemas,json=parameterSchemas,proto3" json:"parameter_schemas,omitempty"` - StartResources []*proto.Resource `protobuf:"bytes,2,rep,name=start_resources,json=startResources,proto3" json:"start_resources,omitempty"` - StopResources []*proto.Resource `protobuf:"bytes,3,rep,name=stop_resources,json=stopResources,proto3" json:"stop_resources,omitempty"` + StartResources []*proto.Resource `protobuf:"bytes,1,rep,name=start_resources,json=startResources,proto3" json:"start_resources,omitempty"` + StopResources []*proto.Resource `protobuf:"bytes,2,rep,name=stop_resources,json=stopResources,proto3" json:"stop_resources,omitempty"` } func (x *CompletedJob_ProjectImport) Reset() { *x = CompletedJob_ProjectImport{} if protoimpl.UnsafeEnabled { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[10] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -777,7 +806,7 @@ func (x *CompletedJob_ProjectImport) String() string { func (*CompletedJob_ProjectImport) ProtoMessage() {} func (x *CompletedJob_ProjectImport) ProtoReflect() protoreflect.Message { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[10] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -793,13 +822,6 @@ func (*CompletedJob_ProjectImport) Descriptor() ([]byte, []int) { return file_provisionerd_proto_provisionerd_proto_rawDescGZIP(), []int{4, 1} } -func (x *CompletedJob_ProjectImport) GetParameterSchemas() []*proto.ParameterSchema { - if x != nil { - return x.ParameterSchemas - } - return nil -} - func (x *CompletedJob_ProjectImport) GetStartResources() []*proto.Resource { if x != nil { return x.StartResources @@ -823,7 +845,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, 0xf8, 0x05, 0x0a, 0x0b, 0x41, 0x63, 0x71, 0x75, 0x69, + 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0xd2, 0x04, 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, @@ -859,91 +881,88 @@ 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, 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, + 0x74, 0x65, 0x1a, 0x0f, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6d, 0x70, + 0x6f, 0x72, 0x74, 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, 0xd3, 0x03, 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, 0x8d, 0x01, 0x0a, 0x0d, 0x50, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x3e, 0x0a, 0x0f, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, + 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, 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, 0x0d, 0x73, 0x74, 0x6f, 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, + 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, 0x9b, + 0x01, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x04, 0x6c, 0x6f, + 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 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, 0x12, 0x49, 0x0a, 0x11, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x73, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x10, 0x70, 0x61, 0x72, 0x61, - 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 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, + 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x22, 0x5b, 0x0a, 0x11, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 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, 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, + 0x9d, 0x02, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x44, + 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x3c, 0x0a, 0x0a, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x4a, 0x6f, 0x62, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, + 0x4a, 0x6f, 0x62, 0x12, 0x4c, 0x0a, 0x09, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, + 0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 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, @@ -970,7 +989,7 @@ func file_provisionerd_proto_provisionerd_proto_rawDescGZIP() []byte { } var file_provisionerd_proto_provisionerd_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_provisionerd_proto_provisionerd_proto_msgTypes = make([]protoimpl.MessageInfo, 11) +var file_provisionerd_proto_provisionerd_proto_msgTypes = make([]protoimpl.MessageInfo, 12) var file_provisionerd_proto_provisionerd_proto_goTypes = []interface{}{ (LogSource)(0), // 0: provisionerd.LogSource (*Empty)(nil), // 1: provisionerd.Empty @@ -979,37 +998,38 @@ var file_provisionerd_proto_provisionerd_proto_goTypes = []interface{}{ (*TransitionedResource)(nil), // 4: provisionerd.TransitionedResource (*CompletedJob)(nil), // 5: provisionerd.CompletedJob (*Log)(nil), // 6: provisionerd.Log - (*JobUpdate)(nil), // 7: provisionerd.JobUpdate - (*AcquiredJob_WorkspaceProvision)(nil), // 8: provisionerd.AcquiredJob.WorkspaceProvision - (*AcquiredJob_ProjectImport)(nil), // 9: provisionerd.AcquiredJob.ProjectImport - (*CompletedJob_WorkspaceProvision)(nil), // 10: provisionerd.CompletedJob.WorkspaceProvision - (*CompletedJob_ProjectImport)(nil), // 11: provisionerd.CompletedJob.ProjectImport - (*proto.Resource)(nil), // 12: provisioner.Resource - (proto.LogLevel)(0), // 13: provisioner.LogLevel - (*proto.ParameterValue)(nil), // 14: provisioner.ParameterValue + (*UpdateJobRequest)(nil), // 7: provisionerd.UpdateJobRequest + (*UpdateJobResponse)(nil), // 8: provisionerd.UpdateJobResponse + (*AcquiredJob_WorkspaceProvision)(nil), // 9: provisionerd.AcquiredJob.WorkspaceProvision + (*AcquiredJob_ProjectImport)(nil), // 10: provisionerd.AcquiredJob.ProjectImport + (*CompletedJob_WorkspaceProvision)(nil), // 11: provisionerd.CompletedJob.WorkspaceProvision + (*CompletedJob_ProjectImport)(nil), // 12: provisionerd.CompletedJob.ProjectImport + (*proto.Resource)(nil), // 13: provisioner.Resource + (proto.LogLevel)(0), // 14: provisioner.LogLevel (*proto.ParameterSchema)(nil), // 15: provisioner.ParameterSchema + (*proto.ParameterValue)(nil), // 16: provisioner.ParameterValue } var file_provisionerd_proto_provisionerd_proto_depIdxs = []int32{ - 8, // 0: provisionerd.AcquiredJob.workspace_provision:type_name -> provisionerd.AcquiredJob.WorkspaceProvision - 9, // 1: provisionerd.AcquiredJob.project_import:type_name -> provisionerd.AcquiredJob.ProjectImport - 12, // 2: provisionerd.TransitionedResource.resource:type_name -> provisioner.Resource - 10, // 3: provisionerd.CompletedJob.workspace_provision:type_name -> provisionerd.CompletedJob.WorkspaceProvision - 11, // 4: provisionerd.CompletedJob.project_import:type_name -> provisionerd.CompletedJob.ProjectImport + 9, // 0: provisionerd.AcquiredJob.workspace_provision:type_name -> provisionerd.AcquiredJob.WorkspaceProvision + 10, // 1: provisionerd.AcquiredJob.project_import:type_name -> provisionerd.AcquiredJob.ProjectImport + 13, // 2: provisionerd.TransitionedResource.resource:type_name -> provisioner.Resource + 11, // 3: provisionerd.CompletedJob.workspace_provision:type_name -> provisionerd.CompletedJob.WorkspaceProvision + 12, // 4: provisionerd.CompletedJob.project_import:type_name -> provisionerd.CompletedJob.ProjectImport 0, // 5: provisionerd.Log.source:type_name -> provisionerd.LogSource - 13, // 6: provisionerd.Log.level:type_name -> provisioner.LogLevel - 6, // 7: provisionerd.JobUpdate.logs:type_name -> provisionerd.Log - 14, // 8: provisionerd.AcquiredJob.WorkspaceProvision.parameter_values:type_name -> provisioner.ParameterValue - 14, // 9: provisionerd.AcquiredJob.ProjectImport.parameter_values:type_name -> provisioner.ParameterValue - 12, // 10: provisionerd.CompletedJob.WorkspaceProvision.resources:type_name -> provisioner.Resource - 15, // 11: provisionerd.CompletedJob.ProjectImport.parameter_schemas:type_name -> provisioner.ParameterSchema - 12, // 12: provisionerd.CompletedJob.ProjectImport.start_resources:type_name -> provisioner.Resource - 12, // 13: provisionerd.CompletedJob.ProjectImport.stop_resources:type_name -> provisioner.Resource + 14, // 6: provisionerd.Log.level:type_name -> provisioner.LogLevel + 6, // 7: provisionerd.UpdateJobRequest.logs:type_name -> provisionerd.Log + 15, // 8: provisionerd.UpdateJobRequest.parameter_schemas:type_name -> provisioner.ParameterSchema + 16, // 9: provisionerd.UpdateJobResponse.parameter_values:type_name -> provisioner.ParameterValue + 16, // 10: provisionerd.AcquiredJob.WorkspaceProvision.parameter_values:type_name -> provisioner.ParameterValue + 13, // 11: provisionerd.CompletedJob.WorkspaceProvision.resources:type_name -> provisioner.Resource + 13, // 12: provisionerd.CompletedJob.ProjectImport.start_resources:type_name -> provisioner.Resource + 13, // 13: provisionerd.CompletedJob.ProjectImport.stop_resources:type_name -> provisioner.Resource 1, // 14: provisionerd.ProvisionerDaemon.AcquireJob:input_type -> provisionerd.Empty - 7, // 15: provisionerd.ProvisionerDaemon.UpdateJob:input_type -> provisionerd.JobUpdate + 7, // 15: provisionerd.ProvisionerDaemon.UpdateJob:input_type -> provisionerd.UpdateJobRequest 3, // 16: provisionerd.ProvisionerDaemon.CancelJob:input_type -> provisionerd.CancelledJob 5, // 17: provisionerd.ProvisionerDaemon.CompleteJob:input_type -> provisionerd.CompletedJob 2, // 18: provisionerd.ProvisionerDaemon.AcquireJob:output_type -> provisionerd.AcquiredJob - 1, // 19: provisionerd.ProvisionerDaemon.UpdateJob:output_type -> provisionerd.Empty + 8, // 19: provisionerd.ProvisionerDaemon.UpdateJob:output_type -> provisionerd.UpdateJobResponse 1, // 20: provisionerd.ProvisionerDaemon.CancelJob:output_type -> provisionerd.Empty 1, // 21: provisionerd.ProvisionerDaemon.CompleteJob:output_type -> provisionerd.Empty 18, // [18:22] is the sub-list for method output_type @@ -1098,7 +1118,7 @@ func file_provisionerd_proto_provisionerd_proto_init() { } } file_provisionerd_proto_provisionerd_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*JobUpdate); i { + switch v := v.(*UpdateJobRequest); i { case 0: return &v.state case 1: @@ -1110,7 +1130,7 @@ func file_provisionerd_proto_provisionerd_proto_init() { } } file_provisionerd_proto_provisionerd_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AcquiredJob_WorkspaceProvision); i { + switch v := v.(*UpdateJobResponse); i { case 0: return &v.state case 1: @@ -1122,7 +1142,7 @@ func file_provisionerd_proto_provisionerd_proto_init() { } } file_provisionerd_proto_provisionerd_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AcquiredJob_ProjectImport); i { + switch v := v.(*AcquiredJob_WorkspaceProvision); i { case 0: return &v.state case 1: @@ -1134,7 +1154,7 @@ func file_provisionerd_proto_provisionerd_proto_init() { } } file_provisionerd_proto_provisionerd_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CompletedJob_WorkspaceProvision); i { + switch v := v.(*AcquiredJob_ProjectImport); i { case 0: return &v.state case 1: @@ -1146,6 +1166,18 @@ func file_provisionerd_proto_provisionerd_proto_init() { } } file_provisionerd_proto_provisionerd_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CompletedJob_WorkspaceProvision); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_provisionerd_proto_provisionerd_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CompletedJob_ProjectImport); i { case 0: return &v.state @@ -1172,7 +1204,7 @@ func file_provisionerd_proto_provisionerd_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_provisionerd_proto_provisionerd_proto_rawDesc, NumEnums: 1, - NumMessages: 11, + NumMessages: 12, NumExtensions: 0, NumServices: 1, }, diff --git a/provisionerd/proto/provisionerd.proto b/provisionerd/proto/provisionerd.proto index 35c7298693f77..620d579910e2d 100644 --- a/provisionerd/proto/provisionerd.proto +++ b/provisionerd/proto/provisionerd.proto @@ -18,9 +18,6 @@ message AcquiredJob { bytes state = 4; } message ProjectImport { - repeated provisioner.ParameterValue parameter_values = 1; - bool skip_parameter_schemas = 2; - bool skip_resources = 3; } string job_id = 1; int64 created_at = 2; @@ -55,9 +52,8 @@ message CompletedJob { repeated provisioner.Resource resources = 2; } message ProjectImport { - repeated provisioner.ParameterSchema parameter_schemas = 1; - repeated provisioner.Resource start_resources = 2; - repeated provisioner.Resource stop_resources = 3; + repeated provisioner.Resource start_resources = 1; + repeated provisioner.Resource stop_resources = 2; } string job_id = 1; oneof type { @@ -80,12 +76,17 @@ message Log { string output = 4; } -// JobUpdate represents an update to a job. -// There may be no log output, but this message -// should still be sent periodically as a heartbeat. -message JobUpdate { +// This message should be sent periodically as a heartbeat. +message UpdateJobRequest { string job_id = 1; repeated Log logs = 2; + repeated provisioner.ParameterSchema parameter_schemas = 3; +} + +message UpdateJobResponse { + // If parameter schemas are sent, the job will respond + // with resolved parameter values. + repeated provisioner.ParameterValue parameter_values = 1; } service ProvisionerDaemon { @@ -97,8 +98,8 @@ service ProvisionerDaemon { // UpdateJob streams periodic updates for a job. // Implementations should buffer logs so this stream // is non-blocking. - rpc UpdateJob(stream JobUpdate) returns (Empty); - + rpc UpdateJob(UpdateJobRequest) returns (UpdateJobResponse); + // CancelJob indicates a job has been cancelled with // an error message. rpc CancelJob(CancelledJob) returns (Empty); diff --git a/provisionerd/proto/provisionerd_drpc.pb.go b/provisionerd/proto/provisionerd_drpc.pb.go index 10ade5583bc32..6e5a116239df3 100644 --- a/provisionerd/proto/provisionerd_drpc.pb.go +++ b/provisionerd/proto/provisionerd_drpc.pb.go @@ -39,7 +39,7 @@ type DRPCProvisionerDaemonClient interface { DRPCConn() drpc.Conn AcquireJob(ctx context.Context, in *Empty) (*AcquiredJob, error) - UpdateJob(ctx context.Context) (DRPCProvisionerDaemon_UpdateJobClient, error) + UpdateJob(ctx context.Context, in *UpdateJobRequest) (*UpdateJobResponse, error) CancelJob(ctx context.Context, in *CancelledJob) (*Empty, error) CompleteJob(ctx context.Context, in *CompletedJob) (*Empty, error) } @@ -63,45 +63,13 @@ func (c *drpcProvisionerDaemonClient) AcquireJob(ctx context.Context, in *Empty) return out, nil } -func (c *drpcProvisionerDaemonClient) UpdateJob(ctx context.Context) (DRPCProvisionerDaemon_UpdateJobClient, error) { - stream, err := c.cc.NewStream(ctx, "/provisionerd.ProvisionerDaemon/UpdateJob", drpcEncoding_File_provisionerd_proto_provisionerd_proto{}) +func (c *drpcProvisionerDaemonClient) UpdateJob(ctx context.Context, in *UpdateJobRequest) (*UpdateJobResponse, error) { + out := new(UpdateJobResponse) + err := c.cc.Invoke(ctx, "/provisionerd.ProvisionerDaemon/UpdateJob", drpcEncoding_File_provisionerd_proto_provisionerd_proto{}, in, out) if err != nil { return nil, err } - x := &drpcProvisionerDaemon_UpdateJobClient{stream} - return x, nil -} - -type DRPCProvisionerDaemon_UpdateJobClient interface { - drpc.Stream - Send(*JobUpdate) error - CloseAndRecv() (*Empty, error) -} - -type drpcProvisionerDaemon_UpdateJobClient struct { - drpc.Stream -} - -func (x *drpcProvisionerDaemon_UpdateJobClient) Send(m *JobUpdate) error { - return x.MsgSend(m, drpcEncoding_File_provisionerd_proto_provisionerd_proto{}) -} - -func (x *drpcProvisionerDaemon_UpdateJobClient) CloseAndRecv() (*Empty, error) { - if err := x.CloseSend(); err != nil { - return nil, err - } - m := new(Empty) - if err := x.MsgRecv(m, drpcEncoding_File_provisionerd_proto_provisionerd_proto{}); err != nil { - return nil, err - } - return m, nil -} - -func (x *drpcProvisionerDaemon_UpdateJobClient) CloseAndRecvMsg(m *Empty) error { - if err := x.CloseSend(); err != nil { - return err - } - return x.MsgRecv(m, drpcEncoding_File_provisionerd_proto_provisionerd_proto{}) + return out, nil } func (c *drpcProvisionerDaemonClient) CancelJob(ctx context.Context, in *CancelledJob) (*Empty, error) { @@ -124,7 +92,7 @@ func (c *drpcProvisionerDaemonClient) CompleteJob(ctx context.Context, in *Compl type DRPCProvisionerDaemonServer interface { AcquireJob(context.Context, *Empty) (*AcquiredJob, error) - UpdateJob(DRPCProvisionerDaemon_UpdateJobStream) error + UpdateJob(context.Context, *UpdateJobRequest) (*UpdateJobResponse, error) CancelJob(context.Context, *CancelledJob) (*Empty, error) CompleteJob(context.Context, *CompletedJob) (*Empty, error) } @@ -135,8 +103,8 @@ func (s *DRPCProvisionerDaemonUnimplementedServer) AcquireJob(context.Context, * return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented) } -func (s *DRPCProvisionerDaemonUnimplementedServer) UpdateJob(DRPCProvisionerDaemon_UpdateJobStream) error { - return drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented) +func (s *DRPCProvisionerDaemonUnimplementedServer) UpdateJob(context.Context, *UpdateJobRequest) (*UpdateJobResponse, error) { + return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented) } func (s *DRPCProvisionerDaemonUnimplementedServer) CancelJob(context.Context, *CancelledJob) (*Empty, error) { @@ -165,9 +133,10 @@ func (DRPCProvisionerDaemonDescription) Method(n int) (string, drpc.Encoding, dr case 1: return "/provisionerd.ProvisionerDaemon/UpdateJob", drpcEncoding_File_provisionerd_proto_provisionerd_proto{}, func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) { - return nil, srv.(DRPCProvisionerDaemonServer). + return srv.(DRPCProvisionerDaemonServer). UpdateJob( - &drpcProvisionerDaemon_UpdateJobStream{in1.(drpc.Stream)}, + ctx, + in1.(*UpdateJobRequest), ) }, DRPCProvisionerDaemonServer.UpdateJob, true case 2: @@ -215,33 +184,20 @@ func (x *drpcProvisionerDaemon_AcquireJobStream) SendAndClose(m *AcquiredJob) er type DRPCProvisionerDaemon_UpdateJobStream interface { drpc.Stream - SendAndClose(*Empty) error - Recv() (*JobUpdate, error) + SendAndClose(*UpdateJobResponse) error } type drpcProvisionerDaemon_UpdateJobStream struct { drpc.Stream } -func (x *drpcProvisionerDaemon_UpdateJobStream) SendAndClose(m *Empty) error { +func (x *drpcProvisionerDaemon_UpdateJobStream) SendAndClose(m *UpdateJobResponse) error { if err := x.MsgSend(m, drpcEncoding_File_provisionerd_proto_provisionerd_proto{}); err != nil { return err } return x.CloseSend() } -func (x *drpcProvisionerDaemon_UpdateJobStream) Recv() (*JobUpdate, error) { - m := new(JobUpdate) - if err := x.MsgRecv(m, drpcEncoding_File_provisionerd_proto_provisionerd_proto{}); err != nil { - return nil, err - } - return m, nil -} - -func (x *drpcProvisionerDaemon_UpdateJobStream) RecvMsg(m *JobUpdate) error { - return x.MsgRecv(m, drpcEncoding_File_provisionerd_proto_provisionerd_proto{}) -} - type DRPCProvisionerDaemon_CancelJobStream interface { drpc.Stream SendAndClose(*Empty) error diff --git a/provisionerd/provisionerd.go b/provisionerd/provisionerd.go index f771ad9094cd1..4fd11b6c9906f 100644 --- a/provisionerd/provisionerd.go +++ b/provisionerd/provisionerd.go @@ -71,7 +71,6 @@ type provisionerDaemon struct { clientDialer Dialer client proto.DRPCProvisionerDaemonClient - updateStream proto.DRPCProvisionerDaemon_UpdateJobClient // Locked when closing the daemon. closeMutex sync.Mutex @@ -104,17 +103,6 @@ func (p *provisionerDaemon) connect(ctx context.Context) { p.opts.Logger.Warn(context.Background(), "failed to dial", slog.Error(err)) continue } - p.updateStream, err = p.client.UpdateJob(ctx) - if err != nil { - if errors.Is(err, context.Canceled) { - return - } - if p.isClosed() { - return - } - p.opts.Logger.Warn(context.Background(), "create update job stream", slog.Error(err)) - continue - } p.opts.Logger.Debug(context.Background(), "connected") break } @@ -131,11 +119,11 @@ func (p *provisionerDaemon) connect(ctx context.Context) { select { case <-p.closed: return - case <-p.updateStream.Context().Done(): + case <-p.client.DRPCConn().Closed(): // We use the update stream to detect when the connection // has been interrupted. This works well, because logs need // to buffer if a job is running in the background. - p.opts.Logger.Debug(context.Background(), "update stream ended", slog.Error(p.updateStream.Context().Err())) + p.opts.Logger.Debug(context.Background(), "client stream ended") p.connect(ctx) } }() @@ -150,7 +138,7 @@ func (p *provisionerDaemon) connect(ctx context.Context) { select { case <-p.closed: return - case <-p.updateStream.Context().Done(): + case <-p.client.DRPCConn().Closed(): return case <-ticker.C: p.acquireJob(ctx) @@ -219,7 +207,7 @@ func (p *provisionerDaemon) runJob(ctx context.Context, job *proto.AcquiredJob) case <-ctx.Done(): return case <-ticker.C: - err := p.updateStream.Send(&proto.JobUpdate{ + _, err := p.client.UpdateJob(ctx, &proto.UpdateJobRequest{ JobId: job.JobId, }) if err != nil { @@ -344,48 +332,59 @@ func (p *provisionerDaemon) runJob(ctx context.Context, job *proto.AcquiredJob) } func (p *provisionerDaemon) runProjectImport(ctx context.Context, provisioner sdkproto.DRPCProvisionerClient, job *proto.AcquiredJob) { - var parameterSchemas []*sdkproto.ParameterSchema - var startResources []*sdkproto.Resource - var stopResources []*sdkproto.Resource - var err error + parameterSchemas, err := p.runProjectImportParse(ctx, provisioner, job) + if err != nil { + p.cancelActiveJobf("run parse: %s", err) + return + } - if !job.GetProjectImport().SkipParameterSchemas { - parameterSchemas, err = p.runProjectImportParse(ctx, provisioner, job) - if err != nil { - p.cancelActiveJobf("run parse: %s", err) - return - } + updateResponse, err := p.client.UpdateJob(ctx, &proto.UpdateJobRequest{ + JobId: job.JobId, + ParameterSchemas: parameterSchemas, + }) + if err != nil { + p.cancelActiveJobf("update job: %s", err) + return } - if !job.GetProjectImport().SkipResources { - startResources, err = p.runProjectImportProvision(ctx, provisioner, job, append(job.GetProjectImport().GetParameterValues(), &sdkproto.ParameterValue{ - DestinationScheme: sdkproto.ParameterDestination_PROVISIONER_VARIABLE, - // TODO: Make this a constant higher-up in the stack. - Name: "coder_workspace_transition", - Value: "start", - })) - if err != nil { - p.cancelActiveJobf("project import provision for start: %s", err) - return - } - stopResources, err = p.runProjectImportProvision(ctx, provisioner, job, append(job.GetProjectImport().GetParameterValues(), &sdkproto.ParameterValue{ - DestinationScheme: sdkproto.ParameterDestination_PROVISIONER_VARIABLE, - Name: "coder_workspace_transition", - Value: "stop", - })) - if err != nil { - p.cancelActiveJobf("project import provision for start: %s", err) + valueByName := map[string]*sdkproto.ParameterValue{} + for _, parameterValue := range updateResponse.ParameterValues { + valueByName[parameterValue.Name] = parameterValue + } + for _, parameterSchema := range parameterSchemas { + _, ok := valueByName[parameterSchema.Name] + if !ok { + p.cancelActiveJobf("missing parameter: %s", parameterSchema.Name) return } } + startResources, err := p.runProjectImportProvision(ctx, provisioner, job, append(updateResponse.ParameterValues, &sdkproto.ParameterValue{ + DestinationScheme: sdkproto.ParameterDestination_PROVISIONER_VARIABLE, + // TODO: Make this a constant higher-up in the stack. + Name: "coder_workspace_transition", + Value: "start", + })) + if err != nil { + p.cancelActiveJobf("project import provision for start: %s", err) + return + } + stopResources, err := p.runProjectImportProvision(ctx, provisioner, job, append(updateResponse.ParameterValues, &sdkproto.ParameterValue{ + DestinationScheme: sdkproto.ParameterDestination_PROVISIONER_VARIABLE, + Name: "coder_workspace_transition", + Value: "stop", + })) + if err != nil { + p.cancelActiveJobf("project import provision for start: %s", err) + return + } + _, err = p.client.CompleteJob(ctx, &proto.CompletedJob{ JobId: job.JobId, Type: &proto.CompletedJob_ProjectImport_{ ProjectImport: &proto.CompletedJob_ProjectImport{ - ParameterSchemas: parameterSchemas, - StartResources: startResources, - StopResources: stopResources, + StartResources: startResources, + StopResources: stopResources, }, }, }) @@ -416,7 +415,7 @@ func (p *provisionerDaemon) runProjectImportParse(ctx context.Context, provision slog.F("output", msgType.Log.Output), ) - err = p.updateStream.Send(&proto.JobUpdate{ + _, err = p.client.UpdateJob(ctx, &proto.UpdateJobRequest{ JobId: job.JobId, Logs: []*proto.Log{{ Source: proto.LogSource_PROVISIONER, @@ -466,7 +465,7 @@ func (p *provisionerDaemon) runProjectImportProvision(ctx context.Context, provi slog.F("output", msgType.Log.Output), ) - err = p.updateStream.Send(&proto.JobUpdate{ + _, err = p.client.UpdateJob(ctx, &proto.UpdateJobRequest{ JobId: job.JobId, Logs: []*proto.Log{{ Source: proto.LogSource_PROVISIONER, @@ -519,7 +518,7 @@ func (p *provisionerDaemon) runWorkspaceProvision(ctx context.Context, provision slog.F("workspace_history_id", job.GetWorkspaceProvision().WorkspaceHistoryId), ) - err = p.updateStream.Send(&proto.JobUpdate{ + _, err = p.client.UpdateJob(ctx, &proto.UpdateJobRequest{ JobId: job.JobId, Logs: []*proto.Log{{ Source: proto.LogSource_PROVISIONER, diff --git a/provisionerd/provisionerd_test.go b/provisionerd/provisionerd_test.go index 99f9f254086d5..7e707dd314ac4 100644 --- a/provisionerd/provisionerd_test.go +++ b/provisionerd/provisionerd_test.go @@ -34,9 +34,8 @@ func TestMain(m *testing.M) { func TestProvisionerd(t *testing.T) { t.Parallel() - noopUpdateJob := func(stream proto.DRPCProvisionerDaemon_UpdateJobStream) error { - <-stream.Context().Done() - return nil + noopUpdateJob := func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) { + return &proto.UpdateJobResponse{}, nil } t.Run("InstantClose", func(t *testing.T) { @@ -170,14 +169,9 @@ func TestProvisionerd(t *testing.T) { }, }, nil }, - updateJob: func(stream proto.DRPCProvisionerDaemon_UpdateJobStream) error { - for { - _, err := stream.Recv() - if err != nil { - return err - } - close(completeChan) - } + updateJob: func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) { + close(completeChan) + return &proto.UpdateJobResponse{}, nil }, cancelJob: func(ctx context.Context, job *proto.CancelledJob) (*proto.Empty, error) { return &proto.Empty{}, nil @@ -222,18 +216,11 @@ func TestProvisionerd(t *testing.T) { }, }, nil }, - updateJob: func(stream proto.DRPCProvisionerDaemon_UpdateJobStream) error { - for { - msg, err := stream.Recv() - if err != nil { - return err - } - if len(msg.Logs) == 0 { - continue - } - + updateJob: func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) { + if len(update.Logs) != 0 { didLog.Store(true) } + return &proto.UpdateJobResponse{}, nil }, completeJob: func(ctx context.Context, job *proto.CompletedJob) (*proto.Empty, error) { didComplete.Store(true) @@ -323,18 +310,11 @@ func TestProvisionerd(t *testing.T) { }, }, nil }, - updateJob: func(stream proto.DRPCProvisionerDaemon_UpdateJobStream) error { - for { - msg, err := stream.Recv() - if err != nil { - return err - } - if len(msg.Logs) == 0 { - continue - } - + updateJob: func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) { + if len(update.Logs) != 0 { didLog.Store(true) } + return &proto.UpdateJobResponse{}, nil }, completeJob: func(ctx context.Context, job *proto.CompletedJob) (*proto.Empty, error) { didComplete.Store(true) @@ -463,7 +443,7 @@ func (p *provisionerTestServer) Provision(request *sdkproto.Provision_Request, s // passable functions for dynamic functionality. type provisionerDaemonTestServer struct { acquireJob func(ctx context.Context, _ *proto.Empty) (*proto.AcquiredJob, error) - updateJob func(stream proto.DRPCProvisionerDaemon_UpdateJobStream) error + updateJob func(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) cancelJob func(ctx context.Context, job *proto.CancelledJob) (*proto.Empty, error) completeJob func(ctx context.Context, job *proto.CompletedJob) (*proto.Empty, error) } @@ -472,8 +452,8 @@ func (p *provisionerDaemonTestServer) AcquireJob(ctx context.Context, empty *pro return p.acquireJob(ctx, empty) } -func (p *provisionerDaemonTestServer) UpdateJob(stream proto.DRPCProvisionerDaemon_UpdateJobStream) error { - return p.updateJob(stream) +func (p *provisionerDaemonTestServer) UpdateJob(ctx context.Context, update *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) { + return p.updateJob(ctx, update) } func (p *provisionerDaemonTestServer) CancelJob(ctx context.Context, job *proto.CancelledJob) (*proto.Empty, error) { From e53f0beb26c83638ce269e3662fdc82d8b736ab2 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 10 Feb 2022 20:54:46 +0000 Subject: [PATCH 05/20] Refactor job update for provisionerd --- coderd/provisionerdaemons.go | 254 +++++++++++-------------- coderd/provisionerjobs.go | 12 -- coderd/provisionerjobs_test.go | 14 +- provisioner/terraform/parse.go | 1 - provisioner/terraform/parse_test.go | 1 - provisionersdk/proto/provisioner.pb.go | 249 ++++++++++++------------ provisionersdk/proto/provisioner.proto | 1 - 7 files changed, 244 insertions(+), 288 deletions(-) diff --git a/coderd/provisionerdaemons.go b/coderd/provisionerdaemons.go index f84da2ebcb8b8..4e4e81ea9ea7e 100644 --- a/coderd/provisionerdaemons.go +++ b/coderd/provisionerdaemons.go @@ -109,12 +109,6 @@ type workspaceProvisionJob struct { DryRun bool `json:"dry_run"` } -// The input for a "project_import" job. -type projectVersionImportJob struct { - OrganizationID string `json:"organization_id"` - ProjectID uuid.UUID `json:"project_id"` -} - // Implementation of the provisioner daemon protobuf server. type provisionerdServer struct { ID uuid.UUID @@ -242,39 +236,8 @@ func (server *provisionerdServer) AcquireJob(ctx context.Context, _ *proto.Empty }, } 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)) - } - - // Compute parameters for the workspace to consume. - parameters, err := parameter.Compute(ctx, server.Database, parameter.ComputeScope{ - ProjectImportJobID: job.ID, - OrganizationID: input.OrganizationID, - ProjectID: uuid.NullUUID{ - UUID: input.ProjectID, - Valid: input.ProjectID.String() != uuid.Nil.String(), - }, - UserID: user.ID, - }, nil) - if err != nil { - 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 { - converted, err := convertComputedParameterValue(parameter) - if err != nil { - return nil, failJob(fmt.Sprintf("convert parameter: %s", err)) - } - protoParameters = append(protoParameters, converted) - } - protoJob.Type = &proto.AcquiredJob_ProjectImport_{ - ProjectImport: &proto.AcquiredJob_ProjectImport{ - ParameterValues: protoParameters, - }, + ProjectImport: &proto.AcquiredJob_ProjectImport{}, } } switch job.StorageMethod { @@ -291,119 +254,137 @@ func (server *provisionerdServer) AcquireJob(ctx context.Context, _ *proto.Empty return protoJob, err } -func (server *provisionerdServer) UpdateJob(stream proto.DRPCProvisionerDaemon_UpdateJobStream) error { - for { - update, err := stream.Recv() - if err != nil { - return err +func (server *provisionerdServer) UpdateJob(ctx context.Context, request *proto.UpdateJobRequest) (*proto.UpdateJobResponse, error) { + parsedID, err := uuid.Parse(request.JobId) + if err != nil { + return nil, xerrors.Errorf("parse job id: %w", err) + } + job, err := server.Database.GetProvisionerJobByID(ctx, parsedID) + if err != nil { + return nil, xerrors.Errorf("get job: %w", err) + } + if !job.WorkerID.Valid { + return nil, xerrors.New("job isn't running yet") + } + if job.WorkerID.UUID.String() != server.ID.String() { + return nil, xerrors.New("you don't own this job") + } + err = server.Database.UpdateProvisionerJobByID(ctx, database.UpdateProvisionerJobByIDParams{ + ID: parsedID, + UpdatedAt: database.Now(), + }) + if err != nil { + return nil, xerrors.Errorf("update job: %w", err) + } + + if len(request.Logs) > 0 { + insertParams := database.InsertProvisionerJobLogsParams{ + JobID: parsedID, } - parsedID, err := uuid.Parse(update.JobId) - if err != nil { - return xerrors.Errorf("parse job id: %w", err) + for _, log := range request.Logs { + logLevel, err := convertLogLevel(log.Level) + if err != nil { + return nil, xerrors.Errorf("convert log level: %w", err) + } + logSource, err := convertLogSource(log.Source) + if err != nil { + return nil, xerrors.Errorf("convert log source: %w", err) + } + insertParams.ID = append(insertParams.ID, uuid.New()) + insertParams.CreatedAt = append(insertParams.CreatedAt, time.UnixMilli(log.CreatedAt)) + insertParams.Level = append(insertParams.Level, logLevel) + insertParams.Source = append(insertParams.Source, logSource) + insertParams.Output = append(insertParams.Output, log.Output) } - job, err := server.Database.GetProvisionerJobByID(stream.Context(), parsedID) + logs, err := server.Database.InsertProvisionerJobLogs(context.Background(), insertParams) if err != nil { - return xerrors.Errorf("get job: %w", err) + return nil, xerrors.Errorf("insert job logs: %w", err) } - if !job.WorkerID.Valid { - return xerrors.New("job isn't running yet") - } - if job.WorkerID.UUID.String() != server.ID.String() { - return xerrors.New("you don't own this job") + data, err := json.Marshal(logs) + if err != nil { + return nil, xerrors.Errorf("marshal job log: %w", err) } - - err = server.Database.UpdateProvisionerJobByID(stream.Context(), database.UpdateProvisionerJobByIDParams{ - ID: parsedID, - UpdatedAt: database.Now(), - }) + err = server.Pubsub.Publish(provisionerJobLogsChannel(parsedID), data) if err != nil { - return xerrors.Errorf("update job: %w", err) + return nil, xerrors.Errorf("publish job log: %w", err) } - if len(update.Logs) > 0 { - insertParams := database.InsertProvisionerJobLogsParams{ - JobID: parsedID, - } - for _, log := range update.Logs { - logLevel, err := convertLogLevel(log.Level) - if err != nil { - return xerrors.Errorf("convert log level: %w", err) - } - logSource, err := convertLogSource(log.Source) - if err != nil { - return xerrors.Errorf("convert log source: %w", err) - } - insertParams.ID = append(insertParams.ID, uuid.New()) - insertParams.CreatedAt = append(insertParams.CreatedAt, time.UnixMilli(log.CreatedAt)) - insertParams.Level = append(insertParams.Level, logLevel) - insertParams.Source = append(insertParams.Source, logSource) - insertParams.Output = append(insertParams.Output, log.Output) - } - logs, err := server.Database.InsertProvisionerJobLogs(context.Background(), insertParams) - if err != nil { - return xerrors.Errorf("insert job logs: %w", err) - } - data, err := json.Marshal(logs) + } + + if len(request.ParameterSchemas) > 0 { + for _, protoParameter := range request.ParameterSchemas { + validationTypeSystem, err := convertValidationTypeSystem(protoParameter.ValidationTypeSystem) if err != nil { - return xerrors.Errorf("marshal job log: %w", err) + return nil, xerrors.Errorf("convert validation type system for %q: %w", protoParameter.Name, err) } - err = server.Pubsub.Publish(provisionerJobLogsChannel(parsedID), data) - if err != nil { - return xerrors.Errorf("publish job log: %w", err) + + parameterSchema := database.InsertParameterSchemaParams{ + ID: uuid.New(), + CreatedAt: database.Now(), + JobID: job.ID, + Name: protoParameter.Name, + Description: protoParameter.Description, + RedisplayValue: protoParameter.RedisplayValue, + ValidationError: protoParameter.ValidationError, + ValidationCondition: protoParameter.ValidationCondition, + ValidationValueType: protoParameter.ValidationValueType, + ValidationTypeSystem: validationTypeSystem, + + DefaultSourceScheme: database.ParameterSourceSchemeNone, + DefaultDestinationScheme: database.ParameterDestinationSchemeNone, + + AllowOverrideDestination: protoParameter.AllowOverrideDestination, + AllowOverrideSource: protoParameter.AllowOverrideSource, } - } - if update.GetProjectImport() != nil { - // Validate that all parameters send from the provisioner daemon - // follow the protocol. - parameterSchemas := make([]database.InsertParameterSchemaParams, 0, len(update.GetProjectImport().ParameterSchemas)) - for _, protoParameter := range update.GetProjectImport().ParameterSchemas { - validationTypeSystem, err := convertValidationTypeSystem(protoParameter.ValidationTypeSystem) + // It's possible a parameter doesn't define a default source! + if protoParameter.DefaultSource != nil { + parameterSourceScheme, err := convertParameterSourceScheme(protoParameter.DefaultSource.Scheme) if err != nil { - return xerrors.Errorf("convert validation type system for %q: %w", protoParameter.Name, err) - } - - parameterSchema := database.InsertParameterSchemaParams{ - ID: uuid.New(), - CreatedAt: database.Now(), - JobID: job.ID, - Name: protoParameter.Name, - Description: protoParameter.Description, - RedisplayValue: protoParameter.RedisplayValue, - ValidationError: protoParameter.ValidationError, - ValidationCondition: protoParameter.ValidationCondition, - ValidationValueType: protoParameter.ValidationValueType, - ValidationTypeSystem: validationTypeSystem, - - DefaultSourceScheme: database.ParameterSourceSchemeNone, - DefaultDestinationScheme: database.ParameterDestinationSchemeNone, - - AllowOverrideDestination: protoParameter.AllowOverrideDestination, - AllowOverrideSource: protoParameter.AllowOverrideSource, + return nil, xerrors.Errorf("convert parameter source scheme: %w", err) } + parameterSchema.DefaultSourceScheme = parameterSourceScheme + parameterSchema.DefaultSourceValue = protoParameter.DefaultSource.Value + } - // It's possible a parameter doesn't define a default source! - if protoParameter.DefaultSource != nil { - parameterSourceScheme, err := convertParameterSourceScheme(protoParameter.DefaultSource.Scheme) - if err != nil { - return xerrors.Errorf("convert parameter source scheme: %w", err) - } - parameterSchema.DefaultSourceScheme = parameterSourceScheme - parameterSchema.DefaultSourceValue = protoParameter.DefaultSource.Value + // It's possible a parameter doesn't define a default destination! + if protoParameter.DefaultDestination != nil { + parameterDestinationScheme, err := convertParameterDestinationScheme(protoParameter.DefaultDestination.Scheme) + if err != nil { + return nil, xerrors.Errorf("convert parameter destination scheme: %w", err) } + parameterSchema.DefaultDestinationScheme = parameterDestinationScheme + } - // It's possible a parameter doesn't define a default destination! - if protoParameter.DefaultDestination != nil { - parameterDestinationScheme, err := convertParameterDestinationScheme(protoParameter.DefaultDestination.Scheme) - if err != nil { - return xerrors.Errorf("convert parameter destination scheme: %w", err) - } - parameterSchema.DefaultDestinationScheme = parameterDestinationScheme - } + _, err = server.Database.InsertParameterSchema(ctx, parameterSchema) + if err != nil { + return nil, xerrors.Errorf("insert parameter schema: %w", err) + } + } - parameterSchemas = append(parameterSchemas, parameterSchema) + parameters, err := parameter.Compute(ctx, server.Database, parameter.ComputeScope{ + ProjectImportJobID: job.ID, + OrganizationID: job.OrganizationID, + UserID: job.InitiatorID, + }, nil) + if err != nil { + return nil, xerrors.Errorf("compute parameters: %w", err) + } + // Convert parameters to the protobuf type. + protoParameters := make([]*sdkproto.ParameterValue, 0, len(parameters)) + for _, parameter := range parameters { + converted, err := convertComputedParameterValue(parameter) + if err != nil { + return nil, xerrors.Errorf("convert parameter: %s", err) } + protoParameters = append(protoParameters, converted) } + + return &proto.UpdateJobResponse{ + ParameterValues: protoParameters, + }, nil } + + return &proto.UpdateJobResponse{}, nil } func (server *provisionerdServer) CancelJob(ctx context.Context, cancelJob *proto.CancelledJob) (*proto.Empty, error) { @@ -450,17 +431,12 @@ func (server *provisionerdServer) CompleteJob(ctx context.Context, completed *pr if err != nil { return nil, xerrors.Errorf("get job by id: %w", err) } - // TODO: Check if the worker ID matches! - // If it doesn't, a provisioner daemon could be impersonating another job! + if job.WorkerID.UUID.String() != server.ID.String() { + return nil, xerrors.Errorf("you don't have permission to update this job") + } switch jobType := completed.Type.(type) { case *proto.CompletedJob_ProjectImport_: - var input projectVersionImportJob - err = json.Unmarshal(job.Input, &input) - if err != nil { - return nil, xerrors.Errorf("unmarshal job data: %w", err) - } - err = server.Database.UpdateProvisionerJobWithCompleteByID(ctx, database.UpdateProvisionerJobWithCompleteByIDParams{ ID: jobID, UpdatedAt: database.Now(), diff --git a/coderd/provisionerjobs.go b/coderd/provisionerjobs.go index d5a446e0d57ad..dce331d153e6a 100644 --- a/coderd/provisionerjobs.go +++ b/coderd/provisionerjobs.go @@ -2,7 +2,6 @@ package coderd import ( "database/sql" - "encoding/json" "errors" "fmt" "net/http" @@ -80,16 +79,6 @@ func (api *api) postProvisionerImportJobByOrganization(rw http.ResponseWriter, r return } - input, err := json.Marshal(projectVersionImportJob{ - OrganizationID: organization.ID, - }) - if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: fmt.Sprintf("marshal job: %s", err), - }) - return - } - jobID := uuid.New() for _, parameterValue := range req.ParameterValues { _, err = api.Database.InsertParameterValue(r.Context(), database.InsertParameterValueParams{ @@ -121,7 +110,6 @@ func (api *api) postProvisionerImportJobByOrganization(rw http.ResponseWriter, r StorageMethod: database.ProvisionerStorageMethodFile, StorageSource: file.Hash, Type: database.ProvisionerJobTypeProjectVersionImport, - Input: input, }) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ diff --git a/coderd/provisionerjobs_test.go b/coderd/provisionerjobs_test.go index 89f90dbf0c07e..f0bb167d5e9f5 100644 --- a/coderd/provisionerjobs_test.go +++ b/coderd/provisionerjobs_test.go @@ -2,7 +2,6 @@ package coderd_test import ( "context" - "fmt" "net/http" "testing" "time" @@ -89,7 +88,6 @@ func TestPostProvisionerImportJobByOrganization(t *testing.T) { }) require.NoError(t, err) job = coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) - fmt.Printf("Job %+v\n", job) values, err := client.ProvisionerJobParameterValues(context.Background(), user.Organization, job.ID) require.NoError(t, err) require.Equal(t, "somevalue", values[0].SourceValue) @@ -120,16 +118,23 @@ func TestProvisionerJobParametersByID(t *testing.T) { Complete: &proto.Parse_Complete{ ParameterSchemas: []*proto.ParameterSchema{{ Name: "example", + DefaultSource: &proto.ParameterSource{ + Scheme: proto.ParameterSource_DATA, + Value: "hello", + }, + DefaultDestination: &proto.ParameterDestination{ + Scheme: proto.ParameterDestination_PROVISIONER_VARIABLE, + }, }}, }, }, }}, Provision: echo.ProvisionComplete, }) - coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) + job = coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) params, err := client.ProvisionerJobParameterValues(context.Background(), user.Organization, job.ID) require.NoError(t, err) - require.Len(t, params, 0) + require.Len(t, params, 1) }) t.Run("ListNoRedisplay", func(t *testing.T) { @@ -149,7 +154,6 @@ func TestProvisionerJobParametersByID(t *testing.T) { }, DefaultDestination: &proto.ParameterDestination{ Scheme: proto.ParameterDestination_PROVISIONER_VARIABLE, - Value: "example", }, RedisplayValue: false, }}, diff --git a/provisioner/terraform/parse.go b/provisioner/terraform/parse.go index 24f4943c5bff5..11efa03a3ec6b 100644 --- a/provisioner/terraform/parse.go +++ b/provisioner/terraform/parse.go @@ -59,7 +59,6 @@ func convertVariableToParameter(variable *tfconfig.Variable) (*proto.ParameterSc } schema.DefaultDestination = &proto.ParameterDestination{ Scheme: proto.ParameterDestination_PROVISIONER_VARIABLE, - Value: variable.Name, } } diff --git a/provisioner/terraform/parse_test.go b/provisioner/terraform/parse_test.go index 2a4ce6a8b2c8e..faf3860a2844a 100644 --- a/provisioner/terraform/parse_test.go +++ b/provisioner/terraform/parse_test.go @@ -78,7 +78,6 @@ func TestParse(t *testing.T) { }, DefaultDestination: &proto.ParameterDestination{ Scheme: proto.ParameterDestination_PROVISIONER_VARIABLE, - Value: "A", }, }}, }, diff --git a/provisionersdk/proto/provisioner.pb.go b/provisionersdk/proto/provisioner.pb.go index 3dcb781e06499..18f034e0c7fd4 100644 --- a/provisionersdk/proto/provisioner.pb.go +++ b/provisionersdk/proto/provisioner.pb.go @@ -274,7 +274,6 @@ type ParameterDestination struct { unknownFields protoimpl.UnknownFields Scheme ParameterDestination_Scheme `protobuf:"varint,1,opt,name=scheme,proto3,enum=provisioner.ParameterDestination_Scheme" json:"scheme,omitempty"` - Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *ParameterDestination) Reset() { @@ -316,13 +315,6 @@ func (x *ParameterDestination) GetScheme() ParameterDestination_Scheme { return ParameterDestination_ENVIRONMENT_VARIABLE } -func (x *ParameterDestination) GetValue() string { - if x != nil { - return x.Value - } - return "" -} - // ParameterValue represents the resolved source and destination of a parameter. type ParameterValue struct { state protoimpl.MessageState @@ -1099,133 +1091,132 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{ 0x63, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x12, 0x0a, 0x06, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x41, 0x54, 0x41, 0x10, 0x00, 0x22, - 0xac, 0x01, 0x0a, 0x14, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x44, 0x65, 0x73, + 0x96, 0x01, 0x0a, 0x14, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x63, 0x68, 0x65, - 0x6d, 0x65, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x22, 0x3c, 0x0a, 0x06, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x45, 0x4e, - 0x56, 0x49, 0x52, 0x4f, 0x4e, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x56, 0x41, 0x52, 0x49, 0x41, 0x42, - 0x4c, 0x45, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, - 0x4e, 0x45, 0x52, 0x5f, 0x56, 0x41, 0x52, 0x49, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x01, 0x22, 0x93, - 0x01, 0x0a, 0x0e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x12, 0x57, 0x0a, 0x12, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x61, - 0x6d, 0x65, 0x74, 0x65, 0x72, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x52, 0x11, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x22, 0x8d, 0x05, 0x0a, 0x0f, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, - 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, - 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x43, - 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x18, 0x03, 0x20, 0x01, 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, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x52, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x12, 0x32, 0x0a, 0x15, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6f, 0x76, 0x65, - 0x72, 0x72, 0x69, 0x64, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x13, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, - 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x52, 0x0a, 0x13, 0x64, 0x65, 0x66, 0x61, 0x75, - 0x6c, 0x74, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x44, 0x65, 0x73, 0x74, - 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x12, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, - 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3c, 0x0a, 0x1a, 0x61, - 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x5f, 0x64, 0x65, - 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x18, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x44, 0x65, - 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x64, - 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0e, 0x72, 0x65, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x12, 0x5d, 0x0a, 0x16, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x27, 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, - 0x2e, 0x54, 0x79, 0x70, 0x65, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x14, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x53, 0x79, 0x73, 0x74, 0x65, - 0x6d, 0x12, 0x32, 0x0a, 0x15, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x13, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, - 0x12, 0x31, 0x0a, 0x14, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, - 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x22, 0x1f, 0x0a, 0x0a, 0x54, 0x79, 0x70, 0x65, 0x53, 0x79, 0x73, 0x74, 0x65, - 0x6d, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x48, - 0x43, 0x4c, 0x10, 0x01, 0x22, 0x4a, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x2b, 0x0a, 0x05, 0x6c, - 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 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, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, - 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, - 0x22, 0x32, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x22, 0xfc, 0x01, 0x0a, 0x05, 0x50, 0x61, 0x72, 0x73, 0x65, 0x1a, 0x27, - 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, - 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x1a, 0x55, 0x0a, 0x08, 0x43, 0x6f, 0x6d, 0x70, 0x6c, - 0x65, 0x74, 0x65, 0x12, 0x49, 0x0a, 0x11, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, - 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, + 0x6d, 0x65, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x22, 0x3c, 0x0a, 0x06, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x45, 0x4e, 0x56, 0x49, 0x52, 0x4f, 0x4e, 0x4d, + 0x45, 0x4e, 0x54, 0x5f, 0x56, 0x41, 0x52, 0x49, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x18, + 0x0a, 0x14, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x52, 0x5f, 0x56, 0x41, + 0x52, 0x49, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x01, 0x22, 0x93, 0x01, 0x0a, 0x0e, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x57, 0x0a, 0x12, 0x64, + 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x44, + 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, + 0x65, 0x52, 0x11, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x8d, + 0x05, 0x0a, 0x0f, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x43, 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 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, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0d, + 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x32, 0x0a, + 0x15, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x5f, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x61, 0x6c, + 0x6c, 0x6f, 0x77, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x12, 0x52, 0x0a, 0x13, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x64, 0x65, 0x73, + 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 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, 0x1a, 0x73, - 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x03, 0x6c, 0x6f, - 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x48, 0x00, 0x52, 0x03, 0x6c, 0x6f, 0x67, - 0x12, 0x39, 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, - 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, - 0x00, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x22, 0xfc, 0x02, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x1a, 0x9e, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, - 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 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, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x72, 0x79, - 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, - 0x75, 0x6e, 0x1a, 0x55, 0x0a, 0x08, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 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, 0x77, 0x0a, 0x08, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, - 0x2e, 0x4c, 0x6f, 0x67, 0x48, 0x00, 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x12, 0x3d, 0x0a, 0x08, 0x63, - 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, - 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x2a, 0x3f, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, - 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, - 0x55, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x02, 0x12, 0x08, - 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, - 0x52, 0x10, 0x04, 0x32, 0xa1, 0x01, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x12, 0x42, 0x0a, 0x05, 0x50, 0x61, 0x72, 0x73, 0x65, 0x12, 0x1a, 0x2e, 0x70, + 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x12, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3c, 0x0a, 0x1a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6f, + 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x61, 0x6c, 0x6c, 0x6f, 0x77, + 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, + 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x72, 0x65, + 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x5d, 0x0a, 0x16, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, + 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 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, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x53, + 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x14, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x54, 0x79, 0x70, 0x65, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x32, 0x0a, 0x15, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x29, 0x0a, 0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x31, 0x0a, 0x14, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x1f, 0x0a, + 0x0a, 0x54, 0x79, 0x70, 0x65, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x08, 0x0a, 0x04, 0x4e, + 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x48, 0x43, 0x4c, 0x10, 0x01, 0x22, 0x4a, + 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x2b, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, + 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, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0x32, 0x0a, 0x08, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xfc, + 0x01, 0x0a, 0x05, 0x50, 0x61, 0x72, 0x73, 0x65, 0x1a, 0x27, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x79, 0x1a, 0x55, 0x0a, 0x08, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x49, 0x0a, + 0x11, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x73, 0x18, 0x02, 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, 0x1a, 0x73, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, + 0x4c, 0x6f, 0x67, 0x48, 0x00, 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x12, 0x39, 0x0a, 0x08, 0x63, 0x6f, + 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, - 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x4e, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x42, 0x2d, 0x5a, 0x2b, 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, 0x73, 0x64, 0x6b, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f, 0x6d, + 0x70, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xfc, 0x02, + 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x9e, 0x01, 0x0a, 0x07, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x79, 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, 0x14, 0x0a, + 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x1a, 0x55, 0x0a, 0x08, + 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 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, 0x77, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x24, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x48, 0x00, + 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x12, 0x3d, 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, + 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x70, + 0x6c, 0x65, 0x74, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x2a, 0x3f, 0x0a, 0x08, + 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, + 0x45, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x08, + 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, + 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x32, 0xa1, 0x01, + 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x42, 0x0a, + 0x05, 0x50, 0x61, 0x72, 0x73, 0x65, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, + 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, + 0x01, 0x12, 0x4e, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, + 0x01, 0x42, 0x2d, 0x5a, 0x2b, 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, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/provisionersdk/proto/provisioner.proto b/provisionersdk/proto/provisioner.proto index 546c081cd6882..0b999dffbc4fe 100644 --- a/provisionersdk/proto/provisioner.proto +++ b/provisionersdk/proto/provisioner.proto @@ -20,7 +20,6 @@ message ParameterDestination { PROVISIONER_VARIABLE = 1; } Scheme scheme = 1; - string value = 2; } // ParameterValue represents the resolved source and destination of a parameter. From 4466836a2208f33906b5435a9bd0497a40f7a56f Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 10 Feb 2022 21:18:05 +0000 Subject: [PATCH 06/20] Handle multiple states correctly when provisioning a project --- coderd/parameter/compute.go | 5 +++ provisionerd/provisionerd.go | 63 +++++++++++++++++++++++-------- provisionerd/provisionerd_test.go | 10 ++++- 3 files changed, 62 insertions(+), 16 deletions(-) diff --git a/coderd/parameter/compute.go b/coderd/parameter/compute.go index 493d72edf9ee9..801c93732ee24 100644 --- a/coderd/parameter/compute.go +++ b/coderd/parameter/compute.go @@ -11,6 +11,11 @@ import ( "github.com/coder/coder/database" ) +const ( + CoderUsername = "coder_username" + CoderWorkspaceTransition = "coder_workspace_transition" +) + // ComputeScope targets identifiers to pull parameters from. type ComputeScope struct { ProjectImportJobID uuid.UUID diff --git a/provisionerd/provisionerd.go b/provisionerd/provisionerd.go index 4fd11b6c9906f..bf50a8cc3281d 100644 --- a/provisionerd/provisionerd.go +++ b/provisionerd/provisionerd.go @@ -19,11 +19,23 @@ import ( "golang.org/x/xerrors" "cdr.dev/slog" + "github.com/coder/coder/coderd/parameter" "github.com/coder/coder/provisionerd/proto" sdkproto "github.com/coder/coder/provisionersdk/proto" "github.com/coder/retry" ) +const ( + missingParameterErrorText = "missing parameter" +) + +// IsMissingParameterError returns whether the error message provided +// is a missing parameter error. This can indicate to consumers that +// they should check parameters. +func IsMissingParameterError(err string) bool { + return strings.Contains(err, missingParameterErrorText) +} + // Dialer represents the function to create a daemon client connection. type Dialer func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) @@ -352,32 +364,53 @@ func (p *provisionerDaemon) runProjectImport(ctx context.Context, provisioner sd valueByName[parameterValue.Name] = parameterValue } for _, parameterSchema := range parameterSchemas { + if parameterSchema.Name == parameter.CoderWorkspaceTransition { + // Hardcode the workspace transition variable. We'll + // make it do stuff later! + continue + } _, ok := valueByName[parameterSchema.Name] if !ok { - p.cancelActiveJobf("missing parameter: %s", parameterSchema.Name) + p.cancelActiveJobf("%s: %s", missingParameterErrorText, parameterSchema.Name) return } } + // Checks if the schema has defined a workspace transition variable. + // If not, we don't need to check for resources provisioned in a stopped state. + hasWorkspaceTransition := false + for _, parameterSchema := range parameterSchemas { + if parameterSchema.Name != parameter.CoderWorkspaceTransition { + continue + } + hasWorkspaceTransition = true + break + } - startResources, err := p.runProjectImportProvision(ctx, provisioner, job, append(updateResponse.ParameterValues, &sdkproto.ParameterValue{ - DestinationScheme: sdkproto.ParameterDestination_PROVISIONER_VARIABLE, - // TODO: Make this a constant higher-up in the stack. - Name: "coder_workspace_transition", - Value: "start", - })) - if err != nil { - p.cancelActiveJobf("project import provision for start: %s", err) - return + startParameters := updateResponse.ParameterValues + if hasWorkspaceTransition { + startParameters = append(updateResponse.ParameterValues, &sdkproto.ParameterValue{ + DestinationScheme: sdkproto.ParameterDestination_PROVISIONER_VARIABLE, + Name: parameter.CoderWorkspaceTransition, + Value: "start", + }) } - stopResources, err := p.runProjectImportProvision(ctx, provisioner, job, append(updateResponse.ParameterValues, &sdkproto.ParameterValue{ - DestinationScheme: sdkproto.ParameterDestination_PROVISIONER_VARIABLE, - Name: "coder_workspace_transition", - Value: "stop", - })) + startResources, err := p.runProjectImportProvision(ctx, provisioner, job, startParameters) if err != nil { p.cancelActiveJobf("project import provision for start: %s", err) return } + stopResources := startResources + if hasWorkspaceTransition { + stopResources, err = p.runProjectImportProvision(ctx, provisioner, job, append(updateResponse.ParameterValues, &sdkproto.ParameterValue{ + DestinationScheme: sdkproto.ParameterDestination_PROVISIONER_VARIABLE, + Name: "coder_workspace_transition", + Value: "stop", + })) + if err != nil { + p.cancelActiveJobf("project import provision for start: %s", err) + return + } + } _, err = p.client.CompleteJob(ctx, &proto.CompletedJob{ JobId: job.JobId, diff --git a/provisionerd/provisionerd_test.go b/provisionerd/provisionerd_test.go index 7e707dd314ac4..db7019a05f5dc 100644 --- a/provisionerd/provisionerd_test.go +++ b/provisionerd/provisionerd_test.go @@ -21,6 +21,7 @@ import ( "cdr.dev/slog" "cdr.dev/slog/sloggers/slogtest" + "github.com/coder/coder/coderd/parameter" "github.com/coder/coder/provisionerd" "github.com/coder/coder/provisionerd/proto" "github.com/coder/coder/provisionersdk" @@ -195,6 +196,7 @@ func TestProvisionerd(t *testing.T) { didComplete atomic.Bool didLog atomic.Bool didAcquireJob atomic.Bool + didDryRun atomic.Bool ) completeChan := make(chan struct{}) closer := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) { @@ -247,7 +249,9 @@ func TestProvisionerd(t *testing.T) { err = stream.Send(&sdkproto.Parse_Response{ Type: &sdkproto.Parse_Response_Complete{ Complete: &sdkproto.Parse_Complete{ - ParameterSchemas: []*sdkproto.ParameterSchema{}, + ParameterSchemas: []*sdkproto.ParameterSchema{{ + Name: parameter.CoderWorkspaceTransition, + }}, }, }, }) @@ -255,6 +259,9 @@ func TestProvisionerd(t *testing.T) { return nil }, provision: func(request *sdkproto.Provision_Request, stream sdkproto.DRPCProvisioner_ProvisionStream) error { + if request.DryRun { + didDryRun.Store(true) + } err := stream.Send(&sdkproto.Provision_Response{ Type: &sdkproto.Provision_Response_Log{ Log: &sdkproto.Log{ @@ -280,6 +287,7 @@ func TestProvisionerd(t *testing.T) { <-completeChan require.True(t, didLog.Load()) require.True(t, didComplete.Load()) + require.True(t, didDryRun.Load()) require.NoError(t, closer.Close()) }) From 8fe05d6f470faab54085fd78c8f36bf0d09031af Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 10 Feb 2022 21:24:46 +0000 Subject: [PATCH 07/20] Add project import job resource table --- database/databasefake/databasefake.go | 67 +++++++++++++++++--------- database/dump.sql | 15 ++++++ database/migrations/000004_jobs.up.sql | 10 ++++ database/models.go | 9 ++++ database/querier.go | 1 + database/query.sql | 6 +++ database/query.sql.go | 37 ++++++++++++++ 7 files changed, 121 insertions(+), 24 deletions(-) diff --git a/database/databasefake/databasefake.go b/database/databasefake/databasefake.go index 9d53254f7af0f..bd0ac62b92fb1 100644 --- a/database/databasefake/databasefake.go +++ b/database/databasefake/databasefake.go @@ -19,18 +19,19 @@ func New() database.Store { organizationMembers: make([]database.OrganizationMember, 0), users: make([]database.User, 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), + 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), + projectImportJobResource: make([]database.ProjectImportJobResource, 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 +46,19 @@ type fakeQuerier struct { users []database.User // New tables - 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 + files []database.File + parameterValue []database.ParameterValue + parameterSchema []database.ParameterSchema + project []database.Project + projectVersion []database.ProjectVersion + projectImportJobResource []database.ProjectImportJobResource + 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. @@ -666,6 +668,23 @@ func (q *fakeQuerier) InsertProject(_ context.Context, arg database.InsertProjec return project, nil } +func (q *fakeQuerier) InsertProjectImportJobResource(ctx context.Context, arg database.InsertProjectImportJobResourceParams) (database.ProjectImportJobResource, error) { + q.mutex.Lock() + defer q.mutex.Unlock() + + //nolint:gosimple + projectResource := database.ProjectImportJobResource{ + ID: arg.ID, + CreatedAt: arg.CreatedAt, + JobID: arg.JobID, + Transition: arg.Transition, + Type: arg.Type, + Name: arg.Name, + } + q.projectImportJobResource = append(q.projectImportJobResource, projectResource) + return projectResource, nil +} + func (q *fakeQuerier) InsertProjectVersion(_ context.Context, arg database.InsertProjectVersionParams) (database.ProjectVersion, error) { q.mutex.Lock() defer q.mutex.Unlock() diff --git a/database/dump.sql b/database/dump.sql index 0fde40950ad7e..d0b1d194b3099 100644 --- a/database/dump.sql +++ b/database/dump.sql @@ -163,6 +163,15 @@ CREATE TABLE project ( active_version_id uuid NOT NULL ); +CREATE TABLE project_import_job_resource ( + id uuid NOT NULL, + created_at timestamp with time zone NOT NULL, + job_id uuid NOT NULL, + transition workspace_transition NOT NULL, + type character varying(256) NOT NULL, + name character varying(64) NOT NULL +); + CREATE TABLE project_version ( id uuid NOT NULL, project_id uuid NOT NULL, @@ -291,6 +300,9 @@ ALTER TABLE ONLY parameter_value ALTER TABLE ONLY project ADD CONSTRAINT project_id_key UNIQUE (id); +ALTER TABLE ONLY project_import_job_resource + ADD CONSTRAINT project_import_job_resource_id_key UNIQUE (id); + ALTER TABLE ONLY project ADD CONSTRAINT project_organization_id_name_key UNIQUE (organization_id, name); @@ -339,6 +351,9 @@ ALTER TABLE ONLY workspace_resource 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_import_job_resource + ADD CONSTRAINT project_import_job_resource_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/000004_jobs.up.sql b/database/migrations/000004_jobs.up.sql index 436847e55d1fa..4d100e7f6fa80 100644 --- a/database/migrations/000004_jobs.up.sql +++ b/database/migrations/000004_jobs.up.sql @@ -119,3 +119,13 @@ CREATE TABLE parameter_value ( -- Prevents duplicates for parameters in the same scope. UNIQUE(name, scope, scope_id) ); + +-- Resources from multiple workspace states are stored here post project-import job. +CREATE TABLE project_import_job_resource ( + id uuid NOT NULL UNIQUE, + created_at timestamptz NOT NULL, + job_id uuid NOT NULL REFERENCES provisioner_job(id) ON DELETE CASCADE, + transition workspace_transition NOT NULL, + type varchar(256) NOT NULL, + name varchar(64) NOT NULL +); diff --git a/database/models.go b/database/models.go index ba4b57bbf0ed9..a0e60915feb23 100644 --- a/database/models.go +++ b/database/models.go @@ -342,6 +342,15 @@ type Project struct { ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"` } +type ProjectImportJobResource 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"` + Transition WorkspaceTransition `db:"transition" json:"transition"` + Type string `db:"type" json:"type"` + Name string `db:"name" json:"name"` +} + type ProjectVersion struct { ID uuid.UUID `db:"id" json:"id"` ProjectID uuid.UUID `db:"project_id" json:"project_id"` diff --git a/database/querier.go b/database/querier.go index 05fc044eeb95b..d9586447b9f57 100644 --- a/database/querier.go +++ b/database/querier.go @@ -48,6 +48,7 @@ type querier interface { InsertParameterSchema(ctx context.Context, arg InsertParameterSchemaParams) (ParameterSchema, error) InsertParameterValue(ctx context.Context, arg InsertParameterValueParams) (ParameterValue, error) InsertProject(ctx context.Context, arg InsertProjectParams) (Project, error) + InsertProjectImportJobResource(ctx context.Context, arg InsertProjectImportJobResourceParams) (ProjectImportJobResource, error) InsertProjectVersion(ctx context.Context, arg InsertProjectVersionParams) (ProjectVersion, error) InsertProvisionerDaemon(ctx context.Context, arg InsertProvisionerDaemonParams) (ProvisionerDaemon, error) InsertProvisionerJob(ctx context.Context, arg InsertProvisionerJobParams) (ProvisionerJob, error) diff --git a/database/query.sql b/database/query.sql index 0330df32aba12..b1c443e934df9 100644 --- a/database/query.sql +++ b/database/query.sql @@ -427,6 +427,12 @@ INSERT INTO VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING *; +-- name: InsertProjectImportJobResource :one +INSERT INTO + project_import_job_resource (id, created_at, job_id, transition, type, name) +VALUES + ($1, $2, $3, $4, $5, $6) RETURNING *; + -- name: InsertProjectVersion :one INSERT INTO project_version ( diff --git a/database/query.sql.go b/database/query.sql.go index ed3bd76e7649c..71ff91f091f79 100644 --- a/database/query.sql.go +++ b/database/query.sql.go @@ -1569,6 +1569,43 @@ func (q *sqlQuerier) InsertProject(ctx context.Context, arg InsertProjectParams) return i, err } +const insertProjectImportJobResource = `-- name: InsertProjectImportJobResource :one +INSERT INTO + project_import_job_resource (id, created_at, job_id, transition, type, name) +VALUES + ($1, $2, $3, $4, $5, $6) RETURNING id, created_at, job_id, transition, type, name +` + +type InsertProjectImportJobResourceParams 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"` + Transition WorkspaceTransition `db:"transition" json:"transition"` + Type string `db:"type" json:"type"` + Name string `db:"name" json:"name"` +} + +func (q *sqlQuerier) InsertProjectImportJobResource(ctx context.Context, arg InsertProjectImportJobResourceParams) (ProjectImportJobResource, error) { + row := q.db.QueryRowContext(ctx, insertProjectImportJobResource, + arg.ID, + arg.CreatedAt, + arg.JobID, + arg.Transition, + arg.Type, + arg.Name, + ) + var i ProjectImportJobResource + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.JobID, + &i.Transition, + &i.Type, + &i.Name, + ) + return i, err +} + const insertProjectVersion = `-- name: InsertProjectVersion :one INSERT INTO project_version ( From 79a56b6d293033d66488e1187c77aded894e5abb Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 10 Feb 2022 22:03:04 +0000 Subject: [PATCH 08/20] Basic creation flow works! --- cli/projectcreate.go | 72 +++-- cli/root.go | 10 + coderd/coderd.go | 1 + coderd/provisionerdaemons.go | 24 ++ coderd/provisionerjobs.go | 33 ++- coderd/provisionerjobs_test.go | 37 +++ codersdk/provisioners.go | 13 + database/databasefake/databasefake.go | 17 ++ database/querier.go | 1 + database/query.sql | 8 + database/query.sql.go | 39 +++ provisionerd/proto/provisionerd.pb.go | 377 ++++++++++---------------- provisionerd/provisionerd.go | 5 +- 13 files changed, 377 insertions(+), 260 deletions(-) diff --git a/cli/projectcreate.go b/cli/projectcreate.go index 48b0ea3040548..00da8d0e39143 100644 --- a/cli/projectcreate.go +++ b/cli/projectcreate.go @@ -11,6 +11,7 @@ import ( "github.com/briandowns/spinner" "github.com/fatih/color" + "github.com/google/uuid" "github.com/manifoldco/promptui" "github.com/spf13/cobra" "golang.org/x/xerrors" @@ -18,6 +19,7 @@ import ( "github.com/coder/coder/coderd" "github.com/coder/coder/codersdk" "github.com/coder/coder/database" + "github.com/coder/coder/provisionerd" ) func projectCreate() *cobra.Command { @@ -49,8 +51,8 @@ func projectCreate() *cobra.Command { Default: filepath.Base(directory), Label: "What's your project's name?", Validate: func(s string) error { - _, err = client.Project(cmd.Context(), organization.Name, s) - if err == nil { + project, _ := client.Project(cmd.Context(), organization.Name, s) + if project.ID.String() != uuid.Nil.String() { return xerrors.New("A project already exists with that name!") } return nil @@ -63,6 +65,7 @@ func projectCreate() *cobra.Command { spin := spinner.New(spinner.CharSets[0], 25*time.Millisecond) spin.Suffix = " Uploading current directory..." spin.Start() + defer spin.Stop() bytes, err := tarDirectory(directory) @@ -79,14 +82,6 @@ func projectCreate() *cobra.Command { StorageMethod: database.ProvisionerStorageMethodFile, StorageSource: resp.Hash, Provisioner: database.ProvisionerTypeTerraform, - // SkipResources on first import to detect variables defined by the project. - SkipResources: true, - // ParameterValues: []coderd.CreateParameterValueRequest{{ - // Name: "aws_access_key", - // SourceValue: "tomato", - // SourceScheme: database.ParameterSourceSchemeData, - // DestinationScheme: database.ParameterDestinationSchemeProvisionerVariable, - // }}, }) if err != nil { return err @@ -102,33 +97,60 @@ func projectCreate() *cobra.Command { if !ok { break } - _, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s %s\n", color.HiGreenString("[parse]"), log.Output) + _, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s %s\n", color.HiGreenString("[tf]"), log.Output) } - _, _ = fmt.Fprintf(cmd.OutOrStdout(), "Parsed project source... displaying parameters:") - - schemas, err := client.ProvisionerJobParameterSchemas(cmd.Context(), organization.Name, job.ID) + job, err = client.ProvisionerJob(cmd.Context(), organization.Name, job.ID) if err != nil { return err } - values, err := client.ProvisionerJobParameterValues(cmd.Context(), organization.Name, job.ID) + if provisionerd.IsMissingParameterError(job.Error) { + fmt.Printf("Missing something!\n") + return nil + } + + resources, err := client.ProvisionerJobResources(cmd.Context(), organization.Name, job.ID) if err != nil { return err } - valueBySchemaID := map[string]coderd.ComputedParameterValue{} - for _, value := range values { - valueBySchemaID[value.SchemaID.String()] = value - } - for _, schema := range schemas { - if value, ok := valueBySchemaID[schema.ID.String()]; ok { - fmt.Printf("Value for: %s %s\n", value.Name, value.SourceValue) - continue - } - fmt.Printf("No value for: %s\n", schema.Name) + fmt.Printf("Resources: %+v\n", resources) + + project, err := client.CreateProject(cmd.Context(), organization.Name, coderd.CreateProjectRequest{ + Name: name, + VersionImportJobID: job.ID, + }) + if err != nil { + return err } + fmt.Printf("Project: %+v\n", project) + + // _, _ = fmt.Fprintf(cmd.OutOrStdout(), "Parsed project source... displaying parameters:") + + // schemas, err := client.ProvisionerJobParameterSchemas(cmd.Context(), organization.Name, job.ID) + // if err != nil { + // return err + // } + + // values, err := client.ProvisionerJobParameterValues(cmd.Context(), organization.Name, job.ID) + // if err != nil { + // return err + // } + // valueBySchemaID := map[string]coderd.ComputedParameterValue{} + // for _, value := range values { + // valueBySchemaID[value.SchemaID.String()] = value + // } + + // for _, schema := range schemas { + // if value, ok := valueBySchemaID[schema.ID.String()]; ok { + // fmt.Printf("Value for: %s %s\n", value.Name, value.SourceValue) + // continue + // } + // fmt.Printf("No value for: %s\n", schema.Name) + // } + // schemas, err := client.ProvisionerJobParameterSchemas(cmd.Context(), organization.Name, job.ID) // if err != nil { // return err diff --git a/cli/root.go b/cli/root.go index 85db65385291a..29ef8e9609b65 100644 --- a/cli/root.go +++ b/cli/root.go @@ -161,6 +161,16 @@ func runPrompt(cmd *cobra.Command, prompt *promptui.Prompt) (string, error) { Invalid: invalid, Valid: valid, } + oldValidate := prompt.Validate + if oldValidate != nil { + // Override the validate function to pass our default! + prompt.Validate = func(s string) error { + if s == "" { + s = defaultValue + } + return oldValidate(s) + } + } value, err := prompt.Run() if value == "" && !prompt.IsConfirm { value = defaultValue diff --git a/coderd/coderd.go b/coderd/coderd.go index 3bd7ff54404de..0a2a9d87e51a8 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -123,6 +123,7 @@ func New(options *Options) http.Handler { r.Get("/", api.provisionerJobByOrganization) r.Get("/schemas", api.provisionerJobParameterSchemasByID) r.Get("/computed", api.provisionerJobComputedParametersByID) + r.Get("/resources", api.provisionerJobResourcesByID) r.Get("/logs", api.provisionerJobLogsByID) }) }) diff --git a/coderd/provisionerdaemons.go b/coderd/provisionerdaemons.go index 4e4e81ea9ea7e..f98a73a962126 100644 --- a/coderd/provisionerdaemons.go +++ b/coderd/provisionerdaemons.go @@ -437,6 +437,30 @@ func (server *provisionerdServer) CompleteJob(ctx context.Context, completed *pr switch jobType := completed.Type.(type) { case *proto.CompletedJob_ProjectImport_: + for transition, resources := range map[database.WorkspaceTransition][]*sdkproto.Resource{ + database.WorkspaceTransitionStart: jobType.ProjectImport.StartResources, + database.WorkspaceTransitionStop: jobType.ProjectImport.StopResources, + } { + for _, resource := range resources { + server.Logger.Info(ctx, "inserting project import job resource", + slog.F("job_id", job.ID.String()), + slog.F("resource_name", resource.Name), + slog.F("resource_type", resource.Type), + slog.F("transition", transition)) + _, err = server.Database.InsertProjectImportJobResource(ctx, database.InsertProjectImportJobResourceParams{ + ID: uuid.New(), + CreatedAt: database.Now(), + JobID: jobID, + Transition: transition, + Type: resource.Type, + Name: resource.Name, + }) + if err != nil { + return nil, xerrors.Errorf("insert resource: %w", err) + } + } + } + err = server.Database.UpdateProvisionerJobWithCompleteByID(ctx, database.UpdateProvisionerJobWithCompleteByIDParams{ ID: jobID, UpdatedAt: database.Now(), diff --git a/coderd/provisionerjobs.go b/coderd/provisionerjobs.go index dce331d153e6a..9c000280f7228 100644 --- a/coderd/provisionerjobs.go +++ b/coderd/provisionerjobs.go @@ -16,6 +16,8 @@ import ( "github.com/coder/coder/httpmw" ) +type ProjectImportJobResource database.ProjectImportJobResource + type ProvisionerJobStatus string // Completed returns whether the job is still processing. @@ -125,9 +127,9 @@ func (api *api) postProvisionerImportJobByOrganization(rw http.ResponseWriter, r // Return parsed parameter schemas for a job. func (api *api) provisionerJobParameterSchemasByID(rw http.ResponseWriter, r *http.Request) { job := httpmw.ProvisionerJobParam(r) - if convertProvisionerJob(job).Status != ProvisionerJobStatusSucceeded { + if !convertProvisionerJob(job).Status.Completed() { httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ - Message: fmt.Sprintf("Job is in state %q! Must be %q.", convertProvisionerJob(job).Status, ProvisionerJobStatusSucceeded), + Message: "Job hasn't completed!", }) return } @@ -150,9 +152,9 @@ func (api *api) provisionerJobParameterSchemasByID(rw http.ResponseWriter, r *ht func (api *api) provisionerJobComputedParametersByID(rw http.ResponseWriter, r *http.Request) { apiKey := httpmw.APIKey(r) job := httpmw.ProvisionerJobParam(r) - if convertProvisionerJob(job).Status != ProvisionerJobStatusSucceeded { + if !convertProvisionerJob(job).Status.Completed() { httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ - Message: fmt.Sprintf("Job is in state %q! Must be %q.", convertProvisionerJob(job).Status, ProvisionerJobStatusSucceeded), + Message: "Job hasn't completed!", }) return } @@ -163,6 +165,29 @@ func (api *api) provisionerJobComputedParametersByID(rw http.ResponseWriter, r * }) } +func (api *api) provisionerJobResourcesByID(rw http.ResponseWriter, r *http.Request) { + job := httpmw.ProvisionerJobParam(r) + if !convertProvisionerJob(job).Status.Completed() { + httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + Message: "Job hasn't completed!", + }) + return + } + resources, err := api.Database.GetProjectImportJobResourcesByJobID(r.Context(), job.ID) + if errors.Is(err, sql.ErrNoRows) { + err = nil + } + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: fmt.Sprintf("get project import job resources: %s", err), + }) + return + } + + render.Status(r, http.StatusOK) + render.JSON(rw, r, resources) +} + func convertProvisionerJob(provisionerJob database.ProvisionerJob) ProvisionerJob { job := ProvisionerJob{ ID: provisionerJob.ID, diff --git a/coderd/provisionerjobs_test.go b/coderd/provisionerjobs_test.go index f0bb167d5e9f5..bb4831362a3bf 100644 --- a/coderd/provisionerjobs_test.go +++ b/coderd/provisionerjobs_test.go @@ -10,6 +10,7 @@ import ( "github.com/coder/coder/coderd" "github.com/coder/coder/coderd/coderdtest" + "github.com/coder/coder/coderd/parameter" "github.com/coder/coder/codersdk" "github.com/coder/coder/database" "github.com/coder/coder/provisioner/echo" @@ -170,3 +171,39 @@ func TestProvisionerJobParametersByID(t *testing.T) { require.Equal(t, params[0].SourceValue, "") }) } + +func TestProvisionerJobResourcesByID(t *testing.T) { + t.Parallel() + t.Run("Something", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + user := coderdtest.CreateInitialUser(t, client) + _ = coderdtest.NewProvisionerDaemon(t, client) + 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{{ + Name: parameter.CoderWorkspaceTransition, + }}, + }, + }, + }}, + Provision: []*proto.Provision_Response{{ + Type: &proto.Provision_Response_Complete{ + Complete: &proto.Provision_Complete{ + Resources: []*proto.Resource{{ + Name: "hello", + Type: "ec2_instance", + }}, + }, + }, + }}, + }) + coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) + resources, err := client.ProvisionerJobResources(context.Background(), user.Organization, job.ID) + require.NoError(t, err) + // One for start, and one for stop! + require.Len(t, resources, 2) + }) +} diff --git a/codersdk/provisioners.go b/codersdk/provisioners.go index ddb84b9b34688..c77df205686b1 100644 --- a/codersdk/provisioners.go +++ b/codersdk/provisioners.go @@ -179,3 +179,16 @@ func (c *Client) ProvisionerJobParameterValues(ctx context.Context, organization var params []coderd.ComputedParameterValue return params, json.NewDecoder(res.Body).Decode(¶ms) } + +func (c *Client) ProvisionerJobResources(ctx context.Context, organization string, job uuid.UUID) ([]coderd.ProjectImportJobResource, error) { + res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/provisioners/jobs/%s/%s/resources", organization, job), nil) + if err != nil { + return nil, err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return nil, readBodyAsError(res) + } + var resources []coderd.ProjectImportJobResource + return resources, json.NewDecoder(res.Body).Decode(&resources) +} diff --git a/database/databasefake/databasefake.go b/database/databasefake/databasefake.go index bd0ac62b92fb1..a0a2f4359839e 100644 --- a/database/databasefake/databasefake.go +++ b/database/databasefake/databasefake.go @@ -401,6 +401,23 @@ func (q *fakeQuerier) GetProjectByOrganizationAndName(_ context.Context, arg dat return database.Project{}, sql.ErrNoRows } +func (q *fakeQuerier) GetProjectImportJobResourcesByJobID(ctx context.Context, jobID uuid.UUID) ([]database.ProjectImportJobResource, error) { + q.mutex.Lock() + defer q.mutex.Unlock() + + resources := make([]database.ProjectImportJobResource, 0) + for _, resource := range q.projectImportJobResource { + if resource.JobID.String() != jobID.String() { + continue + } + resources = append(resources, resource) + } + if len(resources) == 0 { + return nil, sql.ErrNoRows + } + return resources, nil +} + func (q *fakeQuerier) GetProjectVersionsByProjectID(_ context.Context, projectID uuid.UUID) ([]database.ProjectVersion, error) { q.mutex.Lock() defer q.mutex.Unlock() diff --git a/database/querier.go b/database/querier.go index d9586447b9f57..93ba57d931e93 100644 --- a/database/querier.go +++ b/database/querier.go @@ -20,6 +20,7 @@ type querier interface { 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) + GetProjectImportJobResourcesByJobID(ctx context.Context, jobID uuid.UUID) ([]ProjectImportJobResource, error) GetProjectVersionByID(ctx context.Context, id uuid.UUID) (ProjectVersion, error) GetProjectVersionByProjectIDAndName(ctx context.Context, arg GetProjectVersionByProjectIDAndNameParams) (ProjectVersion, error) GetProjectVersionsByProjectID(ctx context.Context, projectID uuid.UUID) ([]ProjectVersion, error) diff --git a/database/query.sql b/database/query.sql index b1c443e934df9..2bffab68704d2 100644 --- a/database/query.sql +++ b/database/query.sql @@ -173,6 +173,14 @@ FROM WHERE job_id = $1; +-- name: GetProjectImportJobResourcesByJobID :many +SELECT + * +FROM + project_import_job_resource +WHERE + job_id = $1; + -- name: GetProjectVersionsByProjectID :many SELECT * diff --git a/database/query.sql.go b/database/query.sql.go index 71ff91f091f79..5b2f3d8bae011 100644 --- a/database/query.sql.go +++ b/database/query.sql.go @@ -424,6 +424,45 @@ func (q *sqlQuerier) GetProjectByOrganizationAndName(ctx context.Context, arg Ge return i, err } +const getProjectImportJobResourcesByJobID = `-- name: GetProjectImportJobResourcesByJobID :many +SELECT + id, created_at, job_id, transition, type, name +FROM + project_import_job_resource +WHERE + job_id = $1 +` + +func (q *sqlQuerier) GetProjectImportJobResourcesByJobID(ctx context.Context, jobID uuid.UUID) ([]ProjectImportJobResource, error) { + rows, err := q.db.QueryContext(ctx, getProjectImportJobResourcesByJobID, jobID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []ProjectImportJobResource + for rows.Next() { + var i ProjectImportJobResource + if err := rows.Scan( + &i.ID, + &i.CreatedAt, + &i.JobID, + &i.Transition, + &i.Type, + &i.Name, + ); 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 getProjectVersionByID = `-- name: GetProjectVersionByID :one SELECT id, project_id, created_at, updated_at, name, description, import_job_id diff --git a/provisionerd/proto/provisionerd.pb.go b/provisionerd/proto/provisionerd.pb.go index 0322a2f163b05..1c3db72fd341b 100644 --- a/provisionerd/proto/provisionerd.pb.go +++ b/provisionerd/proto/provisionerd.pb.go @@ -283,66 +283,6 @@ func (x *CancelledJob) GetError() string { return "" } -// TransitionedResource represents a resource that knows whether -// it's existence is dependent on stop or not. -// -// This is used on import to display start + stopped resources -// for the lifecycle of a workspace. -type TransitionedResource struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Resource *proto.Resource `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` - DestroyOnStop bool `protobuf:"varint,2,opt,name=destroy_on_stop,json=destroyOnStop,proto3" json:"destroy_on_stop,omitempty"` -} - -func (x *TransitionedResource) Reset() { - *x = TransitionedResource{} - if protoimpl.UnsafeEnabled { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TransitionedResource) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TransitionedResource) ProtoMessage() {} - -func (x *TransitionedResource) ProtoReflect() protoreflect.Message { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TransitionedResource.ProtoReflect.Descriptor instead. -func (*TransitionedResource) Descriptor() ([]byte, []int) { - return file_provisionerd_proto_provisionerd_proto_rawDescGZIP(), []int{3} -} - -func (x *TransitionedResource) GetResource() *proto.Resource { - if x != nil { - return x.Resource - } - return nil -} - -func (x *TransitionedResource) GetDestroyOnStop() bool { - if x != nil { - return x.DestroyOnStop - } - return false -} - // CompletedJob is sent when the provisioner daemon completes a job. type CompletedJob struct { state protoimpl.MessageState @@ -359,7 +299,7 @@ type CompletedJob struct { func (x *CompletedJob) Reset() { *x = CompletedJob{} if protoimpl.UnsafeEnabled { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[4] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -372,7 +312,7 @@ func (x *CompletedJob) String() string { func (*CompletedJob) ProtoMessage() {} func (x *CompletedJob) ProtoReflect() protoreflect.Message { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[4] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -385,7 +325,7 @@ func (x *CompletedJob) ProtoReflect() protoreflect.Message { // Deprecated: Use CompletedJob.ProtoReflect.Descriptor instead. func (*CompletedJob) Descriptor() ([]byte, []int) { - return file_provisionerd_proto_provisionerd_proto_rawDescGZIP(), []int{4} + return file_provisionerd_proto_provisionerd_proto_rawDescGZIP(), []int{3} } func (x *CompletedJob) GetJobId() string { @@ -447,7 +387,7 @@ type Log struct { func (x *Log) Reset() { *x = Log{} if protoimpl.UnsafeEnabled { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[5] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -460,7 +400,7 @@ func (x *Log) String() string { func (*Log) ProtoMessage() {} func (x *Log) ProtoReflect() protoreflect.Message { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[5] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -473,7 +413,7 @@ func (x *Log) ProtoReflect() protoreflect.Message { // Deprecated: Use Log.ProtoReflect.Descriptor instead. func (*Log) Descriptor() ([]byte, []int) { - return file_provisionerd_proto_provisionerd_proto_rawDescGZIP(), []int{5} + return file_provisionerd_proto_provisionerd_proto_rawDescGZIP(), []int{4} } func (x *Log) GetSource() LogSource { @@ -518,7 +458,7 @@ type UpdateJobRequest struct { func (x *UpdateJobRequest) Reset() { *x = UpdateJobRequest{} if protoimpl.UnsafeEnabled { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[6] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -531,7 +471,7 @@ func (x *UpdateJobRequest) String() string { func (*UpdateJobRequest) ProtoMessage() {} func (x *UpdateJobRequest) ProtoReflect() protoreflect.Message { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[6] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -544,7 +484,7 @@ func (x *UpdateJobRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateJobRequest.ProtoReflect.Descriptor instead. func (*UpdateJobRequest) Descriptor() ([]byte, []int) { - return file_provisionerd_proto_provisionerd_proto_rawDescGZIP(), []int{6} + return file_provisionerd_proto_provisionerd_proto_rawDescGZIP(), []int{5} } func (x *UpdateJobRequest) GetJobId() string { @@ -581,7 +521,7 @@ type UpdateJobResponse struct { func (x *UpdateJobResponse) Reset() { *x = UpdateJobResponse{} if protoimpl.UnsafeEnabled { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[7] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -594,7 +534,7 @@ func (x *UpdateJobResponse) String() string { func (*UpdateJobResponse) ProtoMessage() {} func (x *UpdateJobResponse) ProtoReflect() protoreflect.Message { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[7] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -607,7 +547,7 @@ func (x *UpdateJobResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateJobResponse.ProtoReflect.Descriptor instead. func (*UpdateJobResponse) Descriptor() ([]byte, []int) { - return file_provisionerd_proto_provisionerd_proto_rawDescGZIP(), []int{7} + return file_provisionerd_proto_provisionerd_proto_rawDescGZIP(), []int{6} } func (x *UpdateJobResponse) GetParameterValues() []*proto.ParameterValue { @@ -631,7 +571,7 @@ type AcquiredJob_WorkspaceProvision struct { func (x *AcquiredJob_WorkspaceProvision) Reset() { *x = AcquiredJob_WorkspaceProvision{} if protoimpl.UnsafeEnabled { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[8] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -644,7 +584,7 @@ func (x *AcquiredJob_WorkspaceProvision) String() string { func (*AcquiredJob_WorkspaceProvision) ProtoMessage() {} func (x *AcquiredJob_WorkspaceProvision) ProtoReflect() protoreflect.Message { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[8] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -697,7 +637,7 @@ type AcquiredJob_ProjectImport struct { func (x *AcquiredJob_ProjectImport) Reset() { *x = AcquiredJob_ProjectImport{} if protoimpl.UnsafeEnabled { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[9] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -710,7 +650,7 @@ func (x *AcquiredJob_ProjectImport) String() string { func (*AcquiredJob_ProjectImport) ProtoMessage() {} func (x *AcquiredJob_ProjectImport) ProtoReflect() protoreflect.Message { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[9] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -738,7 +678,7 @@ type CompletedJob_WorkspaceProvision struct { func (x *CompletedJob_WorkspaceProvision) Reset() { *x = CompletedJob_WorkspaceProvision{} if protoimpl.UnsafeEnabled { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[10] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -751,7 +691,7 @@ func (x *CompletedJob_WorkspaceProvision) String() string { func (*CompletedJob_WorkspaceProvision) ProtoMessage() {} func (x *CompletedJob_WorkspaceProvision) ProtoReflect() protoreflect.Message { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[10] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -764,7 +704,7 @@ func (x *CompletedJob_WorkspaceProvision) ProtoReflect() protoreflect.Message { // Deprecated: Use CompletedJob_WorkspaceProvision.ProtoReflect.Descriptor instead. func (*CompletedJob_WorkspaceProvision) Descriptor() ([]byte, []int) { - return file_provisionerd_proto_provisionerd_proto_rawDescGZIP(), []int{4, 0} + return file_provisionerd_proto_provisionerd_proto_rawDescGZIP(), []int{3, 0} } func (x *CompletedJob_WorkspaceProvision) GetState() []byte { @@ -793,7 +733,7 @@ type CompletedJob_ProjectImport struct { func (x *CompletedJob_ProjectImport) Reset() { *x = CompletedJob_ProjectImport{} if protoimpl.UnsafeEnabled { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[11] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -806,7 +746,7 @@ func (x *CompletedJob_ProjectImport) String() string { func (*CompletedJob_ProjectImport) ProtoMessage() {} func (x *CompletedJob_ProjectImport) ProtoReflect() protoreflect.Message { - mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[11] + mi := &file_provisionerd_proto_provisionerd_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -819,7 +759,7 @@ func (x *CompletedJob_ProjectImport) ProtoReflect() protoreflect.Message { // Deprecated: Use CompletedJob_ProjectImport.ProtoReflect.Descriptor instead. func (*CompletedJob_ProjectImport) Descriptor() ([]byte, []int) { - return file_provisionerd_proto_provisionerd_proto_rawDescGZIP(), []int{4, 1} + return file_provisionerd_proto_provisionerd_proto_rawDescGZIP(), []int{3, 1} } func (x *CompletedJob_ProjectImport) GetStartResources() []*proto.Resource { @@ -886,94 +826,87 @@ var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{ 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, 0xd3, 0x03, 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, + 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xd3, 0x03, 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, 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, 0x8d, 0x01, 0x0a, 0x0d, 0x50, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x3e, 0x0a, 0x0f, 0x73, - 0x74, 0x61, 0x72, 0x74, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, + 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, 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, 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, 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, 0x9b, - 0x01, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x04, 0x6c, 0x6f, - 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 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, 0x12, 0x49, 0x0a, 0x11, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x73, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x65, 0x74, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x10, 0x70, 0x61, 0x72, 0x61, - 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x22, 0x5b, 0x0a, 0x11, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 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, 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, - 0x9d, 0x02, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x44, - 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x3c, 0x0a, 0x0a, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, - 0x4a, 0x6f, 0x62, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, - 0x4a, 0x6f, 0x62, 0x12, 0x4c, 0x0a, 0x09, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, - 0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, + 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x1a, 0x8d, 0x01, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x3e, 0x0a, 0x0f, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 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, 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, 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, 0x9b, 0x01, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 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, + 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, 0x12, 0x49, + 0x0a, 0x11, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, + 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x10, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, + 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x22, 0x5b, 0x0a, 0x11, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 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, 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, 0x9d, 0x02, 0x0a, + 0x11, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x44, 0x61, 0x65, 0x6d, + 0x6f, 0x6e, 0x12, 0x3c, 0x0a, 0x0a, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x4a, 0x6f, 0x62, + 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x4a, 0x6f, 0x62, + 0x12, 0x4c, 0x0a, 0x09, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1e, 0x2e, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 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 ( @@ -989,54 +922,52 @@ func file_provisionerd_proto_provisionerd_proto_rawDescGZIP() []byte { } var file_provisionerd_proto_provisionerd_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_provisionerd_proto_provisionerd_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_provisionerd_proto_provisionerd_proto_msgTypes = make([]protoimpl.MessageInfo, 11) var file_provisionerd_proto_provisionerd_proto_goTypes = []interface{}{ (LogSource)(0), // 0: provisionerd.LogSource (*Empty)(nil), // 1: provisionerd.Empty (*AcquiredJob)(nil), // 2: provisionerd.AcquiredJob (*CancelledJob)(nil), // 3: provisionerd.CancelledJob - (*TransitionedResource)(nil), // 4: provisionerd.TransitionedResource - (*CompletedJob)(nil), // 5: provisionerd.CompletedJob - (*Log)(nil), // 6: provisionerd.Log - (*UpdateJobRequest)(nil), // 7: provisionerd.UpdateJobRequest - (*UpdateJobResponse)(nil), // 8: provisionerd.UpdateJobResponse - (*AcquiredJob_WorkspaceProvision)(nil), // 9: provisionerd.AcquiredJob.WorkspaceProvision - (*AcquiredJob_ProjectImport)(nil), // 10: provisionerd.AcquiredJob.ProjectImport - (*CompletedJob_WorkspaceProvision)(nil), // 11: provisionerd.CompletedJob.WorkspaceProvision - (*CompletedJob_ProjectImport)(nil), // 12: provisionerd.CompletedJob.ProjectImport - (*proto.Resource)(nil), // 13: provisioner.Resource - (proto.LogLevel)(0), // 14: provisioner.LogLevel - (*proto.ParameterSchema)(nil), // 15: provisioner.ParameterSchema - (*proto.ParameterValue)(nil), // 16: provisioner.ParameterValue + (*CompletedJob)(nil), // 4: provisionerd.CompletedJob + (*Log)(nil), // 5: provisionerd.Log + (*UpdateJobRequest)(nil), // 6: provisionerd.UpdateJobRequest + (*UpdateJobResponse)(nil), // 7: provisionerd.UpdateJobResponse + (*AcquiredJob_WorkspaceProvision)(nil), // 8: provisionerd.AcquiredJob.WorkspaceProvision + (*AcquiredJob_ProjectImport)(nil), // 9: provisionerd.AcquiredJob.ProjectImport + (*CompletedJob_WorkspaceProvision)(nil), // 10: provisionerd.CompletedJob.WorkspaceProvision + (*CompletedJob_ProjectImport)(nil), // 11: provisionerd.CompletedJob.ProjectImport + (proto.LogLevel)(0), // 12: provisioner.LogLevel + (*proto.ParameterSchema)(nil), // 13: provisioner.ParameterSchema + (*proto.ParameterValue)(nil), // 14: provisioner.ParameterValue + (*proto.Resource)(nil), // 15: provisioner.Resource } var file_provisionerd_proto_provisionerd_proto_depIdxs = []int32{ - 9, // 0: provisionerd.AcquiredJob.workspace_provision:type_name -> provisionerd.AcquiredJob.WorkspaceProvision - 10, // 1: provisionerd.AcquiredJob.project_import:type_name -> provisionerd.AcquiredJob.ProjectImport - 13, // 2: provisionerd.TransitionedResource.resource:type_name -> provisioner.Resource - 11, // 3: provisionerd.CompletedJob.workspace_provision:type_name -> provisionerd.CompletedJob.WorkspaceProvision - 12, // 4: provisionerd.CompletedJob.project_import:type_name -> provisionerd.CompletedJob.ProjectImport - 0, // 5: provisionerd.Log.source:type_name -> provisionerd.LogSource - 14, // 6: provisionerd.Log.level:type_name -> provisioner.LogLevel - 6, // 7: provisionerd.UpdateJobRequest.logs:type_name -> provisionerd.Log - 15, // 8: provisionerd.UpdateJobRequest.parameter_schemas:type_name -> provisioner.ParameterSchema - 16, // 9: provisionerd.UpdateJobResponse.parameter_values:type_name -> provisioner.ParameterValue - 16, // 10: provisionerd.AcquiredJob.WorkspaceProvision.parameter_values:type_name -> provisioner.ParameterValue - 13, // 11: provisionerd.CompletedJob.WorkspaceProvision.resources:type_name -> provisioner.Resource - 13, // 12: provisionerd.CompletedJob.ProjectImport.start_resources:type_name -> provisioner.Resource - 13, // 13: provisionerd.CompletedJob.ProjectImport.stop_resources:type_name -> provisioner.Resource - 1, // 14: provisionerd.ProvisionerDaemon.AcquireJob:input_type -> provisionerd.Empty - 7, // 15: provisionerd.ProvisionerDaemon.UpdateJob:input_type -> provisionerd.UpdateJobRequest - 3, // 16: provisionerd.ProvisionerDaemon.CancelJob:input_type -> provisionerd.CancelledJob - 5, // 17: provisionerd.ProvisionerDaemon.CompleteJob:input_type -> provisionerd.CompletedJob - 2, // 18: provisionerd.ProvisionerDaemon.AcquireJob:output_type -> provisionerd.AcquiredJob - 8, // 19: provisionerd.ProvisionerDaemon.UpdateJob:output_type -> provisionerd.UpdateJobResponse - 1, // 20: provisionerd.ProvisionerDaemon.CancelJob:output_type -> provisionerd.Empty - 1, // 21: provisionerd.ProvisionerDaemon.CompleteJob:output_type -> provisionerd.Empty - 18, // [18:22] is the sub-list for method output_type - 14, // [14:18] is the sub-list for method input_type - 14, // [14:14] is the sub-list for extension type_name - 14, // [14:14] is the sub-list for extension extendee - 0, // [0:14] is the sub-list for field type_name + 8, // 0: provisionerd.AcquiredJob.workspace_provision:type_name -> provisionerd.AcquiredJob.WorkspaceProvision + 9, // 1: provisionerd.AcquiredJob.project_import:type_name -> provisionerd.AcquiredJob.ProjectImport + 10, // 2: provisionerd.CompletedJob.workspace_provision:type_name -> provisionerd.CompletedJob.WorkspaceProvision + 11, // 3: provisionerd.CompletedJob.project_import:type_name -> provisionerd.CompletedJob.ProjectImport + 0, // 4: provisionerd.Log.source:type_name -> provisionerd.LogSource + 12, // 5: provisionerd.Log.level:type_name -> provisioner.LogLevel + 5, // 6: provisionerd.UpdateJobRequest.logs:type_name -> provisionerd.Log + 13, // 7: provisionerd.UpdateJobRequest.parameter_schemas:type_name -> provisioner.ParameterSchema + 14, // 8: provisionerd.UpdateJobResponse.parameter_values:type_name -> provisioner.ParameterValue + 14, // 9: provisionerd.AcquiredJob.WorkspaceProvision.parameter_values:type_name -> provisioner.ParameterValue + 15, // 10: provisionerd.CompletedJob.WorkspaceProvision.resources:type_name -> provisioner.Resource + 15, // 11: provisionerd.CompletedJob.ProjectImport.start_resources:type_name -> provisioner.Resource + 15, // 12: provisionerd.CompletedJob.ProjectImport.stop_resources:type_name -> provisioner.Resource + 1, // 13: provisionerd.ProvisionerDaemon.AcquireJob:input_type -> provisionerd.Empty + 6, // 14: provisionerd.ProvisionerDaemon.UpdateJob:input_type -> provisionerd.UpdateJobRequest + 3, // 15: provisionerd.ProvisionerDaemon.CancelJob:input_type -> provisionerd.CancelledJob + 4, // 16: provisionerd.ProvisionerDaemon.CompleteJob:input_type -> provisionerd.CompletedJob + 2, // 17: provisionerd.ProvisionerDaemon.AcquireJob:output_type -> provisionerd.AcquiredJob + 7, // 18: provisionerd.ProvisionerDaemon.UpdateJob:output_type -> provisionerd.UpdateJobResponse + 1, // 19: provisionerd.ProvisionerDaemon.CancelJob:output_type -> provisionerd.Empty + 1, // 20: provisionerd.ProvisionerDaemon.CompleteJob:output_type -> provisionerd.Empty + 17, // [17:21] is the sub-list for method output_type + 13, // [13:17] is the sub-list for method input_type + 13, // [13:13] is the sub-list for extension type_name + 13, // [13:13] is the sub-list for extension extendee + 0, // [0:13] is the sub-list for field type_name } func init() { file_provisionerd_proto_provisionerd_proto_init() } @@ -1082,18 +1013,6 @@ func file_provisionerd_proto_provisionerd_proto_init() { } } file_provisionerd_proto_provisionerd_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TransitionedResource); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_provisionerd_proto_provisionerd_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CompletedJob); i { case 0: return &v.state @@ -1105,7 +1024,7 @@ func file_provisionerd_proto_provisionerd_proto_init() { return nil } } - file_provisionerd_proto_provisionerd_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + file_provisionerd_proto_provisionerd_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Log); i { case 0: return &v.state @@ -1117,7 +1036,7 @@ func file_provisionerd_proto_provisionerd_proto_init() { return nil } } - file_provisionerd_proto_provisionerd_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + file_provisionerd_proto_provisionerd_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UpdateJobRequest); i { case 0: return &v.state @@ -1129,7 +1048,7 @@ func file_provisionerd_proto_provisionerd_proto_init() { return nil } } - file_provisionerd_proto_provisionerd_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + file_provisionerd_proto_provisionerd_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UpdateJobResponse); i { case 0: return &v.state @@ -1141,7 +1060,7 @@ func file_provisionerd_proto_provisionerd_proto_init() { return nil } } - file_provisionerd_proto_provisionerd_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + file_provisionerd_proto_provisionerd_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AcquiredJob_WorkspaceProvision); i { case 0: return &v.state @@ -1153,7 +1072,7 @@ func file_provisionerd_proto_provisionerd_proto_init() { return nil } } - file_provisionerd_proto_provisionerd_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + file_provisionerd_proto_provisionerd_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AcquiredJob_ProjectImport); i { case 0: return &v.state @@ -1165,7 +1084,7 @@ func file_provisionerd_proto_provisionerd_proto_init() { return nil } } - file_provisionerd_proto_provisionerd_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + file_provisionerd_proto_provisionerd_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CompletedJob_WorkspaceProvision); i { case 0: return &v.state @@ -1177,7 +1096,7 @@ func file_provisionerd_proto_provisionerd_proto_init() { return nil } } - file_provisionerd_proto_provisionerd_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + file_provisionerd_proto_provisionerd_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CompletedJob_ProjectImport); i { case 0: return &v.state @@ -1194,7 +1113,7 @@ func file_provisionerd_proto_provisionerd_proto_init() { (*AcquiredJob_WorkspaceProvision_)(nil), (*AcquiredJob_ProjectImport_)(nil), } - file_provisionerd_proto_provisionerd_proto_msgTypes[4].OneofWrappers = []interface{}{ + file_provisionerd_proto_provisionerd_proto_msgTypes[3].OneofWrappers = []interface{}{ (*CompletedJob_WorkspaceProvision_)(nil), (*CompletedJob_ProjectImport_)(nil), } @@ -1204,7 +1123,7 @@ func file_provisionerd_proto_provisionerd_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_provisionerd_proto_provisionerd_proto_rawDesc, NumEnums: 1, - NumMessages: 12, + NumMessages: 11, NumExtensions: 0, NumServices: 1, }, diff --git a/provisionerd/provisionerd.go b/provisionerd/provisionerd.go index bf50a8cc3281d..4c438d7360f66 100644 --- a/provisionerd/provisionerd.go +++ b/provisionerd/provisionerd.go @@ -20,6 +20,7 @@ import ( "cdr.dev/slog" "github.com/coder/coder/coderd/parameter" + "github.com/coder/coder/database" "github.com/coder/coder/provisionerd/proto" sdkproto "github.com/coder/coder/provisionersdk/proto" "github.com/coder/retry" @@ -391,7 +392,7 @@ func (p *provisionerDaemon) runProjectImport(ctx context.Context, provisioner sd startParameters = append(updateResponse.ParameterValues, &sdkproto.ParameterValue{ DestinationScheme: sdkproto.ParameterDestination_PROVISIONER_VARIABLE, Name: parameter.CoderWorkspaceTransition, - Value: "start", + Value: string(database.WorkspaceTransitionStart), }) } startResources, err := p.runProjectImportProvision(ctx, provisioner, job, startParameters) @@ -404,7 +405,7 @@ func (p *provisionerDaemon) runProjectImport(ctx context.Context, provisioner sd stopResources, err = p.runProjectImportProvision(ctx, provisioner, job, append(updateResponse.ParameterValues, &sdkproto.ParameterValue{ DestinationScheme: sdkproto.ParameterDestination_PROVISIONER_VARIABLE, Name: "coder_workspace_transition", - Value: "stop", + Value: string(database.WorkspaceTransitionStop), })) if err != nil { p.cancelActiveJobf("project import provision for start: %s", err) From dc86c0e8cf1d221a2f5c81a4b0701ab7a43d25fa Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 10 Feb 2022 23:06:07 +0000 Subject: [PATCH 09/20] Create project fully works!!! --- cli/projectcreate.go | 273 ++++++++++++++------------ provisioner/terraform/parse.go | 6 +- provisioner/terraform/parse_test.go | 9 +- provisionerd/proto/provisionerd.proto | 10 - 4 files changed, 162 insertions(+), 136 deletions(-) diff --git a/cli/projectcreate.go b/cli/projectcreate.go index 00da8d0e39143..fdfd8c43b489f 100644 --- a/cli/projectcreate.go +++ b/cli/projectcreate.go @@ -7,6 +7,7 @@ import ( "io" "os" "path/filepath" + "strings" "time" "github.com/briandowns/spinner" @@ -14,9 +15,11 @@ import ( "github.com/google/uuid" "github.com/manifoldco/promptui" "github.com/spf13/cobra" + "github.com/xlab/treeprint" "golang.org/x/xerrors" "github.com/coder/coder/coderd" + "github.com/coder/coder/coderd/parameter" "github.com/coder/coder/codersdk" "github.com/coder/coder/database" "github.com/coder/coder/provisionerd" @@ -62,150 +65,178 @@ func projectCreate() *cobra.Command { return err } - spin := spinner.New(spinner.CharSets[0], 25*time.Millisecond) - spin.Suffix = " Uploading current directory..." - spin.Start() - - defer spin.Stop() - - bytes, err := tarDirectory(directory) + job, err := doProjectLoop(cmd, client, organization, directory, []coderd.CreateParameterValueRequest{}) if err != nil { return err } - - resp, err := client.UploadFile(cmd.Context(), codersdk.ContentTypeTar, bytes) + project, err := client.CreateProject(cmd.Context(), organization.Name, coderd.CreateProjectRequest{ + Name: name, + VersionImportJobID: job.ID, + }) if err != nil { return err } - job, err := client.CreateProjectVersionImportProvisionerJob(cmd.Context(), organization.Name, coderd.CreateProjectImportJobRequest{ - StorageMethod: database.ProvisionerStorageMethodFile, - StorageSource: resp.Hash, - Provisioner: database.ProvisionerTypeTerraform, + _, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s The %s project has been created!\n", color.HiBlackString(">"), color.HiCyanString(project.Name)) + _, err = runPrompt(cmd, &promptui.Prompt{ + Label: "Create a new workspace?", + IsConfirm: true, + Default: "y", }) if err != nil { return err } - spin.Stop() - logs, err := client.FollowProvisionerJobLogsAfter(cmd.Context(), organization.Name, job.ID, time.Time{}) - if err != nil { - return err - } - for { - log, ok := <-logs - if !ok { - break - } - _, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s %s\n", color.HiGreenString("[tf]"), log.Output) - } + fmt.Printf("Create a new workspace now!\n") + return nil + }, + } + currentDirectory, _ := os.Getwd() + cmd.Flags().StringVarP(&directory, "directory", "d", currentDirectory, "Specify the directory to create from") - job, err = client.ProvisionerJob(cmd.Context(), organization.Name, job.ID) - if err != nil { - return err - } + return cmd +} - if provisionerd.IsMissingParameterError(job.Error) { - fmt.Printf("Missing something!\n") - return nil - } +func doProjectLoop(cmd *cobra.Command, client *codersdk.Client, organization coderd.Organization, directory string, params []coderd.CreateParameterValueRequest) (*coderd.ProvisionerJob, error) { + spin := spinner.New(spinner.CharSets[5], 100*time.Millisecond) + spin.Writer = cmd.OutOrStdout() + spin.Suffix = " Uploading current directory..." + spin.Color("fgHiGreen") + spin.Start() + defer spin.Stop() - resources, err := client.ProvisionerJobResources(cmd.Context(), organization.Name, job.ID) - if err != nil { - return err - } + bytes, err := tarDirectory(directory) + if err != nil { + return nil, err + } + + resp, err := client.UploadFile(cmd.Context(), codersdk.ContentTypeTar, bytes) + if err != nil { + return nil, err + } - fmt.Printf("Resources: %+v\n", resources) + job, err := client.CreateProjectVersionImportProvisionerJob(cmd.Context(), organization.Name, coderd.CreateProjectImportJobRequest{ + StorageMethod: database.ProvisionerStorageMethodFile, + StorageSource: resp.Hash, + Provisioner: database.ProvisionerTypeTerraform, + ParameterValues: params, + }) + if err != nil { + return nil, err + } - project, err := client.CreateProject(cmd.Context(), organization.Name, coderd.CreateProjectRequest{ - Name: name, - VersionImportJobID: job.ID, + spin.Suffix = " Waiting for the import to complete..." + + logs, err := client.FollowProvisionerJobLogsAfter(cmd.Context(), organization.Name, job.ID, time.Time{}) + if err != nil { + return nil, err + } + for { + _, ok := <-logs + if !ok { + break + } + // _, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s %s\n", color.HiGreenString("[tf]"), log.Output) + } + + job, err = client.ProvisionerJob(cmd.Context(), organization.Name, job.ID) + if err != nil { + return nil, err + } + + parameterSchemas, err := client.ProvisionerJobParameterSchemas(cmd.Context(), organization.Name, job.ID) + if err != nil { + return nil, err + } + parameterValues, err := client.ProvisionerJobParameterValues(cmd.Context(), organization.Name, job.ID) + if err != nil { + return nil, err + } + spin.Stop() + + if provisionerd.IsMissingParameterError(job.Error) { + valuesBySchemaID := map[string]coderd.ComputedParameterValue{} + for _, parameterValue := range parameterValues { + valuesBySchemaID[parameterValue.SchemaID.String()] = parameterValue + } + for _, parameterSchema := range parameterSchemas { + _, ok := valuesBySchemaID[parameterSchema.ID.String()] + if ok { + continue + } + if parameterSchema.Name == parameter.CoderWorkspaceTransition { + continue + } + value, err := runPrompt(cmd, &promptui.Prompt{ + Label: fmt.Sprintf("Enter value for %s:", color.HiCyanString(parameterSchema.Name)), }) if err != nil { - return err + return nil, err } + params = append(params, coderd.CreateParameterValueRequest{ + Name: parameterSchema.Name, + SourceValue: value, + SourceScheme: database.ParameterSourceSchemeData, + DestinationScheme: parameterSchema.DefaultDestinationScheme, + }) + } + return doProjectLoop(cmd, client, organization, directory, params) + } - fmt.Printf("Project: %+v\n", project) - - // _, _ = fmt.Fprintf(cmd.OutOrStdout(), "Parsed project source... displaying parameters:") - - // schemas, err := client.ProvisionerJobParameterSchemas(cmd.Context(), organization.Name, job.ID) - // if err != nil { - // return err - // } - - // values, err := client.ProvisionerJobParameterValues(cmd.Context(), organization.Name, job.ID) - // if err != nil { - // return err - // } - // valueBySchemaID := map[string]coderd.ComputedParameterValue{} - // for _, value := range values { - // valueBySchemaID[value.SchemaID.String()] = value - // } - - // for _, schema := range schemas { - // if value, ok := valueBySchemaID[schema.ID.String()]; ok { - // fmt.Printf("Value for: %s %s\n", value.Name, value.SourceValue) - // continue - // } - // fmt.Printf("No value for: %s\n", schema.Name) - // } - - // schemas, err := client.ProvisionerJobParameterSchemas(cmd.Context(), organization.Name, job.ID) - // if err != nil { - // return err - // } - // _, _ = fmt.Fprintf(cmd.OutOrStdout(), "\n %s\n\n", color.HiBlackString("Parameters")) - - // for _, param := range params { - // if param.Value == nil { - // _, _ = fmt.Fprintf(cmd.OutOrStdout(), " %s = must be set\n", color.HiRedString(param.Schema.Name)) - // continue - // } - // value := param.Value.DestinationValue - // if !param.Schema.RedisplayValue { - // value = "" - // } - // output := fmt.Sprintf(" %s = %s", color.HiGreenString(param.Value.SourceValue), color.CyanString(value)) - // param.Value.DefaultSourceValue = false - // param.Value.Scope = database.ParameterScopeOrganization - // param.Value.ScopeID = organization.ID - // if param.Value.DefaultSourceValue { - // output += " (default value)" - // } else { - // output += fmt.Sprintf(" (inherited from %s)", param.Value.Scope) - // } - // root := treeprint.NewWithRoot(output) - // root.AddNode(color.HiBlackString("Description") + "\n" + param.Schema.Description) - // fmt.Fprintln(cmd.OutOrStdout(), strings.Join(strings.Split(root.String(), "\n"), "\n ")) - // } - - // for _, param := range params { - // if param.Value != nil { - // continue - // } - - // value, err := runPrompt(cmd, &promptui.Prompt{ - // Label: "Specify value for " + color.HiCyanString(param.Schema.Name), - // Validate: func(s string) error { - // // param.Schema.Vali - // return nil - // }, - // }) - // if err != nil { - // continue - // } - // fmt.Printf(": %s\n", value) - // } - - _, _ = fmt.Fprintf(cmd.OutOrStdout(), "Create project %q!\n", name) - return nil - }, + if job.Status != coderd.ProvisionerJobStatusSucceeded { + return nil, xerrors.New(job.Error) } - currentDirectory, _ := os.Getwd() - cmd.Flags().StringVarP(&directory, "directory", "d", currentDirectory, "Specify the directory to create from") - return cmd + _, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Successfully imported project source!\n", color.HiGreenString("✓")) + + resources, err := client.ProvisionerJobResources(cmd.Context(), organization.Name, job.ID) + if err != nil { + return nil, err + } + return &job, outputProjectInformation(cmd, parameterSchemas, parameterValues, resources) +} + +func outputProjectInformation(cmd *cobra.Command, parameterSchemas []coderd.ParameterSchema, parameterValues []coderd.ComputedParameterValue, resources []coderd.ProjectImportJobResource) error { + schemaByID := map[string]coderd.ParameterSchema{} + for _, schema := range parameterSchemas { + schemaByID[schema.ID.String()] = schema + } + + _, _ = fmt.Fprintf(cmd.OutOrStdout(), "\n %s\n\n", color.HiBlackString("Parameters")) + for _, value := range parameterValues { + schema, ok := schemaByID[value.SchemaID.String()] + if !ok { + return xerrors.Errorf("schema not found: %s", value.Name) + } + displayValue := value.SourceValue + if !schema.RedisplayValue { + displayValue = "" + } + output := fmt.Sprintf("%s %s %s", color.HiCyanString(value.Name), color.HiBlackString("="), displayValue) + if value.DefaultSourceValue { + output += " (default value)" + } else if value.Scope != database.ParameterScopeImportJob { + output += fmt.Sprintf(" (inherited from %s)", value.Scope) + } + + root := treeprint.NewWithRoot(output) + if schema.Description != "" { + root.AddBranch(fmt.Sprintf("%s\n%s\n", color.HiBlackString("Description"), schema.Description)) + } + if schema.AllowOverrideSource { + root.AddBranch(fmt.Sprintf("%s Users can customize this value!", color.HiYellowString("+"))) + } + _, _ = fmt.Fprintln(cmd.OutOrStdout(), " "+strings.Join(strings.Split(root.String(), "\n"), "\n ")) + } + _, _ = fmt.Fprintf(cmd.OutOrStdout(), " %s\n\n", color.HiBlackString("Resources")) + for _, resource := range resources { + transition := color.HiGreenString("start") + if resource.Transition == database.WorkspaceTransitionStop { + transition = color.HiRedString("stop") + } + _, _ = fmt.Fprintf(cmd.OutOrStdout(), " %s %s on %s\n\n", color.HiCyanString(resource.Type), color.HiCyanString(resource.Name), transition) + } + return nil } func tarDirectory(directory string) ([]byte, error) { diff --git a/provisioner/terraform/parse.go b/provisioner/terraform/parse.go index 11efa03a3ec6b..dcdf77fa3a874 100644 --- a/provisioner/terraform/parse.go +++ b/provisioner/terraform/parse.go @@ -46,6 +46,9 @@ func convertVariableToParameter(variable *tfconfig.Variable) (*proto.ParameterSc Description: variable.Description, RedisplayValue: !variable.Sensitive, ValidationValueType: variable.Type, + DefaultDestination: &proto.ParameterDestination{ + Scheme: proto.ParameterDestination_PROVISIONER_VARIABLE, + }, } if variable.Default != nil { @@ -57,9 +60,6 @@ func convertVariableToParameter(variable *tfconfig.Variable) (*proto.ParameterSc Scheme: proto.ParameterSource_DATA, Value: string(defaultData), } - schema.DefaultDestination = &proto.ParameterDestination{ - Scheme: proto.ParameterDestination_PROVISIONER_VARIABLE, - } } if len(variable.Validations) > 0 && variable.Validations[0].Condition != nil { diff --git a/provisioner/terraform/parse_test.go b/provisioner/terraform/parse_test.go index faf3860a2844a..f77882db6b9bc 100644 --- a/provisioner/terraform/parse_test.go +++ b/provisioner/terraform/parse_test.go @@ -55,6 +55,9 @@ func TestParse(t *testing.T) { Name: "A", RedisplayValue: true, Description: "Testing!", + DefaultDestination: &proto.ParameterDestination{ + Scheme: proto.ParameterDestination_PROVISIONER_VARIABLE, + }, }}, }, }, @@ -100,8 +103,10 @@ func TestParse(t *testing.T) { RedisplayValue: true, ValidationCondition: `var.A == "value"`, ValidationTypeSystem: proto.ParameterSchema_HCL, - }, - }, + DefaultDestination: &proto.ParameterDestination{ + Scheme: proto.ParameterDestination_PROVISIONER_VARIABLE, + }, + }}, }, }, }, diff --git a/provisionerd/proto/provisionerd.proto b/provisionerd/proto/provisionerd.proto index 620d579910e2d..64a37bd3e05ef 100644 --- a/provisionerd/proto/provisionerd.proto +++ b/provisionerd/proto/provisionerd.proto @@ -35,16 +35,6 @@ message CancelledJob { string error = 2; } -// TransitionedResource represents a resource that knows whether -// it's existence is dependent on stop or not. -// -// This is used on import to display start + stopped resources -// for the lifecycle of a workspace. -message TransitionedResource { - provisioner.Resource resource = 1; - bool destroy_on_stop = 2; -} - // CompletedJob is sent when the provisioner daemon completes a job. message CompletedJob { message WorkspaceProvision { From bff96b692b8883ded1191c3ddeaa97d51d32b4ca Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Fri, 11 Feb 2022 03:53:19 +0000 Subject: [PATCH 10/20] Only show job status if completed --- cli/projectcreate.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cli/projectcreate.go b/cli/projectcreate.go index fdfd8c43b489f..766f7fd181e53 100644 --- a/cli/projectcreate.go +++ b/cli/projectcreate.go @@ -131,12 +131,13 @@ func doProjectLoop(cmd *cobra.Command, client *codersdk.Client, organization cod if err != nil { return nil, err } + logBuffer := make([]coderd.ProvisionerJobLog, 0, 64) for { - _, ok := <-logs + log, ok := <-logs if !ok { break } - // _, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s %s\n", color.HiGreenString("[tf]"), log.Output) + logBuffer = append(logBuffer, log) } job, err = client.ProvisionerJob(cmd.Context(), organization.Name, job.ID) @@ -184,6 +185,10 @@ func doProjectLoop(cmd *cobra.Command, client *codersdk.Client, organization cod } if job.Status != coderd.ProvisionerJobStatusSucceeded { + for _, log := range logBuffer { + _, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s %s\n", color.HiGreenString("[tf]"), log.Output) + } + return nil, xerrors.New(job.Error) } From 8766a33cc1a7fc6da593d1d920a5c09df04d2096 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Fri, 11 Feb 2022 05:27:25 +0000 Subject: [PATCH 11/20] Add create workspace support --- .vscode/settings.json | 1 + cli/clitest/clitest.go | 76 +++++++++++++++++++++- cli/login.go | 10 +-- cli/projectcreate.go | 93 +++++++++++---------------- cli/projectcreate_test.go | 70 +++++++++++++++++++-- cli/projects.go | 50 +++++++++++++++ cli/root.go | 6 +- cli/workspacecreate.go | 128 ++++++++++++++++++++++++++++++++++++++ cli/workspaces.go | 1 + 9 files changed, 366 insertions(+), 69 deletions(-) create mode 100644 cli/workspacecreate.go diff --git a/.vscode/settings.json b/.vscode/settings.json index 1c6d6a8f8c189..34ed9fbae2c42 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -44,6 +44,7 @@ "nhooyr", "nolint", "nosec", + "ntqry", "oneof", "parameterscopeid", "promptui", diff --git a/cli/clitest/clitest.go b/cli/clitest/clitest.go index dc390485b0eff..afcda2c5c8e55 100644 --- a/cli/clitest/clitest.go +++ b/cli/clitest/clitest.go @@ -1,21 +1,36 @@ package clitest import ( + "archive/tar" "bufio" + "bytes" "context" + "errors" "io" + "os" + "path/filepath" + "regexp" "testing" "github.com/spf13/cobra" "github.com/stretchr/testify/require" + "golang.org/x/xerrors" "github.com/coder/coder/cli" "github.com/coder/coder/cli/config" "github.com/coder/coder/coderd" "github.com/coder/coder/coderd/coderdtest" "github.com/coder/coder/codersdk" + "github.com/coder/coder/provisioner/echo" ) +var ( + // Used to ensure terminal output doesn't have anything crazy! + stripAnsi = regexp.MustCompile("[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))") +) + +// New creates a CLI instance with a configuration pointed to a +// temporary testing directory. func New(t *testing.T, args ...string) (*cobra.Command, config.Root) { cmd := cli.Root() dir := t.TempDir() @@ -24,6 +39,8 @@ func New(t *testing.T, args ...string) (*cobra.Command, config.Root) { return cmd, root } +// CreateInitialUser creates the initial user and write's the session +// token to the config root provided. func CreateInitialUser(t *testing.T, client *codersdk.Client, root config.Root) coderd.CreateInitialUserRequest { user := coderdtest.CreateInitialUser(t, client) resp, err := client.LoginWithPassword(context.Background(), coderd.LoginWithPasswordRequest{ @@ -38,6 +55,19 @@ func CreateInitialUser(t *testing.T, client *codersdk.Client, root config.Root) return user } +// CreateProjectVersionSource writes the echo provisioner responses into a +// new temporary testing directory. +func CreateProjectVersionSource(t *testing.T, responses *echo.Responses) string { + directory := t.TempDir() + data, err := echo.Tar(responses) + require.NoError(t, err) + err = extractTar(data, directory) + require.NoError(t, err) + return directory +} + +// StdoutLogs provides a writer to t.Log that strips +// all ANSI escape codes. func StdoutLogs(t *testing.T) io.Writer { reader, writer := io.Pipe() scanner := bufio.NewScanner(reader) @@ -50,8 +80,52 @@ func StdoutLogs(t *testing.T) io.Writer { if scanner.Err() != nil { return } - t.Log(scanner.Text()) + t.Log(stripAnsi.ReplaceAllString(scanner.Text(), "")) } }() return writer } + +func extractTar(data []byte, directory string) error { + reader := tar.NewReader(bytes.NewBuffer(data)) + for { + header, err := reader.Next() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + return xerrors.Errorf("read project source archive: %w", err) + } + path := filepath.Join(directory, header.Name) + mode := header.FileInfo().Mode() + if mode == 0 { + mode = 0600 + } + switch header.Typeflag { + case tar.TypeDir: + err = os.MkdirAll(path, mode) + if err != nil { + return xerrors.Errorf("mkdir: %w", err) + } + case tar.TypeReg: + file, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, mode) + if err != nil { + return xerrors.Errorf("create file %q: %w", path, err) + } + // Max file size of 10MB. + _, err = io.CopyN(file, reader, (1<<20)*10) + if errors.Is(err, io.EOF) { + err = nil + } + if err != nil { + _ = file.Close() + return err + } + err = file.Close() + if err != nil { + return err + } + } + } + return nil +} diff --git a/cli/login.go b/cli/login.go index 73758719d0128..a1df7e905d7dd 100644 --- a/cli/login.go +++ b/cli/login.go @@ -49,7 +49,7 @@ func login() *cobra.Command { } _, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Your Coder deployment hasn't been set up!\n", color.HiBlackString(">")) - _, err := runPrompt(cmd, &promptui.Prompt{ + _, err := prompt(cmd, &promptui.Prompt{ Label: "Would you like to create the first user?", IsConfirm: true, Default: "y", @@ -61,7 +61,7 @@ func login() *cobra.Command { if err != nil { return xerrors.Errorf("get current user: %w", err) } - username, err := runPrompt(cmd, &promptui.Prompt{ + username, err := prompt(cmd, &promptui.Prompt{ Label: "What username would you like?", Default: currentUser.Username, }) @@ -69,7 +69,7 @@ func login() *cobra.Command { return xerrors.Errorf("pick username prompt: %w", err) } - organization, err := runPrompt(cmd, &promptui.Prompt{ + organization, err := prompt(cmd, &promptui.Prompt{ Label: "What is the name of your organization?", Default: "acme-corp", }) @@ -77,7 +77,7 @@ func login() *cobra.Command { return xerrors.Errorf("pick organization prompt: %w", err) } - email, err := runPrompt(cmd, &promptui.Prompt{ + email, err := prompt(cmd, &promptui.Prompt{ Label: "What's your email?", Validate: func(s string) error { err := validator.New().Var(s, "email") @@ -91,7 +91,7 @@ func login() *cobra.Command { return xerrors.Errorf("specify email prompt: %w", err) } - password, err := runPrompt(cmd, &promptui.Prompt{ + password, err := prompt(cmd, &promptui.Prompt{ Label: "Enter a password:", Mask: '*', }) diff --git a/cli/projectcreate.go b/cli/projectcreate.go index 766f7fd181e53..69ea595cb654e 100644 --- a/cli/projectcreate.go +++ b/cli/projectcreate.go @@ -3,11 +3,11 @@ package cli import ( "archive/tar" "bytes" + "errors" "fmt" "io" "os" "path/filepath" - "strings" "time" "github.com/briandowns/spinner" @@ -15,7 +15,6 @@ import ( "github.com/google/uuid" "github.com/manifoldco/promptui" "github.com/spf13/cobra" - "github.com/xlab/treeprint" "golang.org/x/xerrors" "github.com/coder/coder/coderd" @@ -27,7 +26,8 @@ import ( func projectCreate() *cobra.Command { var ( - directory string + directory string + provisioner string ) cmd := &cobra.Command{ Use: "create", @@ -41,16 +41,19 @@ func projectCreate() *cobra.Command { if err != nil { return err } - _, err = runPrompt(cmd, &promptui.Prompt{ + _, err = prompt(cmd, &promptui.Prompt{ Default: "y", IsConfirm: true, Label: fmt.Sprintf("Set up %s in your organization?", color.New(color.FgHiCyan).Sprintf("%q", directory)), }) if err != nil { + if errors.Is(err, promptui.ErrAbort) { + return nil + } return err } - name, err := runPrompt(cmd, &promptui.Prompt{ + name, err := prompt(cmd, &promptui.Prompt{ Default: filepath.Base(directory), Label: "What's your project's name?", Validate: func(s string) error { @@ -65,7 +68,7 @@ func projectCreate() *cobra.Command { return err } - job, err := doProjectLoop(cmd, client, organization, directory, []coderd.CreateParameterValueRequest{}) + job, err := validateProjectVersionSource(cmd, client, organization, database.ProvisionerType(provisioner), directory) if err != nil { return err } @@ -77,27 +80,44 @@ func projectCreate() *cobra.Command { return err } + _, err = prompt(cmd, &promptui.Prompt{ + Label: "Create project?", + IsConfirm: true, + Default: "y", + }) + if err != nil { + if errors.Is(err, promptui.ErrAbort) { + return nil + } + return err + } + _, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s The %s project has been created!\n", color.HiBlackString(">"), color.HiCyanString(project.Name)) - _, err = runPrompt(cmd, &promptui.Prompt{ + _, err = prompt(cmd, &promptui.Prompt{ Label: "Create a new workspace?", IsConfirm: true, Default: "y", }) if err != nil { + if errors.Is(err, promptui.ErrAbort) { + return nil + } return err } - fmt.Printf("Create a new workspace now!\n") return nil }, } currentDirectory, _ := os.Getwd() cmd.Flags().StringVarP(&directory, "directory", "d", currentDirectory, "Specify the directory to create from") + cmd.Flags().StringVarP(&provisioner, "provisioner", "p", "terraform", "Customize the provisioner backend") + // This is for testing! There's only 1 provisioner type right now. + cmd.Flags().MarkHidden("provisioner") return cmd } -func doProjectLoop(cmd *cobra.Command, client *codersdk.Client, organization coderd.Organization, directory string, params []coderd.CreateParameterValueRequest) (*coderd.ProvisionerJob, error) { +func validateProjectVersionSource(cmd *cobra.Command, client *codersdk.Client, organization coderd.Organization, provisioner database.ProvisionerType, directory string, parameters ...coderd.CreateParameterValueRequest) (*coderd.ProvisionerJob, error) { spin := spinner.New(spinner.CharSets[5], 100*time.Millisecond) spin.Writer = cmd.OutOrStdout() spin.Suffix = " Uploading current directory..." @@ -118,8 +138,8 @@ func doProjectLoop(cmd *cobra.Command, client *codersdk.Client, organization cod job, err := client.CreateProjectVersionImportProvisionerJob(cmd.Context(), organization.Name, coderd.CreateProjectImportJobRequest{ StorageMethod: database.ProvisionerStorageMethodFile, StorageSource: resp.Hash, - Provisioner: database.ProvisionerTypeTerraform, - ParameterValues: params, + Provisioner: provisioner, + ParameterValues: parameters, }) if err != nil { return nil, err @@ -168,20 +188,20 @@ func doProjectLoop(cmd *cobra.Command, client *codersdk.Client, organization cod if parameterSchema.Name == parameter.CoderWorkspaceTransition { continue } - value, err := runPrompt(cmd, &promptui.Prompt{ + value, err := prompt(cmd, &promptui.Prompt{ Label: fmt.Sprintf("Enter value for %s:", color.HiCyanString(parameterSchema.Name)), }) if err != nil { return nil, err } - params = append(params, coderd.CreateParameterValueRequest{ + parameters = append(parameters, coderd.CreateParameterValueRequest{ Name: parameterSchema.Name, SourceValue: value, SourceScheme: database.ParameterSourceSchemeData, DestinationScheme: parameterSchema.DefaultDestinationScheme, }) } - return doProjectLoop(cmd, client, organization, directory, params) + return validateProjectVersionSource(cmd, client, organization, provisioner, directory, parameters...) } if job.Status != coderd.ProvisionerJobStatusSucceeded { @@ -198,50 +218,7 @@ func doProjectLoop(cmd *cobra.Command, client *codersdk.Client, organization cod if err != nil { return nil, err } - return &job, outputProjectInformation(cmd, parameterSchemas, parameterValues, resources) -} - -func outputProjectInformation(cmd *cobra.Command, parameterSchemas []coderd.ParameterSchema, parameterValues []coderd.ComputedParameterValue, resources []coderd.ProjectImportJobResource) error { - schemaByID := map[string]coderd.ParameterSchema{} - for _, schema := range parameterSchemas { - schemaByID[schema.ID.String()] = schema - } - - _, _ = fmt.Fprintf(cmd.OutOrStdout(), "\n %s\n\n", color.HiBlackString("Parameters")) - for _, value := range parameterValues { - schema, ok := schemaByID[value.SchemaID.String()] - if !ok { - return xerrors.Errorf("schema not found: %s", value.Name) - } - displayValue := value.SourceValue - if !schema.RedisplayValue { - displayValue = "" - } - output := fmt.Sprintf("%s %s %s", color.HiCyanString(value.Name), color.HiBlackString("="), displayValue) - if value.DefaultSourceValue { - output += " (default value)" - } else if value.Scope != database.ParameterScopeImportJob { - output += fmt.Sprintf(" (inherited from %s)", value.Scope) - } - - root := treeprint.NewWithRoot(output) - if schema.Description != "" { - root.AddBranch(fmt.Sprintf("%s\n%s\n", color.HiBlackString("Description"), schema.Description)) - } - if schema.AllowOverrideSource { - root.AddBranch(fmt.Sprintf("%s Users can customize this value!", color.HiYellowString("+"))) - } - _, _ = fmt.Fprintln(cmd.OutOrStdout(), " "+strings.Join(strings.Split(root.String(), "\n"), "\n ")) - } - _, _ = fmt.Fprintf(cmd.OutOrStdout(), " %s\n\n", color.HiBlackString("Resources")) - for _, resource := range resources { - transition := color.HiGreenString("start") - if resource.Transition == database.WorkspaceTransitionStop { - transition = color.HiRedString("stop") - } - _, _ = fmt.Fprintf(cmd.OutOrStdout(), " %s %s on %s\n\n", color.HiCyanString(resource.Type), color.HiCyanString(resource.Name), transition) - } - return nil + return &job, displayProjectImportInfo(cmd, parameterSchemas, parameterValues, resources) } func tarDirectory(directory string) ([]byte, error) { diff --git a/cli/projectcreate_test.go b/cli/projectcreate_test.go index 1d4a83288ddbf..aec401af98781 100644 --- a/cli/projectcreate_test.go +++ b/cli/projectcreate_test.go @@ -6,29 +6,40 @@ import ( "github.com/Netflix/go-expect" "github.com/coder/coder/cli/clitest" "github.com/coder/coder/coderd/coderdtest" + "github.com/coder/coder/database" + "github.com/coder/coder/provisioner/echo" + "github.com/coder/coder/provisionersdk/proto" "github.com/stretchr/testify/require" ) func TestProjectCreate(t *testing.T) { t.Parallel() - t.Run("InitialUserTTY", func(t *testing.T) { + t.Run("NoParameters", func(t *testing.T) { t.Parallel() console, err := expect.NewConsole(expect.WithStdout(clitest.StdoutLogs(t))) require.NoError(t, err) client := coderdtest.New(t) - directory := t.TempDir() - cmd, root := clitest.New(t, "projects", "create", "--directory", directory) + _ = coderdtest.NewProvisionerDaemon(t, client) + source := clitest.CreateProjectVersionSource(t, &echo.Responses{ + Parse: echo.ParseComplete, + Provision: echo.ProvisionComplete, + }) + cmd, root := clitest.New(t, "projects", "create", "--directory", source, "--provisioner", string(database.ProvisionerTypeEcho)) _ = clitest.CreateInitialUser(t, client, root) cmd.SetIn(console.Tty()) cmd.SetOut(console.Tty()) + closeChan := make(chan struct{}) go func() { err := cmd.Execute() require.NoError(t, err) + close(closeChan) }() matches := []string{ "organization?", "y", - "name?", "", + "name?", "test-project", + "project?", "y", + "created!", "n", } for i := 0; i < len(matches); i += 2 { match := matches[i] @@ -38,5 +49,56 @@ func TestProjectCreate(t *testing.T) { _, err = console.SendLine(value) require.NoError(t, err) } + <-closeChan + }) + + t.Run("Parameter", func(t *testing.T) { + t.Parallel() + console, err := expect.NewConsole(expect.WithStdout(clitest.StdoutLogs(t))) + require.NoError(t, err) + client := coderdtest.New(t) + _ = coderdtest.NewProvisionerDaemon(t, client) + source := clitest.CreateProjectVersionSource(t, &echo.Responses{ + Parse: []*proto.Parse_Response{{ + Type: &proto.Parse_Response_Complete{ + Complete: &proto.Parse_Complete{ + ParameterSchemas: []*proto.ParameterSchema{{ + Name: "somevar", + DefaultDestination: &proto.ParameterDestination{ + Scheme: proto.ParameterDestination_PROVISIONER_VARIABLE, + }, + }}, + }, + }, + }}, + Provision: echo.ProvisionComplete, + }) + cmd, root := clitest.New(t, "projects", "create", "--directory", source, "--provisioner", string(database.ProvisionerTypeEcho)) + _ = clitest.CreateInitialUser(t, client, root) + cmd.SetIn(console.Tty()) + cmd.SetOut(console.Tty()) + closeChan := make(chan struct{}) + go func() { + err := cmd.Execute() + require.NoError(t, err) + close(closeChan) + }() + + matches := []string{ + "organization?", "y", + "name?", "test-project", + "somevar:", "value", + "project?", "y", + "created!", "n", + } + for i := 0; i < len(matches); i += 2 { + match := matches[i] + value := matches[i+1] + _, err = console.ExpectString(match) + require.NoError(t, err) + _, err = console.SendLine(value) + require.NoError(t, err) + } + <-closeChan }) } diff --git a/cli/projects.go b/cli/projects.go index e0963459a6ed6..219d1e8733c70 100644 --- a/cli/projects.go +++ b/cli/projects.go @@ -1,8 +1,15 @@ package cli import ( + "fmt" + "strings" + + "github.com/coder/coder/coderd" + "github.com/coder/coder/database" "github.com/fatih/color" "github.com/spf13/cobra" + "github.com/xlab/treeprint" + "golang.org/x/xerrors" ) func projects() *cobra.Command { @@ -29,3 +36,46 @@ func projects() *cobra.Command { return cmd } + +func displayProjectImportInfo(cmd *cobra.Command, parameterSchemas []coderd.ParameterSchema, parameterValues []coderd.ComputedParameterValue, resources []coderd.ProjectImportJobResource) error { + schemaByID := map[string]coderd.ParameterSchema{} + for _, schema := range parameterSchemas { + schemaByID[schema.ID.String()] = schema + } + + _, _ = fmt.Fprintf(cmd.OutOrStdout(), "\n %s\n\n", color.HiBlackString("Parameters")) + for _, value := range parameterValues { + schema, ok := schemaByID[value.SchemaID.String()] + if !ok { + return xerrors.Errorf("schema not found: %s", value.Name) + } + displayValue := value.SourceValue + if !schema.RedisplayValue { + displayValue = "" + } + output := fmt.Sprintf("%s %s %s", color.HiCyanString(value.Name), color.HiBlackString("="), displayValue) + if value.DefaultSourceValue { + output += " (default value)" + } else if value.Scope != database.ParameterScopeImportJob { + output += fmt.Sprintf(" (inherited from %s)", value.Scope) + } + + root := treeprint.NewWithRoot(output) + if schema.Description != "" { + root.AddBranch(fmt.Sprintf("%s\n%s", color.HiBlackString("Description"), schema.Description)) + } + if schema.AllowOverrideSource { + root.AddBranch(fmt.Sprintf("%s Users can customize this value!", color.HiYellowString("+"))) + } + _, _ = fmt.Fprintln(cmd.OutOrStdout(), " "+strings.Join(strings.Split(root.String(), "\n"), "\n ")) + } + _, _ = fmt.Fprintf(cmd.OutOrStdout(), " %s\n\n", color.HiBlackString("Resources")) + for _, resource := range resources { + transition := color.HiGreenString("start") + if resource.Transition == database.WorkspaceTransitionStop { + transition = color.HiRedString("stop") + } + _, _ = fmt.Fprintf(cmd.OutOrStdout(), " %s %s on %s\n\n", color.HiCyanString(resource.Type), color.HiCyanString(resource.Name), transition) + } + return nil +} diff --git a/cli/root.go b/cli/root.go index 29ef8e9609b65..22d9e791d4699 100644 --- a/cli/root.go +++ b/cli/root.go @@ -69,6 +69,8 @@ func Root() *cobra.Command { return cmd } +// createClient returns a new client from the command context. +// The configuration directory will be read from the global flag. func createClient(cmd *cobra.Command) (*codersdk.Client, error) { root := createConfig(cmd) rawURL, err := root.URL().Read() @@ -87,6 +89,7 @@ func createClient(cmd *cobra.Command) (*codersdk.Client, error) { return client, client.SetSessionToken(token) } +// currentOrganization returns the currently active organization for the authenticated user. func currentOrganization(cmd *cobra.Command, client *codersdk.Client) (coderd.Organization, error) { orgs, err := client.UserOrganizations(cmd.Context(), "me") if err != nil { @@ -97,6 +100,7 @@ func currentOrganization(cmd *cobra.Command, client *codersdk.Client) (coderd.Or return orgs[0], nil } +// createConfig consumes the global configuration flag to produce a config root. func createConfig(cmd *cobra.Command) config.Root { globalRoot, err := cmd.Flags().GetString(varGlobalConfig) if err != nil { @@ -116,7 +120,7 @@ func isTTY(reader io.Reader) bool { return isatty.IsTerminal(file.Fd()) } -func runPrompt(cmd *cobra.Command, prompt *promptui.Prompt) (string, error) { +func prompt(cmd *cobra.Command, prompt *promptui.Prompt) (string, error) { var ok bool prompt.Stdin, ok = cmd.InOrStdin().(io.ReadCloser) if !ok { diff --git a/cli/workspacecreate.go b/cli/workspacecreate.go new file mode 100644 index 0000000000000..ede86c3d825a2 --- /dev/null +++ b/cli/workspacecreate.go @@ -0,0 +1,128 @@ +package cli + +import ( + "errors" + "fmt" + "time" + + "github.com/coder/coder/coderd" + "github.com/coder/coder/database" + "github.com/fatih/color" + "github.com/google/uuid" + "github.com/manifoldco/promptui" + "github.com/spf13/cobra" + "golang.org/x/xerrors" +) + +func workspaceCreate() *cobra.Command { + cmd := &cobra.Command{ + Use: "create [name]", + Short: "Create a workspace from a project", + RunE: func(cmd *cobra.Command, args []string) error { + client, err := createClient(cmd) + if err != nil { + return err + } + organization, err := currentOrganization(cmd, client) + if err != nil { + return err + } + + var name string + if len(args) >= 2 { + name = args[1] + } else { + name, err = prompt(cmd, &promptui.Prompt{ + Label: "What's your workspace's name?", + Validate: func(s string) error { + workspace, _ := client.Workspace(cmd.Context(), "", s) + if workspace.ID.String() != uuid.Nil.String() { + return xerrors.New("A workspace already exists with that name!") + } + return nil + }, + }) + if err != nil { + if errors.Is(err, promptui.ErrAbort) { + return nil + } + return err + } + } + + _, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Previewing project create...\n", color.HiBlackString(">")) + + project, err := client.Project(cmd.Context(), organization.Name, args[0]) + if err != nil { + return err + } + projectVersion, err := client.ProjectVersion(cmd.Context(), organization.Name, project.Name, project.ActiveVersionID.String()) + if err != nil { + return err + } + parameterSchemas, err := client.ProvisionerJobParameterSchemas(cmd.Context(), organization.Name, projectVersion.ImportJobID) + if err != nil { + return err + } + parameterValues, err := client.ProvisionerJobParameterValues(cmd.Context(), organization.Name, projectVersion.ImportJobID) + if err != nil { + return err + } + resources, err := client.ProvisionerJobResources(cmd.Context(), organization.Name, projectVersion.ImportJobID) + if err != nil { + return err + } + err = displayProjectImportInfo(cmd, parameterSchemas, parameterValues, resources) + if err != nil { + return err + } + + _, err = prompt(cmd, &promptui.Prompt{ + Label: fmt.Sprintf("Create workspace %s?", color.HiCyanString(name)), + Default: "y", + IsConfirm: true, + }) + if err != nil { + if errors.Is(err, promptui.ErrAbort) { + return nil + } + return err + } + + workspace, err := client.CreateWorkspace(cmd.Context(), "", coderd.CreateWorkspaceRequest{ + ProjectID: project.ID, + Name: name, + }) + if err != nil { + return err + } + history, err := client.CreateWorkspaceHistory(cmd.Context(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{ + ProjectVersionID: projectVersion.ID, + Transition: database.WorkspaceTransitionStart, + }) + if err != nil { + return err + } + + logs, err := client.FollowProvisionerJobLogsAfter(cmd.Context(), organization.Name, history.ProvisionJobID, time.Time{}) + if err != nil { + return err + } + logBuffer := make([]coderd.ProvisionerJobLog, 0, 64) + for { + log, ok := <-logs + if !ok { + break + } + fmt.Printf("Logging: %s\n", log.Output) + logBuffer = append(logBuffer, log) + } + + fmt.Printf("Create workspace! %s\n", name) + + return nil + }, + } + + return cmd +} diff --git a/cli/workspaces.go b/cli/workspaces.go index 4140d8c9ed7a2..d405f00cea88b 100644 --- a/cli/workspaces.go +++ b/cli/workspaces.go @@ -6,6 +6,7 @@ func workspaces() *cobra.Command { cmd := &cobra.Command{ Use: "workspaces", } + cmd.AddCommand(workspaceCreate()) return cmd } From aac220fe5f1a226f71513a70880f40488545bd30 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Fri, 11 Feb 2022 05:29:18 +0000 Subject: [PATCH 12/20] Replace Netflix/go-expect with ActiveState --- cli/login_test.go | 4 +--- cli/projectcreate_test.go | 2 +- go.mod | 7 ++++++- go.sum | 24 ++++++++++++++++++++++++ 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/cli/login_test.go b/cli/login_test.go index f2102177d6710..cb63ffb6b62a5 100644 --- a/cli/login_test.go +++ b/cli/login_test.go @@ -1,5 +1,3 @@ -//go:build !windows - package cli_test import ( @@ -9,7 +7,7 @@ import ( "github.com/coder/coder/coderd/coderdtest" "github.com/stretchr/testify/require" - "github.com/Netflix/go-expect" + "github.com/ActiveState/termtest/expect" ) func TestLogin(t *testing.T) { diff --git a/cli/projectcreate_test.go b/cli/projectcreate_test.go index aec401af98781..2b16abe50c9cd 100644 --- a/cli/projectcreate_test.go +++ b/cli/projectcreate_test.go @@ -3,7 +3,7 @@ package cli_test import ( "testing" - "github.com/Netflix/go-expect" + "github.com/ActiveState/termtest/expect" "github.com/coder/coder/cli/clitest" "github.com/coder/coder/coderd/coderdtest" "github.com/coder/coder/database" diff --git a/go.mod b/go.mod index f84913c114df6..317337c0a0fed 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ replace github.com/hashicorp/terraform-config-inspect => github.com/kylecarbs/te require ( cdr.dev/slog v1.4.1 - github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 + github.com/ActiveState/termtest/expect v0.7.0 github.com/briandowns/spinner v1.18.1 github.com/coder/retry v1.3.0 github.com/fatih/color v1.13.0 @@ -55,8 +55,12 @@ require ( require ( cloud.google.com/go/compute v0.1.0 // indirect + github.com/ActiveState/termtest/conpty v0.5.0 // indirect + github.com/ActiveState/termtest/xpty v0.6.0 // indirect + github.com/ActiveState/vt10x v1.3.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Microsoft/go-winio v0.5.1 // indirect + github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/agext/levenshtein v1.2.3 // indirect github.com/alecthomas/chroma v0.10.0 // indirect @@ -88,6 +92,7 @@ require ( github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a // indirect github.com/klauspost/compress v1.13.6 // indirect + github.com/kr/pty v1.1.8 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a // indirect github.com/mattn/go-colorable v0.1.12 // indirect diff --git a/go.sum b/go.sum index f39b53bdea85f..b14a346104c3c 100644 --- a/go.sum +++ b/go.sum @@ -57,6 +57,14 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= +github.com/ActiveState/termtest/conpty v0.5.0 h1:JLUe6YDs4Jw4xNPCU+8VwTpniYOGeKzQg4SM2YHQNA8= +github.com/ActiveState/termtest/conpty v0.5.0/go.mod h1:LO4208FLsxw6DcNZ1UtuGUMW+ga9PFtX4ntv8Ymg9og= +github.com/ActiveState/termtest/expect v0.7.0 h1:VNrcfXTHXXoe7i+3WgF5QJhstQLGP4saj+XYM9ZzBvY= +github.com/ActiveState/termtest/expect v0.7.0/go.mod h1:64QuJvMtMu7+H5U+5TSMBxAs1FAaLvRIyN7WPOICido= +github.com/ActiveState/termtest/xpty v0.6.0 h1:L9c17TDfy+ed+tY5cMOErn0n2EYG4tj8StdxHmoPok8= +github.com/ActiveState/termtest/xpty v0.6.0/go.mod h1:MmTm/62Ajq+D92emHq8LOu9Q+2+pkBurDLahkUP6Odg= +github.com/ActiveState/vt10x v1.3.1 h1:7qi8BGXUEBghzBxfXSY0J77etO+L95PZQlwD7ay2mn0= +github.com/ActiveState/vt10x v1.3.1/go.mod h1:8wJKd36c9NmCfGyPyOJmkvyIMvbUPfHkfdS8zZlK19s= github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck= @@ -103,6 +111,8 @@ github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01 github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= +github.com/Netflix/go-expect v0.0.0-20200312175327-da48e75238e2/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= @@ -150,6 +160,8 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/autarch/testify v1.2.2 h1:9Q9V6zqhP7R6dv+zRUddv6kXKLo6ecQhnFRFWM71i1c= +github.com/autarch/testify v1.2.2/go.mod h1:oDbHKfFv2/D5UtVrxkk90OKcb6P4/AqF1Pcf6ZbvDQo= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v1.8.0/go.mod h1:xEFuWz+3TYdlPRuo+CqATbeDWIWyaT5uAPwPaWtgse0= @@ -438,6 +450,8 @@ github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXt github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= +github.com/gdamore/encoding v0.0.0-20151215212835-b23993cbb635/go.mod h1:yrQYJKKDTrHmbYxI7CYi+/hbdiDT2m4Hj+t0ikCjsrQ= +github.com/gdamore/tcell v1.0.1-0.20180608172421-b3cebc399d6f/go.mod h1:tqyG50u7+Ctv1w5VX67kLzKcj9YXR/JSBZQq/+mLl1A= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= @@ -715,6 +729,7 @@ github.com/hashicorp/terraform-json v0.13.0 h1:Li9L+lKD1FO5RVFRM1mMMIBDoUHslOniy github.com/hashicorp/terraform-json v0.13.0/go.mod h1:y5OdLBCT+rxbwnpxZs9kGL7R9ExU76+cpdY8zHwoazk= github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I= github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -836,6 +851,7 @@ github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -862,6 +878,7 @@ github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lucasb-eyer/go-colorful v0.0.0-20180526135729-345fbb3dbcdb/go.mod h1:NXg0ArsFk0Y01623LgUqoqcouGDB+PwCCQlrwrG6xJ4= github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw= github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= @@ -1153,6 +1170,7 @@ github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/snowflakedb/gosnowflake v1.6.3/go.mod h1:6hLajn6yxuJ4xUHZegMekpq9rnQbGJ7TMwXjgTmA6lg= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -1184,6 +1202,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -1318,6 +1337,7 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200427165652-729f1e841bcc/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -1540,6 +1560,7 @@ golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200428200454-593003d681fa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1549,6 +1570,7 @@ golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200821140526-fda516888d29/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1556,6 +1578,7 @@ golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1877,6 +1900,7 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0/go.mod h1:OdE7CF6DbADk7lN8LIKRzRJTTZXIjtWgA5THM5lhBAw= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From c493bf9da0a80112ca4a79a3991c97b9c95624aa Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Fri, 11 Feb 2022 05:34:55 +0000 Subject: [PATCH 13/20] Fix linting errors --- cli/clitest/clitest.go | 1 + cli/login_test.go | 3 ++- cli/projectcreate.go | 16 ++++++++++------ cli/projectcreate_test.go | 7 ++++--- cli/projects.go | 5 +++-- cli/workspacecreate.go | 13 +++++++------ coderd/provisionerdaemons.go | 10 +++++----- codersdk/provisioners_test.go | 7 ------- database/databasefake/databasefake.go | 4 ++-- 9 files changed, 34 insertions(+), 32 deletions(-) diff --git a/cli/clitest/clitest.go b/cli/clitest/clitest.go index afcda2c5c8e55..a7dc55802ff66 100644 --- a/cli/clitest/clitest.go +++ b/cli/clitest/clitest.go @@ -96,6 +96,7 @@ func extractTar(data []byte, directory string) error { if err != nil { return xerrors.Errorf("read project source archive: %w", err) } + // #nosec path := filepath.Join(directory, header.Name) mode := header.FileInfo().Mode() if mode == 0 { diff --git a/cli/login_test.go b/cli/login_test.go index cb63ffb6b62a5..a37918a0ad61b 100644 --- a/cli/login_test.go +++ b/cli/login_test.go @@ -3,9 +3,10 @@ package cli_test import ( "testing" + "github.com/stretchr/testify/require" + "github.com/coder/coder/cli/clitest" "github.com/coder/coder/coderd/coderdtest" - "github.com/stretchr/testify/require" "github.com/ActiveState/termtest/expect" ) diff --git a/cli/projectcreate.go b/cli/projectcreate.go index 69ea595cb654e..40bc3a3b68128 100644 --- a/cli/projectcreate.go +++ b/cli/projectcreate.go @@ -112,8 +112,10 @@ func projectCreate() *cobra.Command { cmd.Flags().StringVarP(&directory, "directory", "d", currentDirectory, "Specify the directory to create from") cmd.Flags().StringVarP(&provisioner, "provisioner", "p", "terraform", "Customize the provisioner backend") // This is for testing! There's only 1 provisioner type right now. - cmd.Flags().MarkHidden("provisioner") - + err := cmd.Flags().MarkHidden("provisioner") + if err != nil { + panic(err) + } return cmd } @@ -121,16 +123,18 @@ func validateProjectVersionSource(cmd *cobra.Command, client *codersdk.Client, o spin := spinner.New(spinner.CharSets[5], 100*time.Millisecond) spin.Writer = cmd.OutOrStdout() spin.Suffix = " Uploading current directory..." - spin.Color("fgHiGreen") + err := spin.Color("fgHiGreen") + if err != nil { + return nil, err + } spin.Start() defer spin.Stop() - bytes, err := tarDirectory(directory) + tarData, err := tarDirectory(directory) if err != nil { return nil, err } - - resp, err := client.UploadFile(cmd.Context(), codersdk.ContentTypeTar, bytes) + resp, err := client.UploadFile(cmd.Context(), codersdk.ContentTypeTar, tarData) if err != nil { return nil, err } diff --git a/cli/projectcreate_test.go b/cli/projectcreate_test.go index 2b16abe50c9cd..53a0d83e1ce00 100644 --- a/cli/projectcreate_test.go +++ b/cli/projectcreate_test.go @@ -4,12 +4,13 @@ import ( "testing" "github.com/ActiveState/termtest/expect" + "github.com/stretchr/testify/require" + "github.com/coder/coder/cli/clitest" "github.com/coder/coder/coderd/coderdtest" "github.com/coder/coder/database" "github.com/coder/coder/provisioner/echo" "github.com/coder/coder/provisionersdk/proto" - "github.com/stretchr/testify/require" ) func TestProjectCreate(t *testing.T) { @@ -19,13 +20,13 @@ func TestProjectCreate(t *testing.T) { console, err := expect.NewConsole(expect.WithStdout(clitest.StdoutLogs(t))) require.NoError(t, err) client := coderdtest.New(t) - _ = coderdtest.NewProvisionerDaemon(t, client) source := clitest.CreateProjectVersionSource(t, &echo.Responses{ Parse: echo.ParseComplete, Provision: echo.ProvisionComplete, }) cmd, root := clitest.New(t, "projects", "create", "--directory", source, "--provisioner", string(database.ProvisionerTypeEcho)) _ = clitest.CreateInitialUser(t, client, root) + _ = coderdtest.NewProvisionerDaemon(t, client) cmd.SetIn(console.Tty()) cmd.SetOut(console.Tty()) closeChan := make(chan struct{}) @@ -57,7 +58,6 @@ func TestProjectCreate(t *testing.T) { console, err := expect.NewConsole(expect.WithStdout(clitest.StdoutLogs(t))) require.NoError(t, err) client := coderdtest.New(t) - _ = coderdtest.NewProvisionerDaemon(t, client) source := clitest.CreateProjectVersionSource(t, &echo.Responses{ Parse: []*proto.Parse_Response{{ Type: &proto.Parse_Response_Complete{ @@ -75,6 +75,7 @@ func TestProjectCreate(t *testing.T) { }) cmd, root := clitest.New(t, "projects", "create", "--directory", source, "--provisioner", string(database.ProvisionerTypeEcho)) _ = clitest.CreateInitialUser(t, client, root) + _ = coderdtest.NewProvisionerDaemon(t, client) cmd.SetIn(console.Tty()) cmd.SetOut(console.Tty()) closeChan := make(chan struct{}) diff --git a/cli/projects.go b/cli/projects.go index 219d1e8733c70..b11317367aa8b 100644 --- a/cli/projects.go +++ b/cli/projects.go @@ -4,12 +4,13 @@ import ( "fmt" "strings" - "github.com/coder/coder/coderd" - "github.com/coder/coder/database" "github.com/fatih/color" "github.com/spf13/cobra" "github.com/xlab/treeprint" "golang.org/x/xerrors" + + "github.com/coder/coder/coderd" + "github.com/coder/coder/database" ) func projects() *cobra.Command { diff --git a/cli/workspacecreate.go b/cli/workspacecreate.go index ede86c3d825a2..b6be5b75af418 100644 --- a/cli/workspacecreate.go +++ b/cli/workspacecreate.go @@ -5,13 +5,14 @@ import ( "fmt" "time" - "github.com/coder/coder/coderd" - "github.com/coder/coder/database" "github.com/fatih/color" "github.com/google/uuid" "github.com/manifoldco/promptui" "github.com/spf13/cobra" "golang.org/x/xerrors" + + "github.com/coder/coder/coderd" + "github.com/coder/coder/database" ) func workspaceCreate() *cobra.Command { @@ -108,17 +109,17 @@ func workspaceCreate() *cobra.Command { if err != nil { return err } - logBuffer := make([]coderd.ProvisionerJobLog, 0, 64) + // logBuffer := make([]coderd.ProvisionerJobLog, 0, 64) for { log, ok := <-logs if !ok { break } - fmt.Printf("Logging: %s\n", log.Output) - logBuffer = append(logBuffer, log) + _, _ = fmt.Printf("Logging: %s\n", log.Output) + // logBuffer = append(logBuffer, log) } - fmt.Printf("Create workspace! %s\n", name) + _, _ = fmt.Printf("Create workspace! %s\n", name) return nil }, diff --git a/coderd/provisionerdaemons.go b/coderd/provisionerdaemons.go index f98a73a962126..28ba99b61e061 100644 --- a/coderd/provisionerdaemons.go +++ b/coderd/provisionerdaemons.go @@ -219,8 +219,8 @@ func (server *provisionerdServer) AcquireJob(ctx context.Context, _ *proto.Empty } // Convert parameters to the protobuf type. protoParameters := make([]*sdkproto.ParameterValue, 0, len(parameters)) - for _, parameter := range parameters { - converted, err := convertComputedParameterValue(parameter) + for _, computedParameter := range parameters { + converted, err := convertComputedParameterValue(computedParameter) if err != nil { return nil, failJob(fmt.Sprintf("convert parameter: %s", err)) } @@ -371,8 +371,8 @@ func (server *provisionerdServer) UpdateJob(ctx context.Context, request *proto. } // Convert parameters to the protobuf type. protoParameters := make([]*sdkproto.ParameterValue, 0, len(parameters)) - for _, parameter := range parameters { - converted, err := convertComputedParameterValue(parameter) + for _, computedParameter := range parameters { + converted, err := convertComputedParameterValue(computedParameter) if err != nil { return nil, xerrors.Errorf("convert parameter: %s", err) } @@ -597,7 +597,7 @@ func convertLogSource(logSource proto.LogSource) (database.LogSource, error) { } func convertComputedParameterValue(param parameter.ComputedValue) (*sdkproto.ParameterValue, error) { - scheme := sdkproto.ParameterDestination_ENVIRONMENT_VARIABLE + var scheme sdkproto.ParameterDestination_Scheme switch param.DestinationScheme { case database.ParameterDestinationSchemeEnvironmentVariable: scheme = sdkproto.ParameterDestination_ENVIRONMENT_VARIABLE diff --git a/codersdk/provisioners_test.go b/codersdk/provisioners_test.go index 60c3ad49f8b9d..6808dd19a3b78 100644 --- a/codersdk/provisioners_test.go +++ b/codersdk/provisioners_test.go @@ -106,10 +106,3 @@ func TestFollowProvisionerJobLogsAfter(t *testing.T) { require.False(t, ok) }) } - -func TestProvisionerJobParameterSchemas(t *testing.T) { - t.Parallel() - t.Run("Error", func(t *testing.T) { - - }) -} diff --git a/database/databasefake/databasefake.go b/database/databasefake/databasefake.go index a0a2f4359839e..3af24ad8bb563 100644 --- a/database/databasefake/databasefake.go +++ b/database/databasefake/databasefake.go @@ -401,7 +401,7 @@ func (q *fakeQuerier) GetProjectByOrganizationAndName(_ context.Context, arg dat return database.Project{}, sql.ErrNoRows } -func (q *fakeQuerier) GetProjectImportJobResourcesByJobID(ctx context.Context, jobID uuid.UUID) ([]database.ProjectImportJobResource, error) { +func (q *fakeQuerier) GetProjectImportJobResourcesByJobID(_ context.Context, jobID uuid.UUID) ([]database.ProjectImportJobResource, error) { q.mutex.Lock() defer q.mutex.Unlock() @@ -685,7 +685,7 @@ func (q *fakeQuerier) InsertProject(_ context.Context, arg database.InsertProjec return project, nil } -func (q *fakeQuerier) InsertProjectImportJobResource(ctx context.Context, arg database.InsertProjectImportJobResourceParams) (database.ProjectImportJobResource, error) { +func (q *fakeQuerier) InsertProjectImportJobResource(_ context.Context, arg database.InsertProjectImportJobResourceParams) (database.ProjectImportJobResource, error) { q.mutex.Lock() defer q.mutex.Unlock() From 485c07b56a13b18e5b2dc4a69173d20bcd085b43 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Fri, 11 Feb 2022 05:44:36 +0000 Subject: [PATCH 14/20] Use forked chzyer/readline --- go.mod | 3 +++ go.sum | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 317337c0a0fed..e7168de21e631 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,9 @@ replace github.com/hashicorp/terraform-exec => github.com/kylecarbs/terraform-ex // Required until https://github.com/hashicorp/terraform-config-inspect/pull/74 is merged. replace github.com/hashicorp/terraform-config-inspect => github.com/kylecarbs/terraform-config-inspect v0.0.0-20211215004401-bbc517866b88 +// Required until https://github.com/chzyer/readline/pull/198 is merged. +replace github.com/chzyer/readline => github.com/kylecarbs/readline v0.0.0-20220211054233-0d62993714c8 + require ( cdr.dev/slog v1.4.1 github.com/ActiveState/termtest/expect v0.7.0 diff --git a/go.sum b/go.sum index b14a346104c3c..c8bd72397ed5a 100644 --- a/go.sum +++ b/go.sum @@ -224,8 +224,6 @@ github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d8 github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= @@ -859,6 +857,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4= github.com/kylecarbs/promptui v0.8.1-0.20201231190244-d8f2159af2b2 h1:MUREBTh4kybLY1KyuBfSx+QPfTB8XiUHs6ZxUhOPTnU= github.com/kylecarbs/promptui v0.8.1-0.20201231190244-d8f2159af2b2/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ= +github.com/kylecarbs/readline v0.0.0-20220211054233-0d62993714c8 h1:Y7O3Z3YeNRtw14QrtHpevU4dSjCkov0J40MtQ7Nc0n8= +github.com/kylecarbs/readline v0.0.0-20220211054233-0d62993714c8/go.mod h1:n/KX1BZoN1m9EwoXkn/xAV4fd3k8c++gGBsgLONaPOY= github.com/kylecarbs/terraform-config-inspect v0.0.0-20211215004401-bbc517866b88 h1:tvG/qs5c4worwGyGnbbb4i/dYYLjpFwDMqcIT3awAf8= github.com/kylecarbs/terraform-config-inspect v0.0.0-20211215004401-bbc517866b88/go.mod h1:Z0Nnk4+3Cy89smEbrq+sl1bxc9198gIP4I7wcQF6Kqs= github.com/kylecarbs/terraform-exec v0.15.1-0.20220202050609-a1ce7181b180 h1:yafC0pmxjs18fnO5RdKFLSItJIjYwGfSHTfcUvlZb3E= From b5a774a78c6b577ee53d9e03622a5d3f08a2ccf1 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Fri, 11 Feb 2022 17:38:09 +0000 Subject: [PATCH 15/20] Add create workspace CLI --- cli/clitest/clitest.go | 31 +++++++---------- cli/login_test.go | 14 +++----- cli/projectcreate_test.go | 23 +++++------- cli/root.go | 3 +- cli/workspacecreate.go | 3 ++ cli/workspacecreate_test.go | 62 +++++++++++++++++++++++++++++++++ coderd/coderdtest/coderdtest.go | 3 +- coderd/provisionerdaemons.go | 11 +++--- coderd/provisionerjobs.go | 1 + coderd/workspaces_test.go | 2 +- codersdk/client.go | 24 ++++--------- 11 files changed, 108 insertions(+), 69 deletions(-) create mode 100644 cli/workspacecreate_test.go diff --git a/cli/clitest/clitest.go b/cli/clitest/clitest.go index a7dc55802ff66..ad21230c3b8fb 100644 --- a/cli/clitest/clitest.go +++ b/cli/clitest/clitest.go @@ -4,7 +4,6 @@ import ( "archive/tar" "bufio" "bytes" - "context" "errors" "io" "os" @@ -12,14 +11,13 @@ import ( "regexp" "testing" + "github.com/Netflix/go-expect" "github.com/spf13/cobra" "github.com/stretchr/testify/require" "golang.org/x/xerrors" "github.com/coder/coder/cli" "github.com/coder/coder/cli/config" - "github.com/coder/coder/coderd" - "github.com/coder/coder/coderd/coderdtest" "github.com/coder/coder/codersdk" "github.com/coder/coder/provisioner/echo" ) @@ -39,20 +37,12 @@ func New(t *testing.T, args ...string) (*cobra.Command, config.Root) { return cmd, root } -// CreateInitialUser creates the initial user and write's the session -// token to the config root provided. -func CreateInitialUser(t *testing.T, client *codersdk.Client, root config.Root) coderd.CreateInitialUserRequest { - user := coderdtest.CreateInitialUser(t, client) - resp, err := client.LoginWithPassword(context.Background(), coderd.LoginWithPasswordRequest{ - Email: user.Email, - Password: user.Password, - }) - require.NoError(t, err) - err = root.Session().Write(resp.SessionToken) +// SetupConfig applies the URL and SessionToken of the client to the config. +func SetupConfig(t *testing.T, client *codersdk.Client, root config.Root) { + err := root.Session().Write(client.SessionToken) require.NoError(t, err) err = root.URL().Write(client.URL.String()) require.NoError(t, err) - return user } // CreateProjectVersionSource writes the echo provisioner responses into a @@ -66,9 +56,9 @@ func CreateProjectVersionSource(t *testing.T, responses *echo.Responses) string return directory } -// StdoutLogs provides a writer to t.Log that strips -// all ANSI escape codes. -func StdoutLogs(t *testing.T) io.Writer { +// NewConsole creates a new TTY bound to the command provided. +// All ANSI escape codes are stripped to provide clean output. +func NewConsole(t *testing.T, cmd *cobra.Command) *expect.Console { reader, writer := io.Pipe() scanner := bufio.NewScanner(reader) t.Cleanup(func() { @@ -83,7 +73,12 @@ func StdoutLogs(t *testing.T) io.Writer { t.Log(stripAnsi.ReplaceAllString(scanner.Text(), "")) } }() - return writer + + console, err := expect.NewConsole(expect.WithStdout(writer)) + require.NoError(t, err) + cmd.SetIn(console.Tty()) + cmd.SetOut(console.Tty()) + return console } func extractTar(data []byte, directory string) error { diff --git a/cli/login_test.go b/cli/login_test.go index a37918a0ad61b..faefdf4ccccb3 100644 --- a/cli/login_test.go +++ b/cli/login_test.go @@ -3,12 +3,9 @@ package cli_test import ( "testing" - "github.com/stretchr/testify/require" - "github.com/coder/coder/cli/clitest" "github.com/coder/coder/coderd/coderdtest" - - "github.com/ActiveState/termtest/expect" + "github.com/stretchr/testify/require" ) func TestLogin(t *testing.T) { @@ -23,12 +20,9 @@ func TestLogin(t *testing.T) { t.Run("InitialUserTTY", func(t *testing.T) { t.Parallel() - console, err := expect.NewConsole(expect.WithStdout(clitest.StdoutLogs(t))) - require.NoError(t, err) client := coderdtest.New(t) root, _ := clitest.New(t, "login", client.URL.String()) - root.SetIn(console.Tty()) - root.SetOut(console.Tty()) + console := clitest.NewConsole(t, root) go func() { err := root.Execute() require.NoError(t, err) @@ -44,12 +38,12 @@ func TestLogin(t *testing.T) { for i := 0; i < len(matches); i += 2 { match := matches[i] value := matches[i+1] - _, err = console.ExpectString(match) + _, err := console.ExpectString(match) require.NoError(t, err) _, err = console.SendLine(value) require.NoError(t, err) } - _, err = console.ExpectString("Welcome to Coder") + _, err := console.ExpectString("Welcome to Coder") require.NoError(t, err) }) } diff --git a/cli/projectcreate_test.go b/cli/projectcreate_test.go index 53a0d83e1ce00..547447d3b87bd 100644 --- a/cli/projectcreate_test.go +++ b/cli/projectcreate_test.go @@ -3,7 +3,6 @@ package cli_test import ( "testing" - "github.com/ActiveState/termtest/expect" "github.com/stretchr/testify/require" "github.com/coder/coder/cli/clitest" @@ -17,18 +16,16 @@ func TestProjectCreate(t *testing.T) { t.Parallel() t.Run("NoParameters", func(t *testing.T) { t.Parallel() - console, err := expect.NewConsole(expect.WithStdout(clitest.StdoutLogs(t))) - require.NoError(t, err) client := coderdtest.New(t) + coderdtest.CreateInitialUser(t, client) source := clitest.CreateProjectVersionSource(t, &echo.Responses{ Parse: echo.ParseComplete, Provision: echo.ProvisionComplete, }) cmd, root := clitest.New(t, "projects", "create", "--directory", source, "--provisioner", string(database.ProvisionerTypeEcho)) - _ = clitest.CreateInitialUser(t, client, root) + clitest.SetupConfig(t, client, root) _ = coderdtest.NewProvisionerDaemon(t, client) - cmd.SetIn(console.Tty()) - cmd.SetOut(console.Tty()) + console := clitest.NewConsole(t, cmd) closeChan := make(chan struct{}) go func() { err := cmd.Execute() @@ -45,7 +42,7 @@ func TestProjectCreate(t *testing.T) { for i := 0; i < len(matches); i += 2 { match := matches[i] value := matches[i+1] - _, err = console.ExpectString(match) + _, err := console.ExpectString(match) require.NoError(t, err) _, err = console.SendLine(value) require.NoError(t, err) @@ -55,9 +52,8 @@ func TestProjectCreate(t *testing.T) { t.Run("Parameter", func(t *testing.T) { t.Parallel() - console, err := expect.NewConsole(expect.WithStdout(clitest.StdoutLogs(t))) - require.NoError(t, err) client := coderdtest.New(t) + coderdtest.CreateInitialUser(t, client) source := clitest.CreateProjectVersionSource(t, &echo.Responses{ Parse: []*proto.Parse_Response{{ Type: &proto.Parse_Response_Complete{ @@ -74,10 +70,9 @@ func TestProjectCreate(t *testing.T) { Provision: echo.ProvisionComplete, }) cmd, root := clitest.New(t, "projects", "create", "--directory", source, "--provisioner", string(database.ProvisionerTypeEcho)) - _ = clitest.CreateInitialUser(t, client, root) - _ = coderdtest.NewProvisionerDaemon(t, client) - cmd.SetIn(console.Tty()) - cmd.SetOut(console.Tty()) + clitest.SetupConfig(t, client, root) + coderdtest.NewProvisionerDaemon(t, client) + console := clitest.NewConsole(t, cmd) closeChan := make(chan struct{}) go func() { err := cmd.Execute() @@ -95,7 +90,7 @@ func TestProjectCreate(t *testing.T) { for i := 0; i < len(matches); i += 2 { match := matches[i] value := matches[i+1] - _, err = console.ExpectString(match) + _, err := console.ExpectString(match) require.NoError(t, err) _, err = console.SendLine(value) require.NoError(t, err) diff --git a/cli/root.go b/cli/root.go index 22d9e791d4699..9133d7655d133 100644 --- a/cli/root.go +++ b/cli/root.go @@ -86,7 +86,8 @@ func createClient(cmd *cobra.Command) (*codersdk.Client, error) { return nil, err } client := codersdk.New(serverURL) - return client, client.SetSessionToken(token) + client.SessionToken = token + return client, nil } // currentOrganization returns the currently active organization for the authenticated user. diff --git a/cli/workspacecreate.go b/cli/workspacecreate.go index b6be5b75af418..9eaa4941d26cc 100644 --- a/cli/workspacecreate.go +++ b/cli/workspacecreate.go @@ -36,6 +36,9 @@ func workspaceCreate() *cobra.Command { name, err = prompt(cmd, &promptui.Prompt{ Label: "What's your workspace's name?", Validate: func(s string) error { + if s == "" { + return xerrors.Errorf("You must provide a name!") + } workspace, _ := client.Workspace(cmd.Context(), "", s) if workspace.ID.String() != uuid.Nil.String() { return xerrors.New("A workspace already exists with that name!") diff --git a/cli/workspacecreate_test.go b/cli/workspacecreate_test.go new file mode 100644 index 0000000000000..6b24f315d582c --- /dev/null +++ b/cli/workspacecreate_test.go @@ -0,0 +1,62 @@ +package cli_test + +import ( + "testing" + + "github.com/coder/coder/cli/clitest" + "github.com/coder/coder/coderd/coderdtest" + "github.com/coder/coder/provisioner/echo" + "github.com/coder/coder/provisionersdk/proto" + "github.com/stretchr/testify/require" +) + +func TestWorkspaceCreate(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) + job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, &echo.Responses{ + Parse: echo.ParseComplete, + Provision: []*proto.Provision_Response{{ + Type: &proto.Provision_Response_Complete{ + Complete: &proto.Provision_Complete{ + Resources: []*proto.Resource{{ + Name: "example", + Type: "aws_instance", + }}, + }, + }, + }}, + }) + coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) + project := coderdtest.CreateProject(t, client, user.Organization, job.ID) + cmd, root := clitest.New(t, "workspaces", "create", project.Name) + clitest.SetupConfig(t, client, root) + + console := clitest.NewConsole(t, cmd) + closeChan := make(chan struct{}) + go func() { + err := cmd.Execute() + require.NoError(t, err) + close(closeChan) + }() + + matches := []string{ + "name?", "workspace-name", + "Create workspace", "y", + } + for i := 0; i < len(matches); i += 2 { + match := matches[i] + value := matches[i+1] + _, err := console.ExpectString(match) + require.NoError(t, err) + _, err = console.SendLine(value) + require.NoError(t, err) + } + _, err := console.ExpectString("Create") + require.NoError(t, err) + <-closeChan + }) +} diff --git a/coderd/coderdtest/coderdtest.go b/coderd/coderdtest/coderdtest.go index 82295ef35af9b..4dff2a7939716 100644 --- a/coderd/coderdtest/coderdtest.go +++ b/coderd/coderdtest/coderdtest.go @@ -117,8 +117,7 @@ func CreateInitialUser(t *testing.T, client *codersdk.Client) coderd.CreateIniti Password: req.Password, }) require.NoError(t, err) - err = client.SetSessionToken(login.SessionToken) - require.NoError(t, err) + client.SessionToken = login.SessionToken return req } diff --git a/coderd/provisionerdaemons.go b/coderd/provisionerdaemons.go index 28ba99b61e061..a75730dc0a876 100644 --- a/coderd/provisionerdaemons.go +++ b/coderd/provisionerdaemons.go @@ -195,15 +195,11 @@ func (server *provisionerdServer) AcquireJob(ctx context.Context, _ *proto.Empty if err != nil { return nil, failJob(fmt.Sprintf("get project: %s", err)) } - organization, err := server.Database.GetOrganizationByID(ctx, project.OrganizationID) - if err != nil { - return nil, failJob(fmt.Sprintf("get organization: %s", err)) - } // Compute parameters for the workspace to consume. parameters, err := parameter.Compute(ctx, server.Database, parameter.ComputeScope{ ProjectImportJobID: projectVersion.ImportJobID, - OrganizationID: organization.ID, + OrganizationID: job.OrganizationID, ProjectID: uuid.NullUUID{ UUID: project.ID, Valid: true, @@ -226,6 +222,11 @@ func (server *provisionerdServer) AcquireJob(ctx context.Context, _ *proto.Empty } protoParameters = append(protoParameters, converted) } + protoParameters = append(protoParameters, &sdkproto.ParameterValue{ + DestinationScheme: sdkproto.ParameterDestination_PROVISIONER_VARIABLE, + Name: parameter.CoderWorkspaceTransition, + Value: string(workspaceHistory.Transition), + }) protoJob.Type = &proto.AcquiredJob_WorkspaceProvision_{ WorkspaceProvision: &proto.AcquiredJob_WorkspaceProvision{ diff --git a/coderd/provisionerjobs.go b/coderd/provisionerjobs.go index 9c000280f7228..a7e75aa37b321 100644 --- a/coderd/provisionerjobs.go +++ b/coderd/provisionerjobs.go @@ -112,6 +112,7 @@ func (api *api) postProvisionerImportJobByOrganization(rw http.ResponseWriter, r StorageMethod: database.ProvisionerStorageMethodFile, StorageSource: file.Hash, Type: database.ProvisionerJobTypeProjectVersionImport, + Input: []byte{'{', '}'}, }) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index 06f4023e87499..8d820b5964c31 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -73,7 +73,7 @@ func TestPostWorkspaceByUser(t *testing.T) { Password: anotherUser.Password, }) require.NoError(t, err) - err = client.SetSessionToken(token.SessionToken) + client.SessionToken = token.SessionToken require.NoError(t, err) _, err = client.CreateWorkspace(context.Background(), "", coderd.CreateWorkspaceRequest{ diff --git a/codersdk/client.go b/codersdk/client.go index de67ba1ae8058..976c31d06f3da 100644 --- a/codersdk/client.go +++ b/codersdk/client.go @@ -8,7 +8,6 @@ import ( "fmt" "io" "net/http" - "net/http/cookiejar" "net/url" "strings" @@ -28,27 +27,12 @@ func New(serverURL *url.URL) *Client { // Client is an HTTP caller for methods to the Coder API. type Client struct { - URL *url.URL + URL *url.URL + SessionToken string httpClient *http.Client } -// SetSessionToken applies the provided token to the current client. -func (c *Client) SetSessionToken(token string) error { - if c.httpClient.Jar == nil { - var err error - c.httpClient.Jar, err = cookiejar.New(nil) - if err != nil { - return err - } - } - c.httpClient.Jar.SetCookies(c.URL, []*http.Cookie{{ - Name: httpmw.AuthCookie, - Value: token, - }}) - return nil -} - // 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{}, opts ...func(r *http.Request)) (*http.Response, error) { @@ -76,6 +60,10 @@ func (c *Client) request(ctx context.Context, method, path string, body interfac if err != nil { return nil, xerrors.Errorf("create request: %w", err) } + req.AddCookie(&http.Cookie{ + Name: httpmw.AuthCookie, + Value: c.SessionToken, + }) if body != nil { req.Header.Set("Content-Type", "application/json") } From d2cbf362b487232516d827bafcad0e593e1d4bc1 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Fri, 11 Feb 2022 17:49:26 +0000 Subject: [PATCH 16/20] Add CLI test --- cli/clitest/clitest_test.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 cli/clitest/clitest_test.go diff --git a/cli/clitest/clitest_test.go b/cli/clitest/clitest_test.go new file mode 100644 index 0000000000000..71f9aeefa1bce --- /dev/null +++ b/cli/clitest/clitest_test.go @@ -0,0 +1,29 @@ +package clitest_test + +import ( + "testing" + + "github.com/coder/coder/cli/clitest" + "github.com/coder/coder/coderd/coderdtest" + "github.com/stretchr/testify/require" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + goleak.VerifyTestMain(m) +} + +func TestCli(t *testing.T) { + t.Parallel() + clitest.CreateProjectVersionSource(t, nil) + client := coderdtest.New(t) + cmd, config := clitest.New(t) + clitest.SetupConfig(t, client, config) + console := clitest.NewConsole(t, cmd) + go func() { + err := cmd.Execute() + require.NoError(t, err) + }() + _, err := console.ExpectString("coder") + require.NoError(t, err) +} From a8d00d82fe80e1e947ed290dc3ff846dd677b236 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Fri, 11 Feb 2022 19:12:17 +0000 Subject: [PATCH 17/20] Move jobs to their own APIs --- cli/projectcreate.go | 16 +- cli/workspacecreate.go | 8 +- cli/workspacecreate_test.go | 4 +- coderd/coderd.go | 47 +++-- coderd/coderdtest/coderdtest.go | 28 ++- coderd/coderdtest/coderdtest_test.go | 6 +- coderd/parameters.go | 109 ------------ coderd/projectimport.go | 185 ++++++++++++++++++++ coderd/projectimport_test.go | 162 ++++++++++++++++++ coderd/projects.go | 61 ++++++- coderd/projects_test.go | 16 +- coderd/projectversion_test.go | 6 +- coderd/provisionerdaemons_test.go | 7 +- coderd/provisionerjoblogs.go | 196 --------------------- coderd/provisionerjoblogs_test.go | 133 --------------- coderd/provisionerjobs.go | 245 ++++++++++++++++----------- coderd/provisionerjobs_test.go | 150 ++++++++++++++-- coderd/workspacehistory_test.go | 26 +-- coderd/workspaces_test.go | 14 +- codersdk/projectimport.go | 95 +++++++++++ codersdk/projectimport_test.go | 146 ++++++++++++++++ codersdk/projects_test.go | 14 +- codersdk/provisioners.go | 95 +---------- codersdk/provisioners_test.go | 62 ------- codersdk/workspaces.go | 26 +++ codersdk/workspaces_test.go | 16 +- 26 files changed, 1071 insertions(+), 802 deletions(-) delete mode 100644 coderd/parameters.go create mode 100644 coderd/projectimport.go create mode 100644 coderd/projectimport_test.go delete mode 100644 coderd/provisionerjoblogs.go delete mode 100644 coderd/provisionerjoblogs_test.go create mode 100644 codersdk/projectimport.go create mode 100644 codersdk/projectimport_test.go diff --git a/cli/projectcreate.go b/cli/projectcreate.go index 40bc3a3b68128..21e667b462996 100644 --- a/cli/projectcreate.go +++ b/cli/projectcreate.go @@ -139,7 +139,8 @@ func validateProjectVersionSource(cmd *cobra.Command, client *codersdk.Client, o return nil, err } - job, err := client.CreateProjectVersionImportProvisionerJob(cmd.Context(), organization.Name, coderd.CreateProjectImportJobRequest{ + before := time.Now() + job, err := client.CreateProjectImportJob(cmd.Context(), organization.Name, coderd.CreateProjectImportJobRequest{ StorageMethod: database.ProvisionerStorageMethodFile, StorageSource: resp.Hash, Provisioner: provisioner, @@ -148,10 +149,8 @@ func validateProjectVersionSource(cmd *cobra.Command, client *codersdk.Client, o if err != nil { return nil, err } - spin.Suffix = " Waiting for the import to complete..." - - logs, err := client.FollowProvisionerJobLogsAfter(cmd.Context(), organization.Name, job.ID, time.Time{}) + logs, err := client.ProjectImportJobLogsAfter(cmd.Context(), organization.Name, job.ID, before) if err != nil { return nil, err } @@ -164,16 +163,15 @@ func validateProjectVersionSource(cmd *cobra.Command, client *codersdk.Client, o logBuffer = append(logBuffer, log) } - job, err = client.ProvisionerJob(cmd.Context(), organization.Name, job.ID) + job, err = client.ProjectImportJob(cmd.Context(), organization.Name, job.ID) if err != nil { return nil, err } - - parameterSchemas, err := client.ProvisionerJobParameterSchemas(cmd.Context(), organization.Name, job.ID) + parameterSchemas, err := client.ProjectImportJobSchemas(cmd.Context(), organization.Name, job.ID) if err != nil { return nil, err } - parameterValues, err := client.ProvisionerJobParameterValues(cmd.Context(), organization.Name, job.ID) + parameterValues, err := client.ProjectImportJobParameters(cmd.Context(), organization.Name, job.ID) if err != nil { return nil, err } @@ -218,7 +216,7 @@ func validateProjectVersionSource(cmd *cobra.Command, client *codersdk.Client, o _, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Successfully imported project source!\n", color.HiGreenString("✓")) - resources, err := client.ProvisionerJobResources(cmd.Context(), organization.Name, job.ID) + resources, err := client.ProjectImportJobResources(cmd.Context(), organization.Name, job.ID) if err != nil { return nil, err } diff --git a/cli/workspacecreate.go b/cli/workspacecreate.go index 9eaa4941d26cc..3b4392096f0ae 100644 --- a/cli/workspacecreate.go +++ b/cli/workspacecreate.go @@ -64,15 +64,15 @@ func workspaceCreate() *cobra.Command { if err != nil { return err } - parameterSchemas, err := client.ProvisionerJobParameterSchemas(cmd.Context(), organization.Name, projectVersion.ImportJobID) + parameterSchemas, err := client.ProjectImportJobSchemas(cmd.Context(), organization.Name, projectVersion.ImportJobID) if err != nil { return err } - parameterValues, err := client.ProvisionerJobParameterValues(cmd.Context(), organization.Name, projectVersion.ImportJobID) + parameterValues, err := client.ProjectImportJobParameters(cmd.Context(), organization.Name, projectVersion.ImportJobID) if err != nil { return err } - resources, err := client.ProvisionerJobResources(cmd.Context(), organization.Name, projectVersion.ImportJobID) + resources, err := client.ProjectImportJobResources(cmd.Context(), organization.Name, projectVersion.ImportJobID) if err != nil { return err } @@ -108,7 +108,7 @@ func workspaceCreate() *cobra.Command { return err } - logs, err := client.FollowProvisionerJobLogsAfter(cmd.Context(), organization.Name, history.ProvisionJobID, time.Time{}) + logs, err := client.WorkspaceProvisionJobLogsAfter(cmd.Context(), organization.Name, history.ProvisionJobID, time.Time{}) if err != nil { return err } diff --git a/cli/workspacecreate_test.go b/cli/workspacecreate_test.go index 6b24f315d582c..1b0f0dcf2d2ff 100644 --- a/cli/workspacecreate_test.go +++ b/cli/workspacecreate_test.go @@ -17,7 +17,7 @@ func TestWorkspaceCreate(t *testing.T) { client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) _ = coderdtest.NewProvisionerDaemon(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, &echo.Responses{ + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, &echo.Responses{ Parse: echo.ParseComplete, Provision: []*proto.Provision_Response{{ Type: &proto.Provision_Response_Complete{ @@ -30,7 +30,7 @@ func TestWorkspaceCreate(t *testing.T) { }, }}, }) - coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) + coderdtest.AwaitProjectImportJob(t, client, user.Organization, job.ID) project := coderdtest.CreateProject(t, client, user.Organization, job.ID) cmd, root := clitest.New(t, "workspaces", "create", project.Name) clitest.SetupConfig(t, client, root) diff --git a/coderd/coderd.go b/coderd/coderd.go index 0a2a9d87e51a8..9669dbf92c2e1 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -107,27 +107,38 @@ func New(options *Options) http.Handler { 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("/projectimport/{organization}", func(r chi.Router) { + r.Use( + httpmw.ExtractAPIKey(options.Database, nil), + httpmw.ExtractOrganizationParam(options.Database), + ) + r.Post("/", api.postProjectImportByOrganization) + r.Route("/{provisionerjob}", func(r chi.Router) { + r.Use(httpmw.ExtractProvisionerJobParam(options.Database)) + r.Get("/", api.provisionerJobByID) + r.Get("/schemas", api.projectImportJobSchemasByID) + r.Get("/parameters", api.projectImportJobParametersByID) + r.Get("/resources", api.projectImportJobResourcesByID) + 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("/schemas", api.provisionerJobParameterSchemasByID) - r.Get("/computed", api.provisionerJobComputedParametersByID) - r.Get("/resources", api.provisionerJobResourcesByID) - r.Get("/logs", api.provisionerJobLogsByID) - }) + }) + + r.Route("/workspaceprovision/{organization}", func(r chi.Router) { + r.Use( + httpmw.ExtractAPIKey(options.Database, nil), + httpmw.ExtractOrganizationParam(options.Database), + ) + r.Route("/{provisionerjob}", func(r chi.Router) { + r.Use(httpmw.ExtractProvisionerJobParam(options.Database)) + r.Get("/", api.provisionerJobByID) + r.Get("/logs", api.provisionerJobLogsByID) }) }) + + r.Route("/provisioners/daemons", func(r chi.Router) { + r.Get("/", api.provisionerDaemons) + r.Get("/serve", api.provisionerDaemonsServe) + }) }) r.NotFound(site.Handler(options.Logger).ServeHTTP) return r diff --git a/coderd/coderdtest/coderdtest.go b/coderd/coderdtest/coderdtest.go index 4dff2a7939716..dc7f782c83748 100644 --- a/coderd/coderdtest/coderdtest.go +++ b/coderd/coderdtest/coderdtest.go @@ -28,8 +28,8 @@ import ( "github.com/coder/coder/provisionersdk/proto" ) -// New constructs a new coderd test instance. This returned Server -// should contain no side-effects. +// New constructs an in-memory coderd instance and returns +// the connected client. func New(t *testing.T) *codersdk.Client { // This can be hotswapped for a live database instance. db := databasefake.New() @@ -121,15 +121,15 @@ func CreateInitialUser(t *testing.T, client *codersdk.Client) coderd.CreateIniti return req } -// CreateProjectImportProvisionerJob creates a project import provisioner job +// CreateProjectImportJob 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 { +func CreateProjectImportJob(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{ + job, err := client.CreateProjectImportJob(context.Background(), organization, coderd.CreateProjectImportJobRequest{ StorageSource: file.Hash, StorageMethod: database.ProvisionerStorageMethodFile, Provisioner: database.ProvisionerTypeEcho, @@ -149,12 +149,24 @@ func CreateProject(t *testing.T, client *codersdk.Client, organization string, j return project } -// AwaitProvisionerJob awaits for a job to reach completed status. -func AwaitProvisionerJob(t *testing.T, client *codersdk.Client, organization string, job uuid.UUID) coderd.ProvisionerJob { +// AwaitProjectImportJob awaits for an import job to reach completed status. +func AwaitProjectImportJob(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 - provisionerJob, err = client.ProvisionerJob(context.Background(), organization, job) + provisionerJob, err = client.ProjectImportJob(context.Background(), organization, job) + require.NoError(t, err) + return provisionerJob.Status.Completed() + }, 3*time.Second, 25*time.Millisecond) + return provisionerJob +} + +// AwaitWorkspaceProvisionJob awaits for a workspace provision job to reach completed status. +func AwaitWorkspaceProvisionJob(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 + provisionerJob, err = client.WorkspaceProvisionJob(context.Background(), organization, job) require.NoError(t, err) return provisionerJob.Status.Completed() }, 3*time.Second, 25*time.Millisecond) diff --git a/coderd/coderdtest/coderdtest_test.go b/coderd/coderdtest/coderdtest_test.go index b388ea6428f43..f351bb4a4d5a4 100644 --- a/coderd/coderdtest/coderdtest_test.go +++ b/coderd/coderdtest/coderdtest_test.go @@ -22,8 +22,8 @@ func TestNew(t *testing.T) { client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) closer := coderdtest.NewProvisionerDaemon(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) - coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, nil) + coderdtest.AwaitProjectImportJob(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{ @@ -31,6 +31,6 @@ func TestNew(t *testing.T) { Transition: database.WorkspaceTransitionStart, }) require.NoError(t, err) - coderdtest.AwaitProvisionerJob(t, client, user.Organization, history.ProvisionJobID) + coderdtest.AwaitWorkspaceProvisionJob(t, client, user.Organization, history.ProvisionJobID) closer.Close() } diff --git a/coderd/parameters.go b/coderd/parameters.go deleted file mode 100644 index 0df7f112aadda..0000000000000 --- a/coderd/parameters.go +++ /dev/null @@ -1,109 +0,0 @@ -package coderd - -import ( - "database/sql" - "errors" - "fmt" - "net/http" - - "github.com/go-chi/render" - "github.com/google/uuid" - - "github.com/coder/coder/coderd/parameter" - "github.com/coder/coder/database" - "github.com/coder/coder/httpapi" -) - -// ParameterSchema represents a parameter parsed from project version source. -type ParameterSchema database.ParameterSchema - -// ParameterValue represents a set value for the scope. -type ParameterValue database.ParameterValue - -// ComputedParameterValue represents a computed parameter value. -type ComputedParameterValue parameter.ComputedValue - -// CreateParameterValueRequest is used to create a new parameter value for a scope. -type CreateParameterValueRequest struct { - Name string `json:"name" validate:"required"` - SourceValue string `json:"source_value" validate:"required"` - SourceScheme database.ParameterSourceScheme `json:"source_scheme" validate:"oneof=data,required"` - DestinationScheme database.ParameterDestinationScheme `json:"destination_scheme" validate:"oneof=environment_variable provisioner_variable,required"` -} - -// Abstracts creating parameters into a single request/response format. -// Callers are in charge of validating the requester has permissions to -// perform the creation. -func postParameterValueForScope(rw http.ResponseWriter, r *http.Request, db database.Store, scope database.ParameterScope, scopeID string) { - var createRequest CreateParameterValueRequest - if !httpapi.Read(rw, r, &createRequest) { - return - } - parameterValue, err := db.InsertParameterValue(r.Context(), database.InsertParameterValueParams{ - ID: uuid.New(), - Name: createRequest.Name, - CreatedAt: database.Now(), - UpdatedAt: database.Now(), - Scope: scope, - ScopeID: scopeID, - SourceScheme: createRequest.SourceScheme, - SourceValue: createRequest.SourceValue, - DestinationScheme: createRequest.DestinationScheme, - }) - if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: fmt.Sprintf("insert parameter value: %s", err), - }) - return - } - - render.Status(r, http.StatusCreated) - render.JSON(rw, r, parameterValue) -} - -// Abstracts returning parameters for a scope into a standardized -// request/response format. Callers are responsible for checking -// requester permissions. -func parametersForScope(rw http.ResponseWriter, r *http.Request, db database.Store, req database.GetParameterValuesByScopeParams) { - parameterValues, err := db.GetParameterValuesByScope(r.Context(), req) - if errors.Is(err, sql.ErrNoRows) { - err = nil - parameterValues = []database.ParameterValue{} - } - if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: fmt.Sprintf("get parameter values: %s", err), - }) - return - } - - apiParameterValues := make([]ParameterValue, 0, len(parameterValues)) - for _, parameterValue := range parameterValues { - apiParameterValues = append(apiParameterValues, convertParameterValue(parameterValue)) - } - - render.Status(r, http.StatusOK) - render.JSON(rw, r, apiParameterValues) -} - -// Returns parameters for a specific scope. -func computedParametersForScope(rw http.ResponseWriter, r *http.Request, db database.Store, scope parameter.ComputeScope) { - values, err := parameter.Compute(r.Context(), db, scope, ¶meter.ComputeOptions{ - // We *never* want to send the client secret parameter values. - HideRedisplayValues: true, - }) - if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: fmt.Sprintf("compute values: %s", err), - }) - return - } - - render.Status(r, http.StatusOK) - render.JSON(rw, r, values) -} - -func convertParameterValue(parameterValue database.ParameterValue) ParameterValue { - parameterValue.SourceValue = "" - return ParameterValue(parameterValue) -} diff --git a/coderd/projectimport.go b/coderd/projectimport.go new file mode 100644 index 0000000000000..d3032f9b1eee5 --- /dev/null +++ b/coderd/projectimport.go @@ -0,0 +1,185 @@ +package coderd + +import ( + "database/sql" + "errors" + "fmt" + "net/http" + + "github.com/go-chi/render" + "github.com/google/uuid" + + "github.com/coder/coder/coderd/parameter" + "github.com/coder/coder/database" + "github.com/coder/coder/httpapi" + "github.com/coder/coder/httpmw" +) + +// ParameterSchema represents a parameter parsed from project version source. +type ParameterSchema database.ParameterSchema + +// ComputedParameterValue represents a computed parameter value. +type ComputedParameterValue parameter.ComputedValue + +// ProjectImportJobResource is a resource created by a project import job. +type ProjectImportJobResource database.ProjectImportJobResource + +// CreateProjectImportJobRequest provides options to create a project import job. +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"` + // ParameterValues allows for additional parameters to be provided + // during the dry-run provision stage. + ParameterValues []CreateParameterValueRequest `json:"parameter_values"` +} + +// Create a new project import job! +func (api *api) postProjectImportByOrganization(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 + } + + jobID := uuid.New() + for _, parameterValue := range req.ParameterValues { + _, err = api.Database.InsertParameterValue(r.Context(), database.InsertParameterValueParams{ + ID: uuid.New(), + Name: parameterValue.Name, + CreatedAt: database.Now(), + UpdatedAt: database.Now(), + Scope: database.ParameterScopeImportJob, + ScopeID: jobID.String(), + SourceScheme: parameterValue.SourceScheme, + SourceValue: parameterValue.SourceValue, + DestinationScheme: parameterValue.DestinationScheme, + }) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: fmt.Sprintf("insert parameter value: %s", err), + }) + return + } + } + + job, err := api.Database.InsertProvisionerJob(r.Context(), database.InsertProvisionerJobParams{ + ID: jobID, + 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: []byte{'{', '}'}, + }) + 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)) +} + +// Returns imported parameter schemas from a completed job! +func (api *api) projectImportJobSchemasByID(rw http.ResponseWriter, r *http.Request) { + job := httpmw.ProvisionerJobParam(r) + if !convertProvisionerJob(job).Status.Completed() { + httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + Message: "Job hasn't completed!", + }) + return + } + + schemas, err := api.Database.GetParameterSchemasByJobID(r.Context(), job.ID) + if errors.Is(err, sql.ErrNoRows) { + err = nil + } + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: fmt.Sprintf("list parameter schemas: %s", err), + }) + return + } + if schemas == nil { + schemas = []database.ParameterSchema{} + } + render.Status(r, http.StatusOK) + render.JSON(rw, r, schemas) +} + +// Returns computed parameters for an import job by ID. +func (api *api) projectImportJobParametersByID(rw http.ResponseWriter, r *http.Request) { + apiKey := httpmw.APIKey(r) + job := httpmw.ProvisionerJobParam(r) + if !convertProvisionerJob(job).Status.Completed() { + httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + Message: "Job hasn't completed!", + }) + return + } + values, err := parameter.Compute(r.Context(), api.Database, parameter.ComputeScope{ + ProjectImportJobID: job.ID, + OrganizationID: job.OrganizationID, + UserID: apiKey.UserID, + }, ¶meter.ComputeOptions{ + // We *never* want to send the client secret parameter values. + HideRedisplayValues: true, + }) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: fmt.Sprintf("compute values: %s", err), + }) + return + } + if values == nil { + values = []parameter.ComputedValue{} + } + render.Status(r, http.StatusOK) + render.JSON(rw, r, values) +} + +// Returns resources for an import job by ID. +func (api *api) projectImportJobResourcesByID(rw http.ResponseWriter, r *http.Request) { + job := httpmw.ProvisionerJobParam(r) + if !convertProvisionerJob(job).Status.Completed() { + httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ + Message: "Job hasn't completed!", + }) + return + } + resources, err := api.Database.GetProjectImportJobResourcesByJobID(r.Context(), job.ID) + if errors.Is(err, sql.ErrNoRows) { + err = nil + } + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: fmt.Sprintf("get project import job resources: %s", err), + }) + return + } + if resources == nil { + resources = []database.ProjectImportJobResource{} + } + render.Status(r, http.StatusOK) + render.JSON(rw, r, resources) +} diff --git a/coderd/projectimport_test.go b/coderd/projectimport_test.go new file mode 100644 index 0000000000000..06140190f51d5 --- /dev/null +++ b/coderd/projectimport_test.go @@ -0,0 +1,162 @@ +package coderd_test + +import ( + "context" + "net/http" + "testing" + + "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" + "github.com/stretchr/testify/require" +) + +func TestPostProjectImportByOrganization(t *testing.T) { + t.Parallel() + t.Run("FileNotFound", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + user := coderdtest.CreateInitialUser(t, client) + _, err := client.CreateProjectImportJob(context.Background(), user.Organization, coderd.CreateProjectImportJobRequest{ + StorageMethod: database.ProvisionerStorageMethodFile, + StorageSource: "bananas", + Provisioner: database.ProvisionerTypeEcho, + }) + require.Error(t, err) + }) + t.Run("Create", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + user := coderdtest.CreateInitialUser(t, client) + _ = coderdtest.CreateProjectImportJob(t, client, user.Organization, nil) + }) +} + +func TestProjectImportJobSchemasByID(t *testing.T) { + t.Parallel() + t.Run("ListRunning", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + user := coderdtest.CreateInitialUser(t, client) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, nil) + _, err := client.ProjectImportJobSchemas(context.Background(), user.Organization, job.ID) + var apiErr *codersdk.Error + require.ErrorAs(t, err, &apiErr) + require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) + }) + t.Run("List", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + user := coderdtest.CreateInitialUser(t, client) + coderdtest.NewProvisionerDaemon(t, client) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, &echo.Responses{ + Parse: []*proto.Parse_Response{{ + Type: &proto.Parse_Response_Complete{ + Complete: &proto.Parse_Complete{ + ParameterSchemas: []*proto.ParameterSchema{{ + Name: "example", + DefaultDestination: &proto.ParameterDestination{ + Scheme: proto.ParameterDestination_PROVISIONER_VARIABLE, + }, + }}, + }, + }, + }}, + Provision: echo.ProvisionComplete, + }) + coderdtest.AwaitProjectImportJob(t, client, user.Organization, job.ID) + schemas, err := client.ProjectImportJobSchemas(context.Background(), user.Organization, job.ID) + require.NoError(t, err) + require.NotNil(t, schemas) + require.Len(t, schemas, 1) + }) +} + +func TestProjectImportJobParametersByID(t *testing.T) { + t.Parallel() + t.Run("ListRunning", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + user := coderdtest.CreateInitialUser(t, client) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, nil) + _, err := client.ProjectImportJobSchemas(context.Background(), user.Organization, job.ID) + var apiErr *codersdk.Error + require.ErrorAs(t, err, &apiErr) + require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) + }) + t.Run("List", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + user := coderdtest.CreateInitialUser(t, client) + coderdtest.NewProvisionerDaemon(t, client) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, &echo.Responses{ + Parse: []*proto.Parse_Response{{ + Type: &proto.Parse_Response_Complete{ + Complete: &proto.Parse_Complete{ + ParameterSchemas: []*proto.ParameterSchema{{ + Name: "example", + RedisplayValue: true, + DefaultSource: &proto.ParameterSource{ + Scheme: proto.ParameterSource_DATA, + Value: "hello", + }, + DefaultDestination: &proto.ParameterDestination{ + Scheme: proto.ParameterDestination_PROVISIONER_VARIABLE, + }, + }}, + }, + }, + }}, + Provision: echo.ProvisionComplete, + }) + coderdtest.AwaitProjectImportJob(t, client, user.Organization, job.ID) + params, err := client.ProjectImportJobParameters(context.Background(), user.Organization, job.ID) + require.NoError(t, err) + require.NotNil(t, params) + require.Len(t, params, 1) + require.Equal(t, "hello", params[0].SourceValue) + }) +} + +func TestProjectImportJobResourcesByID(t *testing.T) { + t.Parallel() + t.Run("ListRunning", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + user := coderdtest.CreateInitialUser(t, client) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, nil) + _, err := client.ProjectImportJobResources(context.Background(), user.Organization, job.ID) + var apiErr *codersdk.Error + require.ErrorAs(t, err, &apiErr) + require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) + }) + t.Run("List", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + user := coderdtest.CreateInitialUser(t, client) + coderdtest.NewProvisionerDaemon(t, client) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, &echo.Responses{ + Parse: echo.ParseComplete, + Provision: []*proto.Provision_Response{{ + Type: &proto.Provision_Response_Complete{ + Complete: &proto.Provision_Complete{ + Resources: []*proto.Resource{{ + Name: "some", + Type: "example", + }}, + }, + }, + }}, + }) + coderdtest.AwaitProjectImportJob(t, client, user.Organization, job.ID) + resources, err := client.ProjectImportJobResources(context.Background(), user.Organization, job.ID) + require.NoError(t, err) + require.NotNil(t, resources) + require.Len(t, resources, 2) + require.Equal(t, "some", resources[0].Name) + require.Equal(t, "example", resources[0].Type) + }) +} diff --git a/coderd/projects.go b/coderd/projects.go index f50c192e6a006..90c6ce243888b 100644 --- a/coderd/projects.go +++ b/coderd/projects.go @@ -16,6 +16,17 @@ import ( "github.com/coder/coder/httpmw" ) +// ParameterValue represents a set value for the scope. +type ParameterValue database.ParameterValue + +// CreateParameterValueRequest is used to create a new parameter value for a scope. +type CreateParameterValueRequest struct { + Name string `json:"name" validate:"required"` + SourceValue string `json:"source_value" validate:"required"` + SourceScheme database.ParameterSourceScheme `json:"source_scheme" validate:"oneof=data,required"` + DestinationScheme database.ParameterDestinationScheme `json:"destination_scheme" validate:"oneof=environment_variable provisioner_variable,required"` +} + // Project is the JSON representation of a Coder project. // This type matches the database object for now, but is // abstracted for ease of change later on. @@ -177,16 +188,60 @@ func (*api) projectByOrganization(rw http.ResponseWriter, r *http.Request) { // This should validate the calling user has permissions! func (api *api) postParametersByProject(rw http.ResponseWriter, r *http.Request) { project := httpmw.ProjectParam(r) + var createRequest CreateParameterValueRequest + if !httpapi.Read(rw, r, &createRequest) { + return + } + parameterValue, err := api.Database.InsertParameterValue(r.Context(), database.InsertParameterValueParams{ + ID: uuid.New(), + Name: createRequest.Name, + CreatedAt: database.Now(), + UpdatedAt: database.Now(), + Scope: database.ParameterScopeProject, + ScopeID: project.ID.String(), + SourceScheme: createRequest.SourceScheme, + SourceValue: createRequest.SourceValue, + DestinationScheme: createRequest.DestinationScheme, + }) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: fmt.Sprintf("insert parameter value: %s", err), + }) + return + } - postParameterValueForScope(rw, r, api.Database, database.ParameterScopeProject, project.ID.String()) + render.Status(r, http.StatusCreated) + render.JSON(rw, r, parameterValue) } // Lists parameters for a project. func (api *api) parametersByProject(rw http.ResponseWriter, r *http.Request) { project := httpmw.ProjectParam(r) - - parametersForScope(rw, r, api.Database, database.GetParameterValuesByScopeParams{ + parameterValues, err := api.Database.GetParameterValuesByScope(r.Context(), database.GetParameterValuesByScopeParams{ Scope: database.ParameterScopeProject, ScopeID: project.ID.String(), }) + if errors.Is(err, sql.ErrNoRows) { + err = nil + parameterValues = []database.ParameterValue{} + } + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: fmt.Sprintf("get parameter values: %s", err), + }) + return + } + + apiParameterValues := make([]ParameterValue, 0, len(parameterValues)) + for _, parameterValue := range parameterValues { + apiParameterValues = append(apiParameterValues, convertParameterValue(parameterValue)) + } + + render.Status(r, http.StatusOK) + render.JSON(rw, r, apiParameterValues) +} + +func convertParameterValue(parameterValue database.ParameterValue) ParameterValue { + parameterValue.SourceValue = "" + return ParameterValue(parameterValue) } diff --git a/coderd/projects_test.go b/coderd/projects_test.go index 42efedb939f83..9ea0cbf87f443 100644 --- a/coderd/projects_test.go +++ b/coderd/projects_test.go @@ -30,7 +30,7 @@ func TestProjects(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, nil) _ = coderdtest.CreateProject(t, client, user.Organization, job.ID) projects, err := client.Projects(context.Background(), "") require.NoError(t, err) @@ -54,7 +54,7 @@ func TestProjectsByOrganization(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, nil) _ = coderdtest.CreateProject(t, client, user.Organization, job.ID) projects, err := client.Projects(context.Background(), "") require.NoError(t, err) @@ -68,7 +68,7 @@ func TestPostProjectsByOrganization(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, nil) _ = coderdtest.CreateProject(t, client, user.Organization, job.ID) }) @@ -76,7 +76,7 @@ func TestPostProjectsByOrganization(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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, @@ -94,7 +94,7 @@ func TestProjectByOrganization(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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) @@ -107,7 +107,7 @@ func TestPostParametersByProject(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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", @@ -125,7 +125,7 @@ func TestParametersByProject(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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) @@ -136,7 +136,7 @@ func TestParametersByProject(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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", diff --git a/coderd/projectversion_test.go b/coderd/projectversion_test.go index a842f3ec277cf..d288b53552086 100644 --- a/coderd/projectversion_test.go +++ b/coderd/projectversion_test.go @@ -16,7 +16,7 @@ func TestProjectVersionsByOrganization(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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) @@ -31,7 +31,7 @@ func TestProjectVersionByOrganizationAndName(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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) @@ -44,7 +44,7 @@ func TestPostProjectVersionByOrganization(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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, diff --git a/coderd/provisionerdaemons_test.go b/coderd/provisionerdaemons_test.go index 4c62c7c96e355..b0f1ff54c9b65 100644 --- a/coderd/provisionerdaemons_test.go +++ b/coderd/provisionerdaemons_test.go @@ -11,11 +11,8 @@ import ( ) func TestProvisionerDaemons(t *testing.T) { - // Tests for properly processing specific job - // types should be placed in their respective - // resource location. - // - // eg. project import is a project-related job + // Tests for properly processing specific job types should be placed + // in their respective files. t.Parallel() client := coderdtest.New(t) diff --git a/coderd/provisionerjoblogs.go b/coderd/provisionerjoblogs.go deleted file mode 100644 index f47c7825a300f..0000000000000 --- a/coderd/provisionerjoblogs.go +++ /dev/null @@ -1,196 +0,0 @@ -package coderd - -import ( - "context" - "database/sql" - "encoding/json" - "errors" - "fmt" - "net/http" - "strconv" - "time" - - "github.com/go-chi/render" - "github.com/google/uuid" - - "cdr.dev/slog" - "github.com/coder/coder/database" - "github.com/coder/coder/httpapi" - "github.com/coder/coder/httpmw" -) - -// ProvisionerJobLog represents a single log from a provisioner job. -type ProvisionerJobLog struct { - ID uuid.UUID - CreatedAt time.Time `json:"created_at"` - Source database.LogSource `json:"log_source"` - Level database.LogLevel `json:"log_level"` - Output string `json:"output"` -} - -// Returns provisioner logs based on query parameters. -// The intended usage for a client to stream all logs (with JS API): -// const timestamp = new Date().getTime(); -// 1. GET /logs?before= -// 2. GET /logs?after=&follow -// The combination of these responses should provide all current logs -// to the consumer, and future logs are streamed in the follow request. -func (api *api) provisionerJobLogsByID(rw http.ResponseWriter, r *http.Request) { - follow := r.URL.Query().Has("follow") - afterRaw := r.URL.Query().Get("after") - beforeRaw := r.URL.Query().Get("before") - if beforeRaw != "" && follow { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ - Message: "before cannot be used with follow", - }) - return - } - - var after time.Time - // Only fetch logs created after the time provided. - if afterRaw != "" { - afterMS, err := strconv.ParseInt(afterRaw, 10, 64) - if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ - Message: fmt.Sprintf("unable to parse after %q: %s", afterRaw, err), - }) - return - } - after = time.UnixMilli(afterMS) - } else { - if follow { - after = database.Now() - } - } - var before time.Time - // Only fetch logs created before the time provided. - if beforeRaw != "" { - beforeMS, err := strconv.ParseInt(beforeRaw, 10, 64) - if err != nil { - httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ - Message: fmt.Sprintf("unable to parse before %q: %s", beforeRaw, err), - }) - return - } - before = time.UnixMilli(beforeMS) - } else { - before = database.Now() - } - - job := httpmw.ProvisionerJobParam(r) - if !follow { - logs, err := api.Database.GetProvisionerLogsByIDBetween(r.Context(), database.GetProvisionerLogsByIDBetweenParams{ - JobID: job.ID, - CreatedAfter: after, - CreatedBefore: before, - }) - if errors.Is(err, sql.ErrNoRows) { - err = nil - } - if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: fmt.Sprintf("get provisioner logs: %s", err), - }) - return - } - if logs == nil { - logs = []database.ProvisionerJobLog{} - } - render.Status(r, http.StatusOK) - render.JSON(rw, r, logs) - return - } - - bufferedLogs := make(chan database.ProvisionerJobLog, 128) - closeSubscribe, err := api.Pubsub.Subscribe(provisionerJobLogsChannel(job.ID), func(ctx context.Context, message []byte) { - var logs []database.ProvisionerJobLog - err := json.Unmarshal(message, &logs) - if err != nil { - api.Logger.Warn(r.Context(), fmt.Sprintf("invalid provisioner job log on channel %q: %s", provisionerJobLogsChannel(job.ID), err.Error())) - return - } - - for _, log := range logs { - select { - case bufferedLogs <- log: - default: - // If this overflows users could miss logs streaming. This can happen - // if a database request takes a long amount of time, and we get a lot of logs. - api.Logger.Warn(r.Context(), "provisioner job log overflowing channel") - } - } - }) - if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: fmt.Sprintf("subscribe to provisioner job logs: %s", err), - }) - return - } - defer closeSubscribe() - - provisionerJobLogs, err := api.Database.GetProvisionerLogsByIDBetween(r.Context(), database.GetProvisionerLogsByIDBetweenParams{ - JobID: job.ID, - CreatedAfter: after, - CreatedBefore: before, - }) - if errors.Is(err, sql.ErrNoRows) { - err = nil - provisionerJobLogs = []database.ProvisionerJobLog{} - } - if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: fmt.Sprint("get provisioner job logs: %w", err), - }) - return - } - - // "follow" uses the ndjson format to stream data. - // See: https://canjs.com/doc/can-ndjson-stream.html - rw.Header().Set("Content-Type", "application/stream+json") - rw.WriteHeader(http.StatusOK) - rw.(http.Flusher).Flush() - - // The Go stdlib JSON encoder appends a newline character after message write. - encoder := json.NewEncoder(rw) - - for _, provisionerJobLog := range provisionerJobLogs { - err = encoder.Encode(convertProvisionerJobLog(provisionerJobLog)) - if err != nil { - return - } - } - - ticker := time.NewTicker(250 * time.Millisecond) - defer ticker.Stop() - for { - select { - case <-r.Context().Done(): - return - case log := <-bufferedLogs: - err = encoder.Encode(convertProvisionerJobLog(log)) - if err != nil { - return - } - rw.(http.Flusher).Flush() - case <-ticker.C: - job, err := api.Database.GetProvisionerJobByID(r.Context(), job.ID) - if err != nil { - api.Logger.Warn(r.Context(), "streaming job logs; checking if completed", slog.Error(err), slog.F("job_id", job.ID.String())) - continue - } - if convertProvisionerJob(job).Status.Completed() { - return - } - } - } -} - -func convertProvisionerJobLog(provisionerJobLog database.ProvisionerJobLog) ProvisionerJobLog { - return ProvisionerJobLog{ - ID: provisionerJobLog.ID, - CreatedAt: provisionerJobLog.CreatedAt, - Source: provisionerJobLog.Source, - Level: provisionerJobLog.Level, - Output: provisionerJobLog.Output, - } -} diff --git a/coderd/provisionerjoblogs_test.go b/coderd/provisionerjoblogs_test.go deleted file mode 100644 index 1667d6feb74d5..0000000000000 --- a/coderd/provisionerjoblogs_test.go +++ /dev/null @@ -1,133 +0,0 @@ -package coderd_test - -import ( - "context" - "testing" - "time" - - "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" - "github.com/coder/coder/provisionersdk/proto" -) - -func TestProvisionerJobLogsByName(t *testing.T) { - t.Parallel() - t.Run("List", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t) - user := coderdtest.CreateInitialUser(t, client) - coderdtest.NewProvisionerDaemon(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, &echo.Responses{ - Parse: echo.ParseComplete, - Provision: []*proto.Provision_Response{{ - Type: &proto.Provision_Response_Log{ - Log: &proto.Log{ - Level: proto.LogLevel_INFO, - Output: "log-output", - }, - }, - }, { - Type: &proto.Provision_Response_Complete{ - Complete: &proto.Provision_Complete{}, - }, - }}, - }) - 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: project.ActiveVersionID, - Transition: database.WorkspaceTransitionStart, - }) - require.NoError(t, err) - coderdtest.AwaitProvisionerJob(t, client, user.Organization, history.ProvisionJobID) - // Return the log after completion! - logs, err := client.ProvisionerJobLogs(context.Background(), user.Organization, history.ProvisionJobID) - require.NoError(t, err) - require.NotNil(t, logs) - require.Len(t, logs, 1) - }) - - t.Run("StreamAfterComplete", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t) - user := coderdtest.CreateInitialUser(t, client) - coderdtest.NewProvisionerDaemon(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, &echo.Responses{ - Parse: echo.ParseComplete, - Provision: []*proto.Provision_Response{{ - Type: &proto.Provision_Response_Log{ - Log: &proto.Log{ - Level: proto.LogLevel_INFO, - Output: "log-output", - }, - }, - }, { - Type: &proto.Provision_Response_Complete{ - Complete: &proto.Provision_Complete{}, - }, - }}, - }) - 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: project.ActiveVersionID, - Transition: database.WorkspaceTransitionStart, - }) - require.NoError(t, err) - coderdtest.AwaitProvisionerJob(t, client, user.Organization, history.ProvisionJobID) - - logs, err := client.FollowProvisionerJobLogsAfter(context.Background(), user.Organization, history.ProvisionJobID, before) - require.NoError(t, err) - log, ok := <-logs - require.True(t, ok) - require.Equal(t, "log-output", log.Output) - // Make sure the channel automatically closes! - _, ok = <-logs - require.False(t, ok) - }) - - t.Run("StreamWhileRunning", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t) - user := coderdtest.CreateInitialUser(t, client) - coderdtest.NewProvisionerDaemon(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, &echo.Responses{ - Parse: echo.ParseComplete, - Provision: []*proto.Provision_Response{{ - Type: &proto.Provision_Response_Log{ - Log: &proto.Log{ - Level: proto.LogLevel_INFO, - Output: "log-output", - }, - }, - }, { - Type: &proto.Provision_Response_Complete{ - Complete: &proto.Provision_Complete{}, - }, - }}, - }) - 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: project.ActiveVersionID, - Transition: database.WorkspaceTransitionStart, - }) - require.NoError(t, err) - logs, err := client.FollowProvisionerJobLogsAfter(context.Background(), user.Organization, history.ProvisionJobID, before) - require.NoError(t, err) - log := <-logs - require.Equal(t, "log-output", log.Output) - // Make sure the channel automatically closes! - _, ok := <-logs - require.False(t, ok) - }) -} diff --git a/coderd/provisionerjobs.go b/coderd/provisionerjobs.go index a7e75aa37b321..a817b39f24873 100644 --- a/coderd/provisionerjobs.go +++ b/coderd/provisionerjobs.go @@ -1,23 +1,25 @@ package coderd import ( + "context" "database/sql" + "encoding/json" "errors" "fmt" "net/http" + "strconv" "time" "github.com/go-chi/render" "github.com/google/uuid" - "github.com/coder/coder/coderd/parameter" + "cdr.dev/slog" + "github.com/coder/coder/database" "github.com/coder/coder/httpapi" "github.com/coder/coder/httpmw" ) -type ProjectImportJobResource database.ProjectImportJobResource - type ProvisionerJobStatus string // Completed returns whether the job is still processing. @@ -46,147 +48,186 @@ 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"` - ParameterValues []CreateParameterValueRequest `json:"parameter_values"` +// ProvisionerJobLog represents a single log from a provisioner job. +type ProvisionerJobLog struct { + ID uuid.UUID + CreatedAt time.Time `json:"created_at"` + Source database.LogSource `json:"log_source"` + Level database.LogLevel `json:"log_level"` + Output string `json:"output"` } -func (*api) provisionerJobByOrganization(rw http.ResponseWriter, r *http.Request) { +func (*api) provisionerJobByID(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) { +// Returns provisioner logs based on query parameters. +// The intended usage for a client to stream all logs (with JS API): +// const timestamp = new Date().getTime(); +// 1. GET /logs?before= +// 2. GET /logs?after=&follow +// The combination of these responses should provide all current logs +// to the consumer, and future logs are streamed in the follow request. +func (api *api) provisionerJobLogsByID(rw http.ResponseWriter, r *http.Request) { + follow := r.URL.Query().Has("follow") + afterRaw := r.URL.Query().Get("after") + beforeRaw := r.URL.Query().Get("before") + if beforeRaw != "" && follow { httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ - Message: "file not found", + Message: "before cannot be used with follow", }) return } - if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: fmt.Sprintf("get file: %s", err), - }) - return + + var after time.Time + // Only fetch logs created after the time provided. + if afterRaw != "" { + afterMS, err := strconv.ParseInt(afterRaw, 10, 64) + if err != nil { + httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + Message: fmt.Sprintf("unable to parse after %q: %s", afterRaw, err), + }) + return + } + after = time.UnixMilli(afterMS) + } else { + if follow { + after = database.Now() + } + } + var before time.Time + // Only fetch logs created before the time provided. + if beforeRaw != "" { + beforeMS, err := strconv.ParseInt(beforeRaw, 10, 64) + if err != nil { + httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ + Message: fmt.Sprintf("unable to parse before %q: %s", beforeRaw, err), + }) + return + } + before = time.UnixMilli(beforeMS) + } else { + before = database.Now() } - jobID := uuid.New() - for _, parameterValue := range req.ParameterValues { - _, err = api.Database.InsertParameterValue(r.Context(), database.InsertParameterValueParams{ - ID: uuid.New(), - Name: parameterValue.Name, - CreatedAt: database.Now(), - UpdatedAt: database.Now(), - Scope: database.ParameterScopeImportJob, - ScopeID: jobID.String(), - SourceScheme: parameterValue.SourceScheme, - SourceValue: parameterValue.SourceValue, - DestinationScheme: parameterValue.DestinationScheme, + job := httpmw.ProvisionerJobParam(r) + if !follow { + logs, err := api.Database.GetProvisionerLogsByIDBetween(r.Context(), database.GetProvisionerLogsByIDBetweenParams{ + JobID: job.ID, + CreatedAfter: after, + CreatedBefore: before, }) + if errors.Is(err, sql.ErrNoRows) { + err = nil + } if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: fmt.Sprintf("insert parameter value: %s", err), + Message: fmt.Sprintf("get provisioner logs: %s", err), }) return } + if logs == nil { + logs = []database.ProvisionerJobLog{} + } + render.Status(r, http.StatusOK) + render.JSON(rw, r, logs) + return } - job, err := api.Database.InsertProvisionerJob(r.Context(), database.InsertProvisionerJobParams{ - ID: jobID, - 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: []byte{'{', '}'}, + bufferedLogs := make(chan database.ProvisionerJobLog, 128) + closeSubscribe, err := api.Pubsub.Subscribe(provisionerJobLogsChannel(job.ID), func(ctx context.Context, message []byte) { + var logs []database.ProvisionerJobLog + err := json.Unmarshal(message, &logs) + if err != nil { + api.Logger.Warn(r.Context(), fmt.Sprintf("invalid provisioner job log on channel %q: %s", provisionerJobLogsChannel(job.ID), err.Error())) + return + } + + for _, log := range logs { + select { + case bufferedLogs <- log: + default: + // If this overflows users could miss logs streaming. This can happen + // if a database request takes a long amount of time, and we get a lot of logs. + api.Logger.Warn(r.Context(), "provisioner job log overflowing channel") + } + } }) 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)) -} - -// Return parsed parameter schemas for a job. -func (api *api) provisionerJobParameterSchemasByID(rw http.ResponseWriter, r *http.Request) { - job := httpmw.ProvisionerJobParam(r) - if !convertProvisionerJob(job).Status.Completed() { - httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ - Message: "Job hasn't completed!", + Message: fmt.Sprintf("subscribe to provisioner job logs: %s", err), }) return } + defer closeSubscribe() - schemas, err := api.Database.GetParameterSchemasByJobID(r.Context(), job.ID) + provisionerJobLogs, err := api.Database.GetProvisionerLogsByIDBetween(r.Context(), database.GetProvisionerLogsByIDBetweenParams{ + JobID: job.ID, + CreatedAfter: after, + CreatedBefore: before, + }) if errors.Is(err, sql.ErrNoRows) { err = nil + provisionerJobLogs = []database.ProvisionerJobLog{} } if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: fmt.Sprintf("list parameter schemas: %s", err), + Message: fmt.Sprint("get provisioner job logs: %w", err), }) return } - render.Status(r, http.StatusOK) - render.JSON(rw, r, schemas) -} + // "follow" uses the ndjson format to stream data. + // See: https://canjs.com/doc/can-ndjson-stream.html + rw.Header().Set("Content-Type", "application/stream+json") + rw.WriteHeader(http.StatusOK) + rw.(http.Flusher).Flush() -func (api *api) provisionerJobComputedParametersByID(rw http.ResponseWriter, r *http.Request) { - apiKey := httpmw.APIKey(r) - job := httpmw.ProvisionerJobParam(r) - if !convertProvisionerJob(job).Status.Completed() { - httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ - Message: "Job hasn't completed!", - }) - return - } - computedParametersForScope(rw, r, api.Database, parameter.ComputeScope{ - ProjectImportJobID: job.ID, - OrganizationID: job.OrganizationID, - UserID: apiKey.UserID, - }) -} + // The Go stdlib JSON encoder appends a newline character after message write. + encoder := json.NewEncoder(rw) -func (api *api) provisionerJobResourcesByID(rw http.ResponseWriter, r *http.Request) { - job := httpmw.ProvisionerJobParam(r) - if !convertProvisionerJob(job).Status.Completed() { - httpapi.Write(rw, http.StatusPreconditionFailed, httpapi.Response{ - Message: "Job hasn't completed!", - }) - return - } - resources, err := api.Database.GetProjectImportJobResourcesByJobID(r.Context(), job.ID) - if errors.Is(err, sql.ErrNoRows) { - err = nil + for _, provisionerJobLog := range provisionerJobLogs { + err = encoder.Encode(convertProvisionerJobLog(provisionerJobLog)) + if err != nil { + return + } } - if err != nil { - httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: fmt.Sprintf("get project import job resources: %s", err), - }) - return + + ticker := time.NewTicker(250 * time.Millisecond) + defer ticker.Stop() + for { + select { + case <-r.Context().Done(): + return + case log := <-bufferedLogs: + err = encoder.Encode(convertProvisionerJobLog(log)) + if err != nil { + return + } + rw.(http.Flusher).Flush() + case <-ticker.C: + job, err := api.Database.GetProvisionerJobByID(r.Context(), job.ID) + if err != nil { + api.Logger.Warn(r.Context(), "streaming job logs; checking if completed", slog.Error(err), slog.F("job_id", job.ID.String())) + continue + } + if convertProvisionerJob(job).Status.Completed() { + return + } + } } +} - render.Status(r, http.StatusOK) - render.JSON(rw, r, resources) +func convertProvisionerJobLog(provisionerJobLog database.ProvisionerJobLog) ProvisionerJobLog { + return ProvisionerJobLog{ + ID: provisionerJobLog.ID, + CreatedAt: provisionerJobLog.CreatedAt, + Source: provisionerJobLog.Source, + Level: provisionerJobLog.Level, + Output: provisionerJobLog.Output, + } } func convertProvisionerJob(provisionerJob database.ProvisionerJob) ProvisionerJob { diff --git a/coderd/provisionerjobs_test.go b/coderd/provisionerjobs_test.go index bb4831362a3bf..8a45d7b97f42e 100644 --- a/coderd/provisionerjobs_test.go +++ b/coderd/provisionerjobs_test.go @@ -25,7 +25,7 @@ func TestPostProvisionerImportJobByOrganization(t *testing.T) { user := coderdtest.CreateInitialUser(t, client) _ = coderdtest.NewProvisionerDaemon(t, client) before := time.Now() - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, &echo.Responses{ + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, &echo.Responses{ Parse: []*proto.Parse_Response{{ Type: &proto.Parse_Response_Complete{ Complete: &proto.Parse_Complete{ @@ -44,7 +44,7 @@ func TestPostProvisionerImportJobByOrganization(t *testing.T) { }, }}, }) - logs, err := client.FollowProvisionerJobLogsAfter(context.Background(), user.Organization, job.ID, before) + logs, err := client.ProjectImportJobLogsAfter(context.Background(), user.Organization, job.ID, before) require.NoError(t, err) for { log, ok := <-logs @@ -76,7 +76,7 @@ func TestPostProvisionerImportJobByOrganization(t *testing.T) { require.NoError(t, err) file, err := client.UploadFile(context.Background(), codersdk.ContentTypeTar, data) require.NoError(t, err) - job, err := client.CreateProjectVersionImportProvisionerJob(context.Background(), user.Organization, coderd.CreateProjectImportJobRequest{ + job, err := client.CreateProjectImportJob(context.Background(), user.Organization, coderd.CreateProjectImportJobRequest{ StorageSource: file.Hash, StorageMethod: database.ProvisionerStorageMethodFile, Provisioner: database.ProvisionerTypeEcho, @@ -88,8 +88,8 @@ func TestPostProvisionerImportJobByOrganization(t *testing.T) { }}, }) require.NoError(t, err) - job = coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) - values, err := client.ProvisionerJobParameterValues(context.Background(), user.Organization, job.ID) + job = coderdtest.AwaitProjectImportJob(t, client, user.Organization, job.ID) + values, err := client.ProjectImportJobParameters(context.Background(), user.Organization, job.ID) require.NoError(t, err) require.Equal(t, "somevalue", values[0].SourceValue) }) @@ -101,8 +101,8 @@ func TestProvisionerJobParametersByID(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) - _, err := client.ProvisionerJobParameterValues(context.Background(), user.Organization, job.ID) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, nil) + _, err := client.ProjectImportJobParameters(context.Background(), user.Organization, job.ID) var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusPreconditionFailed, apiErr.StatusCode()) @@ -113,7 +113,7 @@ func TestProvisionerJobParametersByID(t *testing.T) { client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) _ = coderdtest.NewProvisionerDaemon(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, &echo.Responses{ + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, &echo.Responses{ Parse: []*proto.Parse_Response{{ Type: &proto.Parse_Response_Complete{ Complete: &proto.Parse_Complete{ @@ -132,8 +132,8 @@ func TestProvisionerJobParametersByID(t *testing.T) { }}, Provision: echo.ProvisionComplete, }) - job = coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) - params, err := client.ProvisionerJobParameterValues(context.Background(), user.Organization, job.ID) + job = coderdtest.AwaitProjectImportJob(t, client, user.Organization, job.ID) + params, err := client.ProjectImportJobParameters(context.Background(), user.Organization, job.ID) require.NoError(t, err) require.Len(t, params, 1) }) @@ -143,7 +143,7 @@ func TestProvisionerJobParametersByID(t *testing.T) { client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) _ = coderdtest.NewProvisionerDaemon(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, &echo.Responses{ + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, &echo.Responses{ Parse: []*proto.Parse_Response{{ Type: &proto.Parse_Response_Complete{ Complete: &proto.Parse_Complete{ @@ -163,8 +163,8 @@ func TestProvisionerJobParametersByID(t *testing.T) { }}, Provision: echo.ProvisionComplete, }) - coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) - params, err := client.ProvisionerJobParameterValues(context.Background(), user.Organization, job.ID) + coderdtest.AwaitProjectImportJob(t, client, user.Organization, job.ID) + params, err := client.ProjectImportJobParameters(context.Background(), user.Organization, job.ID) require.NoError(t, err) require.Len(t, params, 1) require.NotNil(t, params[0]) @@ -179,7 +179,7 @@ func TestProvisionerJobResourcesByID(t *testing.T) { client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) _ = coderdtest.NewProvisionerDaemon(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, &echo.Responses{ + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, &echo.Responses{ Parse: []*proto.Parse_Response{{ Type: &proto.Parse_Response_Complete{ Complete: &proto.Parse_Complete{ @@ -200,10 +200,128 @@ func TestProvisionerJobResourcesByID(t *testing.T) { }, }}, }) - coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) - resources, err := client.ProvisionerJobResources(context.Background(), user.Organization, job.ID) + coderdtest.AwaitProjectImportJob(t, client, user.Organization, job.ID) + resources, err := client.ProjectImportJobResources(context.Background(), user.Organization, job.ID) require.NoError(t, err) // One for start, and one for stop! require.Len(t, resources, 2) }) } + +func TestProvisionerJobLogsByName(t *testing.T) { + t.Parallel() + t.Run("List", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + user := coderdtest.CreateInitialUser(t, client) + coderdtest.NewProvisionerDaemon(t, client) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, &echo.Responses{ + Parse: echo.ParseComplete, + Provision: []*proto.Provision_Response{{ + Type: &proto.Provision_Response_Log{ + Log: &proto.Log{ + Level: proto.LogLevel_INFO, + Output: "log-output", + }, + }, + }, { + Type: &proto.Provision_Response_Complete{ + Complete: &proto.Provision_Complete{}, + }, + }}, + }) + project := coderdtest.CreateProject(t, client, user.Organization, job.ID) + coderdtest.AwaitProjectImportJob(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: project.ActiveVersionID, + Transition: database.WorkspaceTransitionStart, + }) + require.NoError(t, err) + coderdtest.AwaitProjectImportJob(t, client, user.Organization, history.ProvisionJobID) + // Return the log after completion! + logs, err := client.WorkspaceProvisionJobLogsBefore(context.Background(), user.Organization, history.ProvisionJobID, time.Time{}) + require.NoError(t, err) + require.NotNil(t, logs) + require.Len(t, logs, 1) + }) + + t.Run("StreamAfterComplete", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + user := coderdtest.CreateInitialUser(t, client) + coderdtest.NewProvisionerDaemon(t, client) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, &echo.Responses{ + Parse: echo.ParseComplete, + Provision: []*proto.Provision_Response{{ + Type: &proto.Provision_Response_Log{ + Log: &proto.Log{ + Level: proto.LogLevel_INFO, + Output: "log-output", + }, + }, + }, { + Type: &proto.Provision_Response_Complete{ + Complete: &proto.Provision_Complete{}, + }, + }}, + }) + project := coderdtest.CreateProject(t, client, user.Organization, job.ID) + coderdtest.AwaitProjectImportJob(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: project.ActiveVersionID, + Transition: database.WorkspaceTransitionStart, + }) + require.NoError(t, err) + coderdtest.AwaitProjectImportJob(t, client, user.Organization, history.ProvisionJobID) + + logs, err := client.WorkspaceProvisionJobLogsAfter(context.Background(), user.Organization, history.ProvisionJobID, before) + require.NoError(t, err) + log, ok := <-logs + require.True(t, ok) + require.Equal(t, "log-output", log.Output) + // Make sure the channel automatically closes! + _, ok = <-logs + require.False(t, ok) + }) + + t.Run("StreamWhileRunning", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + user := coderdtest.CreateInitialUser(t, client) + coderdtest.NewProvisionerDaemon(t, client) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, &echo.Responses{ + Parse: echo.ParseComplete, + Provision: []*proto.Provision_Response{{ + Type: &proto.Provision_Response_Log{ + Log: &proto.Log{ + Level: proto.LogLevel_INFO, + Output: "log-output", + }, + }, + }, { + Type: &proto.Provision_Response_Complete{ + Complete: &proto.Provision_Complete{}, + }, + }}, + }) + project := coderdtest.CreateProject(t, client, user.Organization, job.ID) + coderdtest.AwaitProjectImportJob(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: project.ActiveVersionID, + Transition: database.WorkspaceTransitionStart, + }) + require.NoError(t, err) + logs, err := client.WorkspaceProvisionJobLogsAfter(context.Background(), user.Organization, history.ProvisionJobID, before) + require.NoError(t, err) + log := <-logs + require.Equal(t, "log-output", log.Output) + // Make sure the channel automatically closes! + _, ok := <-logs + require.False(t, ok) + }) +} diff --git a/coderd/workspacehistory_test.go b/coderd/workspacehistory_test.go index 7b6ba0e8cab74..987955b7a336f 100644 --- a/coderd/workspacehistory_test.go +++ b/coderd/workspacehistory_test.go @@ -22,7 +22,7 @@ func TestPostWorkspaceHistoryByUser(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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{ @@ -40,11 +40,11 @@ func TestPostWorkspaceHistoryByUser(t *testing.T) { client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) coderdtest.NewProvisionerDaemon(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, &echo.Responses{ + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, &echo.Responses{ Provision: []*proto.Provision_Response{{}}, }) project := coderdtest.CreateProject(t, client, user.Organization, job.ID) - coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) + coderdtest.AwaitProjectImportJob(t, client, user.Organization, job.ID) workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID) _, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{ ProjectVersionID: project.ActiveVersionID, @@ -61,9 +61,9 @@ func TestPostWorkspaceHistoryByUser(t *testing.T) { client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) closeDaemon := coderdtest.NewProvisionerDaemon(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, nil) project := coderdtest.CreateProject(t, client, user.Organization, job.ID) - coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) + coderdtest.AwaitProjectImportJob(t, client, user.Organization, job.ID) // Close here so workspace history doesn't process! closeDaemon.Close() workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID) @@ -87,16 +87,16 @@ func TestPostWorkspaceHistoryByUser(t *testing.T) { client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) coderdtest.NewProvisionerDaemon(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, nil) project := coderdtest.CreateProject(t, client, user.Organization, job.ID) - coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) + coderdtest.AwaitProjectImportJob(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: project.ActiveVersionID, Transition: database.WorkspaceTransitionStart, }) require.NoError(t, err) - coderdtest.AwaitProvisionerJob(t, client, user.Organization, firstHistory.ProvisionJobID) + coderdtest.AwaitWorkspaceProvisionJob(t, client, user.Organization, firstHistory.ProvisionJobID) secondHistory, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{ ProjectVersionID: project.ActiveVersionID, Transition: database.WorkspaceTransitionStart, @@ -117,7 +117,7 @@ func TestWorkspaceHistoryByUser(t *testing.T) { client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) coderdtest.NewProvisionerDaemon(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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) @@ -131,9 +131,9 @@ func TestWorkspaceHistoryByUser(t *testing.T) { client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) coderdtest.NewProvisionerDaemon(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, nil) project := coderdtest.CreateProject(t, client, user.Organization, job.ID) - coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) + coderdtest.AwaitProjectImportJob(t, client, user.Organization, job.ID) workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID) _, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{ ProjectVersionID: project.ActiveVersionID, @@ -152,8 +152,8 @@ func TestWorkspaceHistoryByName(t *testing.T) { client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) coderdtest.NewProvisionerDaemon(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) - coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, nil) + coderdtest.AwaitProjectImportJob(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{ diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index 8d820b5964c31..cdc6bc353cde7 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -29,7 +29,7 @@ func TestWorkspaces(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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(), "") @@ -58,7 +58,7 @@ func TestPostWorkspaceByUser(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, nil) project := coderdtest.CreateProject(t, client, user.Organization, job.ID) anotherUser := coderd.CreateUserRequest{ @@ -90,7 +90,7 @@ func TestPostWorkspaceByUser(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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{ @@ -107,7 +107,7 @@ func TestPostWorkspaceByUser(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, nil) project := coderdtest.CreateProject(t, client, user.Organization, job.ID) _ = coderdtest.CreateWorkspace(t, client, "", project.ID) }) @@ -117,7 +117,7 @@ func TestWorkspaceByUser(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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) @@ -130,7 +130,7 @@ func TestWorkspacesByProject(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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) @@ -141,7 +141,7 @@ func TestWorkspacesByProject(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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) diff --git a/codersdk/projectimport.go b/codersdk/projectimport.go new file mode 100644 index 0000000000000..95f7cb6812872 --- /dev/null +++ b/codersdk/projectimport.go @@ -0,0 +1,95 @@ +package codersdk + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/google/uuid" + + "github.com/coder/coder/coderd" +) + +// CreateProjectImportJob creates a new import job in the organization provided. +// ProjectImportJob is not associated with a project by default. Projects +// are created from import. +func (c *Client) CreateProjectImportJob(ctx context.Context, organization string, req coderd.CreateProjectImportJobRequest) (coderd.ProvisionerJob, error) { + res, err := c.request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/projectimport/%s", 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) +} + +// ProjectImportJob returns an import job by ID. +func (c *Client) ProjectImportJob(ctx context.Context, organization string, job uuid.UUID) (coderd.ProvisionerJob, error) { + res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/projectimport/%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) +} + +// ProjectImportJobLogsBefore returns logs that occurred before a specific time. +func (c *Client) ProjectImportJobLogsBefore(ctx context.Context, organization string, job uuid.UUID, before time.Time) ([]coderd.ProvisionerJobLog, error) { + return c.provisionerJobLogsBefore(ctx, "projectimport", organization, job, before) +} + +// ProjectImportJobLogsAfter streams logs for a project import operation that occurred after a specific time. +func (c *Client) ProjectImportJobLogsAfter(ctx context.Context, organization string, job uuid.UUID, after time.Time) (<-chan coderd.ProvisionerJobLog, error) { + return c.provisionerJobLogsAfter(ctx, "projectimport", organization, job, after) +} + +// ProjectImportJobSchemas returns schemas for an import job by ID. +func (c *Client) ProjectImportJobSchemas(ctx context.Context, organization string, job uuid.UUID) ([]coderd.ParameterSchema, error) { + res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/projectimport/%s/%s/schemas", organization, job), nil) + if err != nil { + return nil, err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return nil, readBodyAsError(res) + } + var params []coderd.ParameterSchema + return params, json.NewDecoder(res.Body).Decode(¶ms) +} + +// ProjectImportJobParameters returns computed parameters for a project import job. +func (c *Client) ProjectImportJobParameters(ctx context.Context, organization string, job uuid.UUID) ([]coderd.ComputedParameterValue, error) { + res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/projectimport/%s/%s/parameters", organization, job), nil) + if err != nil { + return nil, err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return nil, readBodyAsError(res) + } + var params []coderd.ComputedParameterValue + return params, json.NewDecoder(res.Body).Decode(¶ms) +} + +// ProjectImportJobResources returns resources for a project import job. +func (c *Client) ProjectImportJobResources(ctx context.Context, organization string, job uuid.UUID) ([]coderd.ProjectImportJobResource, error) { + res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/projectimport/%s/%s/resources", organization, job), nil) + if err != nil { + return nil, err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return nil, readBodyAsError(res) + } + var resources []coderd.ProjectImportJobResource + return resources, json.NewDecoder(res.Body).Decode(&resources) +} diff --git a/codersdk/projectimport_test.go b/codersdk/projectimport_test.go new file mode 100644 index 0000000000000..8cc6b28a23f6c --- /dev/null +++ b/codersdk/projectimport_test.go @@ -0,0 +1,146 @@ +package codersdk_test + +import ( + "context" + "testing" + "time" + + "github.com/coder/coder/coderd" + "github.com/coder/coder/coderd/coderdtest" + "github.com/coder/coder/provisioner/echo" + "github.com/coder/coder/provisionersdk/proto" + "github.com/google/uuid" + "github.com/stretchr/testify/require" +) + +func TestCreateProjectImportJob(t *testing.T) { + t.Parallel() + t.Run("Error", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + _, err := client.CreateProjectImportJob(context.Background(), "", coderd.CreateProjectImportJobRequest{}) + require.Error(t, err) + }) + + t.Run("Create", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + user := coderdtest.CreateInitialUser(t, client) + _ = coderdtest.CreateProjectImportJob(t, client, user.Organization, nil) + }) +} + +func TestProjectImportJob(t *testing.T) { + t.Parallel() + t.Run("Error", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + _, err := client.ProjectImportJob(context.Background(), "", uuid.New()) + require.Error(t, err) + }) + + t.Run("Get", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + user := coderdtest.CreateInitialUser(t, client) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, nil) + _, err := client.ProjectImportJob(context.Background(), user.Organization, job.ID) + require.NoError(t, err) + }) +} + +func TestProjectImportJobLogsBefore(t *testing.T) { + t.Parallel() + t.Run("Error", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + _, err := client.ProjectImportJobLogsBefore(context.Background(), "", uuid.New(), time.Time{}) + require.Error(t, err) + }) + + t.Run("Get", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + user := coderdtest.CreateInitialUser(t, client) + coderdtest.NewProvisionerDaemon(t, client) + before := time.Now() + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, &echo.Responses{ + Parse: []*proto.Parse_Response{{ + Type: &proto.Parse_Response_Log{ + Log: &proto.Log{ + Output: "hello", + }, + }, + }}, + Provision: echo.ProvisionComplete, + }) + logs, err := client.ProjectImportJobLogsAfter(context.Background(), user.Organization, job.ID, before) + require.NoError(t, err) + <-logs + }) +} + +func TestProjectImportJobLogsAfter(t *testing.T) { + t.Parallel() + t.Run("Error", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + _, err := client.ProjectImportJobLogsAfter(context.Background(), "", uuid.New(), time.Time{}) + require.Error(t, err) + }) + + t.Run("Get", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + user := coderdtest.CreateInitialUser(t, client) + coderdtest.NewProvisionerDaemon(t, client) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, &echo.Responses{ + Parse: []*proto.Parse_Response{{ + Type: &proto.Parse_Response_Log{ + Log: &proto.Log{ + Output: "hello", + }, + }, + }, { + Type: &proto.Parse_Response_Complete{ + Complete: &proto.Parse_Complete{}, + }, + }}, + Provision: echo.ProvisionComplete, + }) + coderdtest.AwaitProjectImportJob(t, client, user.Organization, job.ID) + logs, err := client.ProjectImportJobLogsBefore(context.Background(), user.Organization, job.ID, time.Time{}) + require.NoError(t, err) + require.Len(t, logs, 1) + }) +} + +func TestProjectImportJobSchemas(t *testing.T) { + t.Parallel() + t.Run("Error", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + _, err := client.ProjectImportJobSchemas(context.Background(), "", uuid.New()) + require.Error(t, err) + }) +} + +func TestProjectImportJobParameters(t *testing.T) { + t.Parallel() + t.Run("Error", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + _, err := client.ProjectImportJobParameters(context.Background(), "", uuid.New()) + require.Error(t, err) + }) +} + +func TestProjectImportJobResources(t *testing.T) { + t.Parallel() + t.Run("Error", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + _, err := client.ProjectImportJobResources(context.Background(), "", uuid.New()) + require.Error(t, err) + }) +} diff --git a/codersdk/projects_test.go b/codersdk/projects_test.go index b280887ec84ae..4b5459fbede15 100644 --- a/codersdk/projects_test.go +++ b/codersdk/projects_test.go @@ -43,7 +43,7 @@ func TestProject(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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) @@ -66,7 +66,7 @@ func TestCreateProject(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, nil) _ = coderdtest.CreateProject(t, client, user.Organization, job.ID) }) } @@ -84,7 +84,7 @@ func TestProjectVersions(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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) @@ -104,7 +104,7 @@ func TestProjectVersion(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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) @@ -124,7 +124,7 @@ func TestCreateProjectVersion(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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, @@ -146,7 +146,7 @@ func TestProjectParameters(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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) @@ -166,7 +166,7 @@ func TestCreateProjectParameter(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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", diff --git a/codersdk/provisioners.go b/codersdk/provisioners.go index c77df205686b1..afef953beabb9 100644 --- a/codersdk/provisioners.go +++ b/codersdk/provisioners.go @@ -20,6 +20,7 @@ import ( "github.com/coder/coder/provisionersdk" ) +// ProvisionerDaemons returns registered provisionerd instances. func (c *Client) ProvisionerDaemons(ctx context.Context) ([]coderd.ProvisionerDaemon, error) { res, err := c.request(ctx, http.MethodGet, "/api/v2/provisioners/daemons", nil) if err != nil { @@ -59,51 +60,15 @@ 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, 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, organization string, jobID uuid.UUID, after, before time.Time) ([]coderd.ProvisionerJobLog, error) { +// provisionerJobLogsBefore provides log output that occurred before a time. +// This is abstracted from a specific job type to provide consistency between +// APIs. Logs is the only shared route between jobs. +func (c *Client) provisionerJobLogsBefore(ctx context.Context, jobType, organization string, job uuid.UUID, before time.Time) ([]coderd.ProvisionerJobLog, error) { values := url.Values{} - if !after.IsZero() { - values["after"] = []string{strconv.FormatInt(after.UTC().UnixMilli(), 10)} - } 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/%s/logs?%s", organization, jobID, values.Encode()), nil) + res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/%s/%s/%s/logs?%s", jobType, organization, job, values.Encode()), nil) if err != nil { return nil, err } @@ -116,14 +81,13 @@ func (c *Client) ProvisionerJobLogsBetween(ctx context.Context, organization str return logs, json.NewDecoder(res.Body).Decode(&logs) } -// 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, organization string, jobID uuid.UUID, after time.Time) (<-chan coderd.ProvisionerJobLog, error) { +// provisionerJobLogsAfter streams logs that occurred after a specific time. +func (c *Client) provisionerJobLogsAfter(ctx context.Context, jobType, organization string, job 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/%s/logs?follow%s", organization, jobID, afterQuery), nil) + res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/%s/%s/%s/logs?follow%s", jobType, organization, job, afterQuery), nil) if err != nil { return nil, err } @@ -151,44 +115,3 @@ func (c *Client) FollowProvisionerJobLogsAfter(ctx context.Context, organization }() return logs, nil } - -// ProvisionerJobParameters returns computed project parameters for a job by ID. -func (c *Client) ProvisionerJobParameterSchemas(ctx context.Context, organization string, job uuid.UUID) ([]coderd.ParameterSchema, error) { - res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/provisioners/jobs/%s/%s/schemas", organization, job), nil) - if err != nil { - return nil, err - } - defer res.Body.Close() - if res.StatusCode != http.StatusOK { - return nil, readBodyAsError(res) - } - var params []coderd.ParameterSchema - return params, json.NewDecoder(res.Body).Decode(¶ms) -} - -// ProvisionerJobParameterValues returns computed parameters for a provisioner job. -func (c *Client) ProvisionerJobParameterValues(ctx context.Context, organization string, job uuid.UUID) ([]coderd.ComputedParameterValue, error) { - res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/provisioners/jobs/%s/%s/computed", organization, job), nil) - if err != nil { - return nil, err - } - defer res.Body.Close() - if res.StatusCode != http.StatusOK { - return nil, readBodyAsError(res) - } - var params []coderd.ComputedParameterValue - return params, json.NewDecoder(res.Body).Decode(¶ms) -} - -func (c *Client) ProvisionerJobResources(ctx context.Context, organization string, job uuid.UUID) ([]coderd.ProjectImportJobResource, error) { - res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/provisioners/jobs/%s/%s/resources", organization, job), nil) - if err != nil { - return nil, err - } - defer res.Body.Close() - if res.StatusCode != http.StatusOK { - return nil, readBodyAsError(res) - } - var resources []coderd.ProjectImportJobResource - return resources, json.NewDecoder(res.Body).Decode(&resources) -} diff --git a/codersdk/provisioners_test.go b/codersdk/provisioners_test.go index 6808dd19a3b78..9bb4528ebec1e 100644 --- a/codersdk/provisioners_test.go +++ b/codersdk/provisioners_test.go @@ -3,16 +3,11 @@ package codersdk_test import ( "context" "testing" - "time" - "github.com/google/uuid" "github.com/stretchr/testify/require" "github.com/coder/coder/coderd/coderdtest" - "github.com/coder/coder/database" - "github.com/coder/coder/provisioner/echo" "github.com/coder/coder/provisionerd/proto" - sdkproto "github.com/coder/coder/provisionersdk/proto" ) func TestProvisionerDaemons(t *testing.T) { @@ -49,60 +44,3 @@ func TestProvisionerDaemonClient(t *testing.T) { require.NoError(t, err) }) } -func TestProvisionerJobLogs(t *testing.T) { - t.Parallel() - t.Run("Error", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t) - _, err := client.ProvisionerJobLogs(context.Background(), "nothing", uuid.New()) - require.Error(t, err) - }) - - t.Run("List", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t) - user := coderdtest.CreateInitialUser(t, client) - _ = coderdtest.NewProvisionerDaemon(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) - _, err := client.ProvisionerJobLogs(context.Background(), user.Organization, job.ID) - require.NoError(t, err) - }) -} - -func TestFollowProvisionerJobLogsAfter(t *testing.T) { - t.Parallel() - t.Run("Error", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t) - _, err := client.FollowProvisionerJobLogsAfter(context.Background(), "nothing", uuid.New(), time.Time{}) - require.Error(t, err) - }) - - t.Run("Stream", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t) - user := coderdtest.CreateInitialUser(t, client) - _ = coderdtest.NewProvisionerDaemon(t, client) - 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.Parse_Response_Complete{ - Complete: &sdkproto.Parse_Complete{}, - }, - }}, - Provision: echo.ProvisionComplete, - }) - logs, err := client.FollowProvisionerJobLogsAfter(context.Background(), user.Organization, job.ID, before) - require.NoError(t, err) - _, ok := <-logs - require.True(t, ok) - _, ok = <-logs - require.False(t, ok) - }) -} diff --git a/codersdk/workspaces.go b/codersdk/workspaces.go index b84efeff628d1..28f926c518049 100644 --- a/codersdk/workspaces.go +++ b/codersdk/workspaces.go @@ -5,6 +5,9 @@ import ( "encoding/json" "fmt" "net/http" + "time" + + "github.com/google/uuid" "github.com/coder/coder/coderd" ) @@ -131,3 +134,26 @@ func (c *Client) CreateWorkspaceHistory(ctx context.Context, owner, workspace st var workspaceHistory coderd.WorkspaceHistory return workspaceHistory, json.NewDecoder(res.Body).Decode(&workspaceHistory) } + +func (c *Client) WorkspaceProvisionJob(ctx context.Context, organization string, job uuid.UUID) (coderd.ProvisionerJob, error) { + res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/workspaceprovision/%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) +} + +// WorkspaceProvisionJobLogsBefore returns logs that occurred before a specific time. +func (c *Client) WorkspaceProvisionJobLogsBefore(ctx context.Context, organization string, job uuid.UUID, before time.Time) ([]coderd.ProvisionerJobLog, error) { + return c.provisionerJobLogsBefore(ctx, "workspaceprovision", organization, job, before) +} + +// WorkspaceProvisionJobLogsAfter streams logs for a workspace provision operation that occurred after a specific time. +func (c *Client) WorkspaceProvisionJobLogsAfter(ctx context.Context, organization string, job uuid.UUID, after time.Time) (<-chan coderd.ProvisionerJobLog, error) { + return c.provisionerJobLogsAfter(ctx, "workspaceprovision", organization, job, after) +} diff --git a/codersdk/workspaces_test.go b/codersdk/workspaces_test.go index 39ad8eddf4663..503895243dbcb 100644 --- a/codersdk/workspaces_test.go +++ b/codersdk/workspaces_test.go @@ -42,7 +42,7 @@ func TestWorkspacesByProject(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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) @@ -62,7 +62,7 @@ func TestWorkspace(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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) @@ -83,7 +83,7 @@ func TestListWorkspaceHistory(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(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) @@ -105,9 +105,9 @@ func TestWorkspaceHistory(t *testing.T) { client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) _ = coderdtest.NewProvisionerDaemon(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, nil) project := coderdtest.CreateProject(t, client, user.Organization, job.ID) - coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) + coderdtest.AwaitProjectImportJob(t, client, user.Organization, job.ID) workspace := coderdtest.CreateWorkspace(t, client, "", project.ID) _, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{ ProjectVersionID: project.ActiveVersionID, @@ -130,7 +130,7 @@ func TestCreateWorkspace(t *testing.T) { t.Parallel() client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, nil) project := coderdtest.CreateProject(t, client, user.Organization, job.ID) _ = coderdtest.CreateWorkspace(t, client, "", project.ID) }) @@ -150,9 +150,9 @@ func TestCreateWorkspaceHistory(t *testing.T) { client := coderdtest.New(t) user := coderdtest.CreateInitialUser(t, client) _ = coderdtest.NewProvisionerDaemon(t, client) - job := coderdtest.CreateProjectImportProvisionerJob(t, client, user.Organization, nil) + job := coderdtest.CreateProjectImportJob(t, client, user.Organization, nil) project := coderdtest.CreateProject(t, client, user.Organization, job.ID) - coderdtest.AwaitProvisionerJob(t, client, user.Organization, job.ID) + coderdtest.AwaitProjectImportJob(t, client, user.Organization, job.ID) workspace := coderdtest.CreateWorkspace(t, client, "", project.ID) _, err := client.CreateWorkspaceHistory(context.Background(), "", workspace.Name, coderd.CreateWorkspaceHistoryRequest{ ProjectVersionID: project.ActiveVersionID, From e4770bb8f2ace23849dd8904967e7cfc646ec157 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Sat, 12 Feb 2022 00:00:25 +0000 Subject: [PATCH 18/20] Remove go-expect --- cli/clitest/clitest.go | 28 +++++++--------------------- cli/clitest/clitest_test.go | 2 ++ cli/login_test.go | 2 ++ cli/projectcreate.go | 2 +- cli/projectcreate_test.go | 2 ++ cli/workspacecreate.go | 6 ++---- go.mod | 7 +------ go.sum | 24 ------------------------ 8 files changed, 17 insertions(+), 56 deletions(-) diff --git a/cli/clitest/clitest.go b/cli/clitest/clitest.go index ad21230c3b8fb..e4a7de024a5d6 100644 --- a/cli/clitest/clitest.go +++ b/cli/clitest/clitest.go @@ -14,7 +14,6 @@ import ( "github.com/Netflix/go-expect" "github.com/spf13/cobra" "github.com/stretchr/testify/require" - "golang.org/x/xerrors" "github.com/coder/coder/cli" "github.com/coder/coder/cli/config" @@ -51,8 +50,7 @@ func CreateProjectVersionSource(t *testing.T, responses *echo.Responses) string directory := t.TempDir() data, err := echo.Tar(responses) require.NoError(t, err) - err = extractTar(data, directory) - require.NoError(t, err) + extractTar(t, data, directory) return directory } @@ -81,16 +79,14 @@ func NewConsole(t *testing.T, cmd *cobra.Command) *expect.Console { return console } -func extractTar(data []byte, directory string) error { +func extractTar(t *testing.T, data []byte, directory string) { reader := tar.NewReader(bytes.NewBuffer(data)) for { header, err := reader.Next() if errors.Is(err, io.EOF) { break } - if err != nil { - return xerrors.Errorf("read project source archive: %w", err) - } + require.NoError(t, err) // #nosec path := filepath.Join(directory, header.Name) mode := header.FileInfo().Mode() @@ -100,28 +96,18 @@ func extractTar(data []byte, directory string) error { switch header.Typeflag { case tar.TypeDir: err = os.MkdirAll(path, mode) - if err != nil { - return xerrors.Errorf("mkdir: %w", err) - } + require.NoError(t, err) case tar.TypeReg: file, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, mode) - if err != nil { - return xerrors.Errorf("create file %q: %w", path, err) - } + require.NoError(t, err) // Max file size of 10MB. _, err = io.CopyN(file, reader, (1<<20)*10) if errors.Is(err, io.EOF) { err = nil } - if err != nil { - _ = file.Close() - return err - } + require.NoError(t, err) err = file.Close() - if err != nil { - return err - } + require.NoError(t, err) } } - return nil } diff --git a/cli/clitest/clitest_test.go b/cli/clitest/clitest_test.go index 71f9aeefa1bce..806e04ecc2a4e 100644 --- a/cli/clitest/clitest_test.go +++ b/cli/clitest/clitest_test.go @@ -1,3 +1,5 @@ +//go:build !windows + package clitest_test import ( diff --git a/cli/login_test.go b/cli/login_test.go index faefdf4ccccb3..06f942ee95b9c 100644 --- a/cli/login_test.go +++ b/cli/login_test.go @@ -1,3 +1,5 @@ +//go:build !windows + package cli_test import ( diff --git a/cli/projectcreate.go b/cli/projectcreate.go index 21e667b462996..4c67201d5a894 100644 --- a/cli/projectcreate.go +++ b/cli/projectcreate.go @@ -111,7 +111,7 @@ func projectCreate() *cobra.Command { currentDirectory, _ := os.Getwd() cmd.Flags().StringVarP(&directory, "directory", "d", currentDirectory, "Specify the directory to create from") cmd.Flags().StringVarP(&provisioner, "provisioner", "p", "terraform", "Customize the provisioner backend") - // This is for testing! There's only 1 provisioner type right now. + // This is for testing! err := cmd.Flags().MarkHidden("provisioner") if err != nil { panic(err) diff --git a/cli/projectcreate_test.go b/cli/projectcreate_test.go index 547447d3b87bd..ed802475ffe94 100644 --- a/cli/projectcreate_test.go +++ b/cli/projectcreate_test.go @@ -1,3 +1,5 @@ +//go:build !windows + package cli_test import ( diff --git a/cli/workspacecreate.go b/cli/workspacecreate.go index 3b4392096f0ae..d95fab74e2008 100644 --- a/cli/workspacecreate.go +++ b/cli/workspacecreate.go @@ -112,17 +112,15 @@ func workspaceCreate() *cobra.Command { if err != nil { return err } - // logBuffer := make([]coderd.ProvisionerJobLog, 0, 64) for { log, ok := <-logs if !ok { break } - _, _ = fmt.Printf("Logging: %s\n", log.Output) - // logBuffer = append(logBuffer, log) + _, _ = fmt.Printf("Terraform: %s\n", log.Output) } - _, _ = fmt.Printf("Create workspace! %s\n", name) + _, _ = fmt.Printf("Created workspace! %s\n", name) return nil }, diff --git a/go.mod b/go.mod index e7168de21e631..7a692c6352a8e 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ replace github.com/chzyer/readline => github.com/kylecarbs/readline v0.0.0-20220 require ( cdr.dev/slog v1.4.1 - github.com/ActiveState/termtest/expect v0.7.0 + github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 github.com/briandowns/spinner v1.18.1 github.com/coder/retry v1.3.0 github.com/fatih/color v1.13.0 @@ -58,12 +58,8 @@ require ( require ( cloud.google.com/go/compute v0.1.0 // indirect - github.com/ActiveState/termtest/conpty v0.5.0 // indirect - github.com/ActiveState/termtest/xpty v0.6.0 // indirect - github.com/ActiveState/vt10x v1.3.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Microsoft/go-winio v0.5.1 // indirect - github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/agext/levenshtein v1.2.3 // indirect github.com/alecthomas/chroma v0.10.0 // indirect @@ -95,7 +91,6 @@ require ( github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a // indirect github.com/klauspost/compress v1.13.6 // indirect - github.com/kr/pty v1.1.8 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a // indirect github.com/mattn/go-colorable v0.1.12 // indirect diff --git a/go.sum b/go.sum index c8bd72397ed5a..231783ed456a6 100644 --- a/go.sum +++ b/go.sum @@ -57,14 +57,6 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= -github.com/ActiveState/termtest/conpty v0.5.0 h1:JLUe6YDs4Jw4xNPCU+8VwTpniYOGeKzQg4SM2YHQNA8= -github.com/ActiveState/termtest/conpty v0.5.0/go.mod h1:LO4208FLsxw6DcNZ1UtuGUMW+ga9PFtX4ntv8Ymg9og= -github.com/ActiveState/termtest/expect v0.7.0 h1:VNrcfXTHXXoe7i+3WgF5QJhstQLGP4saj+XYM9ZzBvY= -github.com/ActiveState/termtest/expect v0.7.0/go.mod h1:64QuJvMtMu7+H5U+5TSMBxAs1FAaLvRIyN7WPOICido= -github.com/ActiveState/termtest/xpty v0.6.0 h1:L9c17TDfy+ed+tY5cMOErn0n2EYG4tj8StdxHmoPok8= -github.com/ActiveState/termtest/xpty v0.6.0/go.mod h1:MmTm/62Ajq+D92emHq8LOu9Q+2+pkBurDLahkUP6Odg= -github.com/ActiveState/vt10x v1.3.1 h1:7qi8BGXUEBghzBxfXSY0J77etO+L95PZQlwD7ay2mn0= -github.com/ActiveState/vt10x v1.3.1/go.mod h1:8wJKd36c9NmCfGyPyOJmkvyIMvbUPfHkfdS8zZlK19s= github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck= @@ -111,8 +103,6 @@ github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01 github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= -github.com/Netflix/go-expect v0.0.0-20200312175327-da48e75238e2/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= @@ -160,8 +150,6 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/autarch/testify v1.2.2 h1:9Q9V6zqhP7R6dv+zRUddv6kXKLo6ecQhnFRFWM71i1c= -github.com/autarch/testify v1.2.2/go.mod h1:oDbHKfFv2/D5UtVrxkk90OKcb6P4/AqF1Pcf6ZbvDQo= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v1.8.0/go.mod h1:xEFuWz+3TYdlPRuo+CqATbeDWIWyaT5uAPwPaWtgse0= @@ -448,8 +436,6 @@ github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXt github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= -github.com/gdamore/encoding v0.0.0-20151215212835-b23993cbb635/go.mod h1:yrQYJKKDTrHmbYxI7CYi+/hbdiDT2m4Hj+t0ikCjsrQ= -github.com/gdamore/tcell v1.0.1-0.20180608172421-b3cebc399d6f/go.mod h1:tqyG50u7+Ctv1w5VX67kLzKcj9YXR/JSBZQq/+mLl1A= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= @@ -727,7 +713,6 @@ github.com/hashicorp/terraform-json v0.13.0 h1:Li9L+lKD1FO5RVFRM1mMMIBDoUHslOniy github.com/hashicorp/terraform-json v0.13.0/go.mod h1:y5OdLBCT+rxbwnpxZs9kGL7R9ExU76+cpdY8zHwoazk= github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I= github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= -github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -849,7 +834,6 @@ github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= -github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -878,7 +862,6 @@ github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lucasb-eyer/go-colorful v0.0.0-20180526135729-345fbb3dbcdb/go.mod h1:NXg0ArsFk0Y01623LgUqoqcouGDB+PwCCQlrwrG6xJ4= github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw= github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= @@ -1170,7 +1153,6 @@ github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/snowflakedb/gosnowflake v1.6.3/go.mod h1:6hLajn6yxuJ4xUHZegMekpq9rnQbGJ7TMwXjgTmA6lg= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -1202,7 +1184,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -1337,7 +1318,6 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200427165652-729f1e841bcc/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -1560,7 +1540,6 @@ golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200428200454-593003d681fa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1570,7 +1549,6 @@ golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200821140526-fda516888d29/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1578,7 +1556,6 @@ golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1900,7 +1877,6 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0/go.mod h1:OdE7CF6DbADk7lN8LIKRzRJTTZXIjtWgA5THM5lhBAw= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From c6cee947fd78f47ec82c74972ff48a4217cff2a4 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Sat, 12 Feb 2022 19:06:42 +0000 Subject: [PATCH 19/20] Fix requested changes --- cli/clitest/clitest.go | 1 + cli/projects.go | 1 - cli/workspacecreate.go | 3 ++- coderd/files.go | 14 ++++++++++++-- coderd/files_test.go | 11 +++++++++++ coderd/parameter/compute.go | 2 +- coderd/provisionerjobs.go | 1 - codersdk/files.go | 2 +- 8 files changed, 28 insertions(+), 7 deletions(-) diff --git a/cli/clitest/clitest.go b/cli/clitest/clitest.go index e4a7de024a5d6..e9fbbd4f23d1d 100644 --- a/cli/clitest/clitest.go +++ b/cli/clitest/clitest.go @@ -23,6 +23,7 @@ import ( var ( // Used to ensure terminal output doesn't have anything crazy! + // See: https://stackoverflow.com/a/29497680 stripAnsi = regexp.MustCompile("[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))") ) diff --git a/cli/projects.go b/cli/projects.go index b11317367aa8b..bce9930bd21ca 100644 --- a/cli/projects.go +++ b/cli/projects.go @@ -17,7 +17,6 @@ func projects() *cobra.Command { cmd := &cobra.Command{ Use: "projects", Aliases: []string{"project"}, - Long: "Testing something", Example: ` - Create a project for developers to create workspaces diff --git a/cli/workspacecreate.go b/cli/workspacecreate.go index d95fab74e2008..be883ebaffe00 100644 --- a/cli/workspacecreate.go +++ b/cli/workspacecreate.go @@ -120,8 +120,9 @@ func workspaceCreate() *cobra.Command { _, _ = fmt.Printf("Terraform: %s\n", log.Output) } - _, _ = fmt.Printf("Created workspace! %s\n", name) + // This command is WIP, and output will change! + _, _ = fmt.Printf("Created workspace! %s\n", name) return nil }, } diff --git a/coderd/files.go b/coderd/files.go index 01337b9d2e143..069509c567ca6 100644 --- a/coderd/files.go +++ b/coderd/files.go @@ -40,8 +40,18 @@ func (api *api) postFiles(rw http.ResponseWriter, r *http.Request) { return } hashBytes := sha256.Sum256(data) - file, err := api.Database.InsertFile(r.Context(), database.InsertFileParams{ - Hash: hex.EncodeToString(hashBytes[:]), + hash := hex.EncodeToString(hashBytes[:]) + file, err := api.Database.GetFileByHash(r.Context(), hash) + if err == nil { + // The file already exists! + render.Status(r, http.StatusOK) + render.JSON(rw, r, UploadFileResponse{ + Hash: file.Hash, + }) + return + } + file, err = api.Database.InsertFile(r.Context(), database.InsertFileParams{ + Hash: hash, CreatedBy: apiKey.UserID, CreatedAt: database.Now(), Mimetype: contentType, diff --git a/coderd/files_test.go b/coderd/files_test.go index 2ffa455df7e81..d14c1aff99fed 100644 --- a/coderd/files_test.go +++ b/coderd/files_test.go @@ -27,4 +27,15 @@ func TestPostFiles(t *testing.T) { _, err := client.UploadFile(context.Background(), codersdk.ContentTypeTar, make([]byte, 1024)) require.NoError(t, err) }) + + t.Run("InsertAlreadyExists", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t) + _ = coderdtest.CreateInitialUser(t, client) + data := make([]byte, 1024) + _, err := client.UploadFile(context.Background(), codersdk.ContentTypeTar, data) + require.NoError(t, err) + _, err = client.UploadFile(context.Background(), codersdk.ContentTypeTar, data) + require.NoError(t, err) + }) } diff --git a/coderd/parameter/compute.go b/coderd/parameter/compute.go index 801c93732ee24..d7c68a5ac30e1 100644 --- a/coderd/parameter/compute.go +++ b/coderd/parameter/compute.go @@ -88,7 +88,7 @@ func Compute(ctx context.Context, db database.Store, scope ComputeScope, options continue } if _, ok := compute.computedParameterByName[parameterSchema.Name]; ok { - // We already have a value! No need to use th default. + // We already have a value! No need to use the default. continue } diff --git a/coderd/provisionerjobs.go b/coderd/provisionerjobs.go index a817b39f24873..a99ca905c83c5 100644 --- a/coderd/provisionerjobs.go +++ b/coderd/provisionerjobs.go @@ -170,7 +170,6 @@ func (api *api) provisionerJobLogsByID(rw http.ResponseWriter, r *http.Request) }) if errors.Is(err, sql.ErrNoRows) { err = nil - provisionerJobLogs = []database.ProvisionerJobLog{} } if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ diff --git a/codersdk/files.go b/codersdk/files.go index 25c7fcb70cc2b..f4fe82f8cd146 100644 --- a/codersdk/files.go +++ b/codersdk/files.go @@ -20,7 +20,7 @@ func (c *Client) UploadFile(ctx context.Context, contentType string, content []b return coderd.UploadFileResponse{}, err } defer res.Body.Close() - if res.StatusCode != http.StatusCreated { + if res.StatusCode != http.StatusCreated && res.StatusCode != http.StatusOK { return coderd.UploadFileResponse{}, readBodyAsError(res) } var resp coderd.UploadFileResponse From f9814beb3777f40fb2d7f5288cc37c835b881c39 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Sat, 12 Feb 2022 19:22:02 +0000 Subject: [PATCH 20/20] Skip workspacecreate test on windows --- cli/workspacecreate_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cli/workspacecreate_test.go b/cli/workspacecreate_test.go index 1b0f0dcf2d2ff..138e0ee1e61d6 100644 --- a/cli/workspacecreate_test.go +++ b/cli/workspacecreate_test.go @@ -1,3 +1,5 @@ +//go:build !windows + package cli_test import ( 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