From c64636bf873a1b7dc06d0f3640bc3d9c992dd010 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Wed, 22 Feb 2023 21:04:31 +0000 Subject: [PATCH 01/20] Add git auth providers schema --- coderd/database/dbauthz/querier.go | 22 +++++ coderd/database/dbauthz/querier_test.go | 12 +++ coderd/database/dbfake/databasefake.go | 20 ++++ coderd/database/dump.sql | 5 +- .../000100_template_version_gitauth.down.sql | 2 + .../000100_template_version_gitauth.up.sql | 4 + coderd/database/models.go | 2 + coderd/database/querier.go | 1 + coderd/database/queries.sql.go | 45 +++++++-- coderd/database/queries/templateversions.sql | 9 ++ provisionersdk/proto/provisioner.pb.go | 93 +++++++++++-------- provisionersdk/proto/provisioner.proto | 3 +- 12 files changed, 167 insertions(+), 51 deletions(-) create mode 100644 coderd/database/migrations/000100_template_version_gitauth.down.sql create mode 100644 coderd/database/migrations/000100_template_version_gitauth.up.sql diff --git a/coderd/database/dbauthz/querier.go b/coderd/database/dbauthz/querier.go index 91e912cb90271..a71a79614e6d3 100644 --- a/coderd/database/dbauthz/querier.go +++ b/coderd/database/dbauthz/querier.go @@ -885,6 +885,28 @@ func (q *querier) UpdateTemplateVersionDescriptionByJobID(ctx context.Context, a return q.db.UpdateTemplateVersionDescriptionByJobID(ctx, arg) } +func (q *querier) UpdateTemplateVersionGitAuthProvidersByJobID(ctx context.Context, arg database.UpdateTemplateVersionGitAuthProvidersByJobIDParams) error { + // An actor is allowed to update the template version git auth providers if they are authorized to update the template. + tv, err := q.db.GetTemplateVersionByJobID(ctx, arg.JobID) + if err != nil { + return err + } + var obj rbac.Objecter + if !tv.TemplateID.Valid { + obj = rbac.ResourceTemplate.InOrg(tv.OrganizationID) + } else { + tpl, err := q.db.GetTemplateByID(ctx, tv.TemplateID.UUID) + if err != nil { + return err + } + obj = tpl + } + if err := q.authorizeContext(ctx, rbac.ActionUpdate, obj); err != nil { + return err + } + return q.db.UpdateTemplateVersionGitAuthProvidersByJobID(ctx, arg) +} + func (q *querier) GetTemplateGroupRoles(ctx context.Context, id uuid.UUID) ([]database.TemplateGroup, error) { // An actor is authorized to read template group roles if they are authorized to read the template. template, err := q.db.GetTemplateByID(ctx, id) diff --git a/coderd/database/dbauthz/querier_test.go b/coderd/database/dbauthz/querier_test.go index 8fc6178001f1d..72b92c924f62d 100644 --- a/coderd/database/dbauthz/querier_test.go +++ b/coderd/database/dbauthz/querier_test.go @@ -724,6 +724,18 @@ func (s *MethodTestSuite) TestTemplate() { Readme: "foo", }).Asserts(t1, rbac.ActionUpdate).Returns() })) + s.Run("UpdateTemplateVersionGitAuthProvidersByJobID", s.Subtest(func(db database.Store, check *expects) { + jobID := uuid.New() + t1 := dbgen.Template(s.T(), db, database.Template{}) + _ = dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{ + TemplateID: uuid.NullUUID{UUID: t1.ID, Valid: true}, + JobID: jobID, + }) + check.Args(database.UpdateTemplateVersionGitAuthProvidersByJobIDParams{ + JobID: jobID, + GitAuthProviders: []string{}, + }).Asserts(t1, rbac.ActionUpdate).Returns() + })) } func (s *MethodTestSuite) TestUser() { diff --git a/coderd/database/dbfake/databasefake.go b/coderd/database/dbfake/databasefake.go index 1426eb494833f..966b8b737ec8e 100644 --- a/coderd/database/dbfake/databasefake.go +++ b/coderd/database/dbfake/databasefake.go @@ -3260,6 +3260,26 @@ func (q *fakeQuerier) UpdateTemplateVersionDescriptionByJobID(_ context.Context, return sql.ErrNoRows } +func (q *fakeQuerier) UpdateTemplateVersionGitAuthProvidersByJobID(_ context.Context, arg database.UpdateTemplateVersionGitAuthProvidersByJobIDParams) error { + if err := validateDatabaseType(arg); err != nil { + return err + } + + q.mutex.Lock() + defer q.mutex.Unlock() + + for index, templateVersion := range q.templateVersions { + if templateVersion.JobID != arg.JobID { + continue + } + templateVersion.GitAuthProviders = arg.GitAuthProviders + templateVersion.UpdatedAt = arg.UpdatedAt + q.templateVersions[index] = templateVersion + return nil + } + return sql.ErrNoRows +} + func (q *fakeQuerier) UpdateWorkspaceAgentConnectionByID(_ context.Context, arg database.UpdateWorkspaceAgentConnectionByIDParams) error { if err := validateDatabaseType(arg); err != nil { return err diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 2137be816ef84..29316b88633c6 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -417,9 +417,12 @@ CREATE TABLE template_versions ( name character varying(64) NOT NULL, readme character varying(1048576) NOT NULL, job_id uuid NOT NULL, - created_by uuid NOT NULL + created_by uuid NOT NULL, + git_auth_providers text[] ); +COMMENT ON COLUMN template_versions.git_auth_providers IS 'IDs of Git auth providers for a specific template version'; + CREATE TABLE templates ( id uuid NOT NULL, created_at timestamp with time zone NOT NULL, diff --git a/coderd/database/migrations/000100_template_version_gitauth.down.sql b/coderd/database/migrations/000100_template_version_gitauth.down.sql new file mode 100644 index 0000000000000..099f9bdefc54c --- /dev/null +++ b/coderd/database/migrations/000100_template_version_gitauth.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE template_versions + DROP COLUMN git_auth_providers; diff --git a/coderd/database/migrations/000100_template_version_gitauth.up.sql b/coderd/database/migrations/000100_template_version_gitauth.up.sql new file mode 100644 index 0000000000000..883190f979782 --- /dev/null +++ b/coderd/database/migrations/000100_template_version_gitauth.up.sql @@ -0,0 +1,4 @@ +ALTER TABLE template_versions + ADD COLUMN git_auth_providers text[]; + +COMMENT ON COLUMN template_versions.git_auth_providers IS 'IDs of Git auth providers for a specific template version'; diff --git a/coderd/database/models.go b/coderd/database/models.go index 18276551a488e..21e22566fc40b 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -1433,6 +1433,8 @@ type TemplateVersion struct { Readme string `db:"readme" json:"readme"` JobID uuid.UUID `db:"job_id" json:"job_id"` CreatedBy uuid.UUID `db:"created_by" json:"created_by"` + // IDs of Git auth providers for a specific template version + GitAuthProviders []string `db:"git_auth_providers" json:"git_auth_providers"` } type TemplateVersionParameter struct { diff --git a/coderd/database/querier.go b/coderd/database/querier.go index 4f7bf73b4de3e..d9a614078626c 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -189,6 +189,7 @@ type sqlcQuerier interface { UpdateTemplateMetaByID(ctx context.Context, arg UpdateTemplateMetaByIDParams) (Template, error) UpdateTemplateVersionByID(ctx context.Context, arg UpdateTemplateVersionByIDParams) error UpdateTemplateVersionDescriptionByJobID(ctx context.Context, arg UpdateTemplateVersionDescriptionByJobIDParams) error + UpdateTemplateVersionGitAuthProvidersByJobID(ctx context.Context, arg UpdateTemplateVersionGitAuthProvidersByJobIDParams) error UpdateUserDeletedByID(ctx context.Context, arg UpdateUserDeletedByIDParams) error UpdateUserHashedPassword(ctx context.Context, arg UpdateUserHashedPasswordParams) error UpdateUserLastSeenAt(ctx context.Context, arg UpdateUserLastSeenAtParams) (User, error) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 1f1c79bbc624d..8ad1f4378e528 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -3671,7 +3671,7 @@ func (q *sqlQuerier) InsertTemplateVersionParameter(ctx context.Context, arg Ins const getPreviousTemplateVersion = `-- name: GetPreviousTemplateVersion :one SELECT - id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by + id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers FROM template_versions WHERE @@ -3705,13 +3705,14 @@ func (q *sqlQuerier) GetPreviousTemplateVersion(ctx context.Context, arg GetPrev &i.Readme, &i.JobID, &i.CreatedBy, + pq.Array(&i.GitAuthProviders), ) return i, err } const getTemplateVersionByID = `-- name: GetTemplateVersionByID :one SELECT - id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by + id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers FROM template_versions WHERE @@ -3731,13 +3732,14 @@ func (q *sqlQuerier) GetTemplateVersionByID(ctx context.Context, id uuid.UUID) ( &i.Readme, &i.JobID, &i.CreatedBy, + pq.Array(&i.GitAuthProviders), ) return i, err } const getTemplateVersionByJobID = `-- name: GetTemplateVersionByJobID :one SELECT - id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by + id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers FROM template_versions WHERE @@ -3757,13 +3759,14 @@ func (q *sqlQuerier) GetTemplateVersionByJobID(ctx context.Context, jobID uuid.U &i.Readme, &i.JobID, &i.CreatedBy, + pq.Array(&i.GitAuthProviders), ) return i, err } const getTemplateVersionByTemplateIDAndName = `-- name: GetTemplateVersionByTemplateIDAndName :one SELECT - id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by + id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers FROM template_versions WHERE @@ -3789,13 +3792,14 @@ func (q *sqlQuerier) GetTemplateVersionByTemplateIDAndName(ctx context.Context, &i.Readme, &i.JobID, &i.CreatedBy, + pq.Array(&i.GitAuthProviders), ) return i, err } const getTemplateVersionsByIDs = `-- name: GetTemplateVersionsByIDs :many SELECT - id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by + id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers FROM template_versions WHERE @@ -3821,6 +3825,7 @@ func (q *sqlQuerier) GetTemplateVersionsByIDs(ctx context.Context, ids []uuid.UU &i.Readme, &i.JobID, &i.CreatedBy, + pq.Array(&i.GitAuthProviders), ); err != nil { return nil, err } @@ -3837,7 +3842,7 @@ func (q *sqlQuerier) GetTemplateVersionsByIDs(ctx context.Context, ids []uuid.UU const getTemplateVersionsByTemplateID = `-- name: GetTemplateVersionsByTemplateID :many SELECT - id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by + id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers FROM template_versions WHERE @@ -3901,6 +3906,7 @@ func (q *sqlQuerier) GetTemplateVersionsByTemplateID(ctx context.Context, arg Ge &i.Readme, &i.JobID, &i.CreatedBy, + pq.Array(&i.GitAuthProviders), ); err != nil { return nil, err } @@ -3916,7 +3922,7 @@ func (q *sqlQuerier) GetTemplateVersionsByTemplateID(ctx context.Context, arg Ge } const getTemplateVersionsCreatedAfter = `-- name: GetTemplateVersionsCreatedAfter :many -SELECT id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by FROM template_versions WHERE created_at > $1 +SELECT id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers FROM template_versions WHERE created_at > $1 ` func (q *sqlQuerier) GetTemplateVersionsCreatedAfter(ctx context.Context, createdAt time.Time) ([]TemplateVersion, error) { @@ -3938,6 +3944,7 @@ func (q *sqlQuerier) GetTemplateVersionsCreatedAfter(ctx context.Context, create &i.Readme, &i.JobID, &i.CreatedBy, + pq.Array(&i.GitAuthProviders), ); err != nil { return nil, err } @@ -3966,7 +3973,7 @@ INSERT INTO created_by ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by + ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, git_auth_providers ` type InsertTemplateVersionParams struct { @@ -4004,6 +4011,7 @@ func (q *sqlQuerier) InsertTemplateVersion(ctx context.Context, arg InsertTempla &i.Readme, &i.JobID, &i.CreatedBy, + pq.Array(&i.GitAuthProviders), ) return i, err } @@ -4050,6 +4058,27 @@ func (q *sqlQuerier) UpdateTemplateVersionDescriptionByJobID(ctx context.Context return err } +const updateTemplateVersionGitAuthProvidersByJobID = `-- name: UpdateTemplateVersionGitAuthProvidersByJobID :exec +UPDATE + template_versions +SET + git_auth_providers = $2, + updated_at = $3 +WHERE + job_id = $1 +` + +type UpdateTemplateVersionGitAuthProvidersByJobIDParams struct { + JobID uuid.UUID `db:"job_id" json:"job_id"` + GitAuthProviders []string `db:"git_auth_providers" json:"git_auth_providers"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` +} + +func (q *sqlQuerier) UpdateTemplateVersionGitAuthProvidersByJobID(ctx context.Context, arg UpdateTemplateVersionGitAuthProvidersByJobIDParams) error { + _, err := q.db.ExecContext(ctx, updateTemplateVersionGitAuthProvidersByJobID, arg.JobID, pq.Array(arg.GitAuthProviders), arg.UpdatedAt) + return err +} + const getTemplateVersionVariables = `-- name: GetTemplateVersionVariables :many SELECT template_version_id, name, description, type, value, default_value, required, sensitive FROM template_version_variables WHERE template_version_id = $1 ` diff --git a/coderd/database/queries/templateversions.sql b/coderd/database/queries/templateversions.sql index 78dcff44e2d30..e4efc7f46ab90 100644 --- a/coderd/database/queries/templateversions.sql +++ b/coderd/database/queries/templateversions.sql @@ -102,6 +102,15 @@ SET WHERE job_id = $1; +-- name: UpdateTemplateVersionGitAuthProvidersByJobID :exec +UPDATE + template_versions +SET + git_auth_providers = $2, + updated_at = $3 +WHERE + job_id = $1; + -- name: GetPreviousTemplateVersion :one SELECT * diff --git a/provisionersdk/proto/provisioner.pb.go b/provisionersdk/proto/provisioner.pb.go index aef691ec3fca1..205949cfb0a7b 100644 --- a/provisionersdk/proto/provisioner.pb.go +++ b/provisionersdk/proto/provisioner.pb.go @@ -2388,11 +2388,12 @@ type Provision_Complete struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - State []byte `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"` - Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` - Resources []*Resource `protobuf:"bytes,3,rep,name=resources,proto3" json:"resources,omitempty"` - Parameters []*RichParameter `protobuf:"bytes,4,rep,name=parameters,proto3" json:"parameters,omitempty"` - Plan []byte `protobuf:"bytes,5,opt,name=plan,proto3" json:"plan,omitempty"` + State []byte `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"` + Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` + Resources []*Resource `protobuf:"bytes,3,rep,name=resources,proto3" json:"resources,omitempty"` + Parameters []*RichParameter `protobuf:"bytes,4,rep,name=parameters,proto3" json:"parameters,omitempty"` + GitAuthProviders []string `protobuf:"bytes,5,rep,name=git_auth_providers,json=gitAuthProviders,proto3" json:"git_auth_providers,omitempty"` + Plan []byte `protobuf:"bytes,6,opt,name=plan,proto3" json:"plan,omitempty"` } func (x *Provision_Complete) Reset() { @@ -2455,6 +2456,13 @@ func (x *Provision_Complete) GetParameters() []*RichParameter { return nil } +func (x *Provision_Complete) GetGitAuthProviders() []string { + if x != nil { + return x.GitAuthProviders + } + return nil +} + func (x *Provision_Complete) GetPlan() []byte { if x != nil { return x.Plan @@ -2793,7 +2801,7 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{ 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, 0xc7, 0x0a, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x70, 0x65, 0x22, 0xf5, 0x0a, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0xd1, 0x02, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x53, 0x0a, 0x14, 0x77, 0x6f, @@ -2858,7 +2866,7 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x06, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x1a, 0xbb, 0x01, 0x0a, 0x08, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x1a, 0xe9, 0x01, 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, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x33, @@ -2869,40 +2877,43 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{ 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, - 0x12, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x70, - 0x6c, 0x61, 0x6e, 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, 0x2a, 0x3b, 0x0a, - 0x0f, 0x41, 0x70, 0x70, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, - 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x57, 0x4e, 0x45, 0x52, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, - 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, - 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x02, 0x2a, 0x37, 0x0a, 0x13, 0x57, 0x6f, - 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, - 0x53, 0x54, 0x4f, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4f, - 0x59, 0x10, 0x02, 0x32, 0xa3, 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, 0x50, 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, 0x28, 0x01, 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, + 0x2c, 0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x67, 0x69, 0x74, + 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, + 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x70, 0x6c, 0x61, + 0x6e, 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, 0x2a, 0x3b, 0x0a, 0x0f, 0x41, + 0x70, 0x70, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, + 0x0a, 0x05, 0x4f, 0x57, 0x4e, 0x45, 0x52, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x55, 0x54, + 0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, + 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x02, 0x2a, 0x37, 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x54, + 0x4f, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4f, 0x59, 0x10, + 0x02, 0x32, 0xa3, 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, 0x50, 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, 0x28, 0x01, 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 e9f714033688d..227ff0d909279 100644 --- a/provisionersdk/proto/provisioner.proto +++ b/provisionersdk/proto/provisioner.proto @@ -255,7 +255,8 @@ message Provision { string error = 2; repeated Resource resources = 3; repeated RichParameter parameters = 4; - bytes plan = 5; + repeated string git_auth_providers = 5; + bytes plan = 6; } message Response { oneof type { From 6a617127fb1c7737ba7193f9cd740eb5373edb3c Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Wed, 22 Feb 2023 23:11:56 +0000 Subject: [PATCH 02/20] Pipe git auth providers to the schema --- .../provisionerdserver/provisionerdserver.go | 15 +- .../provisionerdserver_test.go | 10 +- provisioner/terraform/executor.go | 41 +- provisioner/terraform/resources.go | 56 +- provisioner/terraform/resources_test.go | 68 +- .../git-auth-providers/git-auth-providers.tf | 27 + .../git-auth-providers.tfplan.dot | 24 + .../git-auth-providers.tfplan.json | 212 +++++ .../git-auth-providers.tfstate.dot | 24 + .../git-auth-providers.tfstate.json | 79 ++ provisionerd/proto/provisionerd.pb.go | 202 ++--- provisionerd/proto/provisionerd.proto | 1 + provisionerd/runner/runner.go | 43 +- provisionersdk/proto/provisioner.pb.go | 774 ++++++++++-------- provisionersdk/proto/provisioner.proto | 6 + 15 files changed, 1065 insertions(+), 517 deletions(-) create mode 100644 provisioner/terraform/testdata/git-auth-providers/git-auth-providers.tf create mode 100644 provisioner/terraform/testdata/git-auth-providers/git-auth-providers.tfplan.dot create mode 100644 provisioner/terraform/testdata/git-auth-providers/git-auth-providers.tfplan.json create mode 100644 provisioner/terraform/testdata/git-auth-providers/git-auth-providers.tfstate.dot create mode 100644 provisioner/terraform/testdata/git-auth-providers/git-auth-providers.tfstate.json diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index d740d325a1f65..3b734c0ef02d7 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -305,7 +305,7 @@ func (server *Server) includeLastVariableValues(ctx context.Context, templateVer templateVersion, err := server.Database.GetTemplateVersionByID(ctx, templateVersionID) if err != nil { - return nil, fmt.Errorf("get template version: %w", err) + return nil, xerrors.Errorf("get template version: %w", err) } if templateVersion.TemplateID.UUID == uuid.Nil { @@ -314,7 +314,7 @@ func (server *Server) includeLastVariableValues(ctx context.Context, templateVer template, err := server.Database.GetTemplateByID(ctx, templateVersion.TemplateID.UUID) if err != nil { - return nil, fmt.Errorf("get template: %w", err) + return nil, xerrors.Errorf("get template: %w", err) } if template.ActiveVersionID == uuid.Nil { @@ -323,7 +323,7 @@ func (server *Server) includeLastVariableValues(ctx context.Context, templateVer templateVariables, err := server.Database.GetTemplateVersionVariables(ctx, template.ActiveVersionID) if err != nil && !xerrors.Is(err, sql.ErrNoRows) { - return nil, fmt.Errorf("get template version variables: %w", err) + return nil, xerrors.Errorf("get template version variables: %w", err) } for _, templateVariable := range templateVariables { @@ -810,6 +810,15 @@ func (server *Server) CompleteJob(ctx context.Context, completed *proto.Complete } } + err = server.Database.UpdateTemplateVersionGitAuthProvidersByJobID(ctx, database.UpdateTemplateVersionGitAuthProvidersByJobIDParams{ + JobID: jobID, + GitAuthProviders: jobType.TemplateImport.GitAuthProviders, + UpdatedAt: database.Now(), + }) + if err != nil { + return nil, xerrors.Errorf("update template version git auth providers: %w", err) + } + err = server.Database.UpdateProvisionerJobWithCompleteByID(ctx, database.UpdateProvisionerJobWithCompleteByIDParams{ ID: jobID, UpdatedAt: database.Now(), diff --git a/coderd/provisionerdserver/provisionerdserver_test.go b/coderd/provisionerdserver/provisionerdserver_test.go index e456d5d4e0c4e..74f3ce6e65171 100644 --- a/coderd/provisionerdserver/provisionerdserver_test.go +++ b/coderd/provisionerdserver/provisionerdserver_test.go @@ -734,10 +734,16 @@ func TestCompleteJob(t *testing.T) { t.Run("TemplateImport", func(t *testing.T) { t.Parallel() srv := setup(t, false) + jobID := uuid.New() + version, err := srv.Database.InsertTemplateVersion(ctx, database.InsertTemplateVersionParams{ + ID: uuid.New(), + JobID: jobID, + }) + require.NoError(t, err) job, err := srv.Database.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{ - ID: uuid.New(), + ID: jobID, Provisioner: database.ProvisionerTypeEcho, - Input: []byte(`{"template_version_id": "` + uuid.NewString() + `"}`), + Input: []byte(`{"template_version_id": "` + version.ID.String() + `"}`), StorageMethod: database.ProvisionerStorageMethodFile, Type: database.ProvisionerJobTypeWorkspaceBuild, }) diff --git a/provisioner/terraform/executor.go b/provisioner/terraform/executor.go index 7d2335beb071f..d3c3318340095 100644 --- a/provisioner/terraform/executor.go +++ b/provisioner/terraform/executor.go @@ -223,7 +223,7 @@ func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr l if err != nil { return nil, xerrors.Errorf("terraform plan: %w", err) } - resources, parameters, err := e.planResources(ctx, killCtx, planfilePath) + state, err := e.planResources(ctx, killCtx, planfilePath) if err != nil { return nil, err } @@ -234,31 +234,32 @@ func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr l return &proto.Provision_Response{ Type: &proto.Provision_Response_Complete{ Complete: &proto.Provision_Complete{ - Parameters: parameters, - Resources: resources, - Plan: planFileByt, + Parameters: state.Parameters, + Resources: state.Resources, + GitAuthProviders: state.GitAuthProviders, + Plan: planFileByt, }, }, }, nil } // planResources must only be called while the lock is held. -func (e *executor) planResources(ctx, killCtx context.Context, planfilePath string) ([]*proto.Resource, []*proto.RichParameter, error) { +func (e *executor) planResources(ctx, killCtx context.Context, planfilePath string) (*ConvertedState, error) { plan, err := e.showPlan(ctx, killCtx, planfilePath) if err != nil { - return nil, nil, xerrors.Errorf("show terraform plan file: %w", err) + return nil, xerrors.Errorf("show terraform plan file: %w", err) } rawGraph, err := e.graph(ctx, killCtx) if err != nil { - return nil, nil, xerrors.Errorf("graph: %w", err) + return nil, xerrors.Errorf("graph: %w", err) } modules := []*tfjson.StateModule{} if plan.PriorState != nil { modules = append(modules, plan.PriorState.Values.RootModule) } modules = append(modules, plan.PlannedValues.RootModule) - return ConvertResourcesAndParameters(modules, rawGraph) + return ConvertState(modules, rawGraph) } // showPlan must only be called while the lock is held. @@ -332,7 +333,7 @@ func (e *executor) apply( if err != nil { return nil, xerrors.Errorf("terraform apply: %w", err) } - resources, parameters, err := e.stateResources(ctx, killCtx) + state, err := e.stateResources(ctx, killCtx) if err != nil { return nil, err } @@ -344,35 +345,35 @@ func (e *executor) apply( return &proto.Provision_Response{ Type: &proto.Provision_Response_Complete{ Complete: &proto.Provision_Complete{ - Parameters: parameters, - Resources: resources, - State: stateContent, + Parameters: state.Parameters, + Resources: state.Resources, + GitAuthProviders: state.GitAuthProviders, + State: stateContent, }, }, }, nil } // stateResources must only be called while the lock is held. -func (e *executor) stateResources(ctx, killCtx context.Context) ([]*proto.Resource, []*proto.RichParameter, error) { +func (e *executor) stateResources(ctx, killCtx context.Context) (*ConvertedState, error) { state, err := e.state(ctx, killCtx) if err != nil { - return nil, nil, err + return nil, err } rawGraph, err := e.graph(ctx, killCtx) if err != nil { - return nil, nil, xerrors.Errorf("get terraform graph: %w", err) + return nil, xerrors.Errorf("get terraform graph: %w", err) } - var resources []*proto.Resource - var parameters []*proto.RichParameter + var converted *ConvertedState if state.Values != nil { - resources, parameters, err = ConvertResourcesAndParameters([]*tfjson.StateModule{ + converted, err = ConvertState([]*tfjson.StateModule{ state.Values.RootModule, }, rawGraph) if err != nil { - return nil, nil, err + return nil, err } } - return resources, parameters, nil + return converted, nil } // state must only be called while the lock is held. diff --git a/provisioner/terraform/resources.go b/provisioner/terraform/resources.go index 69e235adb4ec6..de4fc46c4b7a5 100644 --- a/provisioner/terraform/resources.go +++ b/provisioner/terraform/resources.go @@ -72,17 +72,23 @@ type metadataItem struct { IsNull bool `mapstructure:"is_null"` } -// ConvertResourcesAndParameters consumes Terraform state and a GraphViz representation +type ConvertedState struct { + Resources []*proto.Resource + Parameters []*proto.RichParameter + GitAuthProviders []string +} + +// ConvertState consumes Terraform state and a GraphViz representation // produced by `terraform graph` to produce resources consumable by Coder. // nolint:gocyclo -func ConvertResourcesAndParameters(modules []*tfjson.StateModule, rawGraph string) ([]*proto.Resource, []*proto.RichParameter, error) { +func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*ConvertedState, error) { parsedGraph, err := gographviz.ParseString(rawGraph) if err != nil { - return nil, nil, xerrors.Errorf("parse graph: %w", err) + return nil, xerrors.Errorf("parse graph: %w", err) } graph, err := gographviz.NewAnalysedGraph(parsedGraph) if err != nil { - return nil, nil, xerrors.Errorf("analyze graph: %w", err) + return nil, xerrors.Errorf("analyze graph: %w", err) } resources := make([]*proto.Resource, 0) @@ -118,11 +124,11 @@ func ConvertResourcesAndParameters(modules []*tfjson.StateModule, rawGraph strin var attrs agentAttributes err = mapstructure.Decode(tfResource.AttributeValues, &attrs) if err != nil { - return nil, nil, xerrors.Errorf("decode agent attributes: %w", err) + return nil, xerrors.Errorf("decode agent attributes: %w", err) } if _, ok := agentNames[tfResource.Name]; ok { - return nil, nil, xerrors.Errorf("duplicate agent name: %s", tfResource.Name) + return nil, xerrors.Errorf("duplicate agent name: %s", tfResource.Name) } agentNames[tfResource.Name] = struct{}{} @@ -171,7 +177,7 @@ func ConvertResourcesAndParameters(modules []*tfjson.StateModule, rawGraph strin break } if agentNode == nil { - return nil, nil, xerrors.Errorf("couldn't find node on graph: %q", agentLabel) + return nil, xerrors.Errorf("couldn't find node on graph: %q", agentLabel) } var agentResource *graphResource @@ -260,7 +266,7 @@ func ConvertResourcesAndParameters(modules []*tfjson.StateModule, rawGraph strin var attrs agentAppAttributes err = mapstructure.Decode(resource.AttributeValues, &attrs) if err != nil { - return nil, nil, xerrors.Errorf("decode app attributes: %w", err) + return nil, xerrors.Errorf("decode app attributes: %w", err) } // Default to the resource name if none is set! @@ -277,11 +283,11 @@ func ConvertResourcesAndParameters(modules []*tfjson.StateModule, rawGraph strin } if !provisioner.AppSlugRegex.MatchString(attrs.Slug) { - return nil, nil, xerrors.Errorf("invalid app slug %q, please update your coder/coder provider to the latest version and specify the slug property on each coder_app", attrs.Slug) + return nil, xerrors.Errorf("invalid app slug %q, please update your coder/coder provider to the latest version and specify the slug property on each coder_app", attrs.Slug) } if _, exists := appSlugs[attrs.Slug]; exists { - return nil, nil, xerrors.Errorf("duplicate app slug, they must be unique per template: %q", attrs.Slug) + return nil, xerrors.Errorf("duplicate app slug, they must be unique per template: %q", attrs.Slug) } appSlugs[attrs.Slug] = struct{}{} @@ -341,7 +347,7 @@ func ConvertResourcesAndParameters(modules []*tfjson.StateModule, rawGraph strin var attrs metadataAttributes err = mapstructure.Decode(resource.AttributeValues, &attrs) if err != nil { - return nil, nil, xerrors.Errorf("decode metadata attributes: %w", err) + return nil, xerrors.Errorf("decode metadata attributes: %w", err) } resourceLabel := convertAddressToLabel(resource.Address) @@ -432,7 +438,7 @@ func ConvertResourcesAndParameters(modules []*tfjson.StateModule, rawGraph strin var param provider.Parameter err = mapstructure.Decode(resource.AttributeValues, ¶m) if err != nil { - return nil, nil, xerrors.Errorf("decode map values for coder_parameter.%s: %w", resource.Name, err) + return nil, xerrors.Errorf("decode map values for coder_parameter.%s: %w", resource.Name, err) } protoParam := &proto.RichParameter{ Name: param.Name, @@ -464,7 +470,31 @@ func ConvertResourcesAndParameters(modules []*tfjson.StateModule, rawGraph strin } } - return resources, parameters, nil + // A map is used to ensure we don't have duplicates! + gitAuthProvidersMap := map[string]struct{}{} + for _, tfResources := range tfResourcesByLabel { + for _, resource := range tfResources { + // + if resource.Type != "coder_git_auth" { + continue + } + id, ok := resource.AttributeValues["id"].(string) + if !ok { + return nil, xerrors.Errorf("git auth id is not a string") + } + gitAuthProvidersMap[id] = struct{}{} + } + } + gitAuthProviders := make([]string, 0, len(gitAuthProvidersMap)) + for id := range gitAuthProvidersMap { + gitAuthProviders = append(gitAuthProviders, id) + } + + return &ConvertedState{ + Resources: resources, + Parameters: parameters, + GitAuthProviders: gitAuthProviders, + }, nil } // convertAddressToLabel returns the Terraform address without the count diff --git a/provisioner/terraform/resources_test.go b/provisioner/terraform/resources_test.go index cd2acb923df52..ef95ef5154ff8 100644 --- a/provisioner/terraform/resources_test.go +++ b/provisioner/terraform/resources_test.go @@ -22,8 +22,9 @@ func TestConvertResources(t *testing.T) { // nolint:dogsled _, filename, _, _ := runtime.Caller(0) type testCase struct { - resources []*proto.Resource - parameters []*proto.RichParameter + resources []*proto.Resource + parameters []*proto.RichParameter + gitAuthProviders []string } // nolint:paralleltest for folderName, expected := range map[string]testCase{ @@ -294,6 +295,22 @@ func TestConvertResources(t *testing.T) { }}, }}, }, + "git-auth-providers": { + resources: []*proto.Resource{{ + Name: "dev", + Type: "null_resource", + Agents: []*proto.Agent{{ + Name: "main", + OperatingSystem: "linux", + Architecture: "amd64", + Auth: &proto.Agent_Token{}, + LoginBeforeReady: true, + ConnectionTimeoutSeconds: 120, + StartupScriptTimeoutSeconds: 300, + }}, + }}, + gitAuthProviders: []string{"github", "gitlab"}, + }, } { folderName := folderName expected := expected @@ -319,10 +336,11 @@ func TestConvertResources(t *testing.T) { // and that no errors occur! modules = append(modules, tfPlan.PlannedValues.RootModule) } - resources, parameters, err := terraform.ConvertResourcesAndParameters(modules, string(tfPlanGraph)) + state, err := terraform.ConvertState(modules, string(tfPlanGraph)) require.NoError(t, err) - sortResources(resources) - sortParameters(parameters) + sortResources(state.Resources) + sortParameters(state.Parameters) + sort.Strings(state.GitAuthProviders) expectedNoMetadata := make([]*proto.Resource, 0) for _, resource := range expected.resources { @@ -342,7 +360,7 @@ func TestConvertResources(t *testing.T) { err = json.Unmarshal(data, &expectedNoMetadataMap) require.NoError(t, err) - data, err = json.Marshal(resources) + data, err = json.Marshal(state.Resources) require.NoError(t, err) var resourcesMap []map[string]interface{} err = json.Unmarshal(data, &resourcesMap) @@ -354,10 +372,12 @@ func TestConvertResources(t *testing.T) { } parametersWant, err := json.Marshal(expected.parameters) require.NoError(t, err) - parametersGot, err := json.Marshal(parameters) + parametersGot, err := json.Marshal(state.Parameters) require.NoError(t, err) require.Equal(t, string(parametersWant), string(parametersGot)) require.Equal(t, expectedNoMetadataMap, resourcesMap) + + require.ElementsMatch(t, expected.gitAuthProviders, state.GitAuthProviders) }) t.Run("Provision", func(t *testing.T) { @@ -370,11 +390,12 @@ func TestConvertResources(t *testing.T) { tfStateGraph, err := os.ReadFile(filepath.Join(dir, folderName+".tfstate.dot")) require.NoError(t, err) - resources, parameters, err := terraform.ConvertResourcesAndParameters([]*tfjson.StateModule{tfState.Values.RootModule}, string(tfStateGraph)) + state, err := terraform.ConvertState([]*tfjson.StateModule{tfState.Values.RootModule}, string(tfStateGraph)) require.NoError(t, err) - sortResources(resources) - sortParameters(parameters) - for _, resource := range resources { + sortResources(state.Resources) + sortParameters(state.Parameters) + sort.Strings(state.GitAuthProviders) + for _, resource := range state.Resources { for _, agent := range resource.Agents { agent.Id = "" if agent.GetToken() != "" { @@ -393,13 +414,14 @@ func TestConvertResources(t *testing.T) { err = json.Unmarshal(data, &expectedMap) require.NoError(t, err) - data, err = json.Marshal(resources) + data, err = json.Marshal(state.Resources) require.NoError(t, err) var resourcesMap []map[string]interface{} err = json.Unmarshal(data, &resourcesMap) require.NoError(t, err) require.Equal(t, expectedMap, resourcesMap) + require.ElementsMatch(t, expected.gitAuthProviders, state.GitAuthProviders) }) }) } @@ -428,8 +450,8 @@ func TestAppSlugValidation(t *testing.T) { } } - resources, _, err := terraform.ConvertResourcesAndParameters([]*tfjson.StateModule{tfPlan.PlannedValues.RootModule}, string(tfPlanGraph)) - require.Nil(t, resources) + state, err := terraform.ConvertState([]*tfjson.StateModule{tfPlan.PlannedValues.RootModule}, string(tfPlanGraph)) + require.Nil(t, state) require.Error(t, err) require.ErrorContains(t, err, "invalid app slug") @@ -440,8 +462,8 @@ func TestAppSlugValidation(t *testing.T) { } } - resources, _, err = terraform.ConvertResourcesAndParameters([]*tfjson.StateModule{tfPlan.PlannedValues.RootModule}, string(tfPlanGraph)) - require.Nil(t, resources) + state, err = terraform.ConvertState([]*tfjson.StateModule{tfPlan.PlannedValues.RootModule}, string(tfPlanGraph)) + require.Nil(t, state) require.Error(t, err) require.ErrorContains(t, err, "duplicate app slug") } @@ -473,7 +495,7 @@ func TestInstanceTypeAssociation(t *testing.T) { t.Parallel() instanceType, err := cryptorand.String(12) require.NoError(t, err) - resources, _, err := terraform.ConvertResourcesAndParameters([]*tfjson.StateModule{{ + state, err := terraform.ConvertState([]*tfjson.StateModule{{ Resources: []*tfjson.StateResource{{ Address: tc.ResourceType + ".dev", Type: tc.ResourceType, @@ -492,8 +514,8 @@ func TestInstanceTypeAssociation(t *testing.T) { } }`) require.NoError(t, err) - require.Len(t, resources, 1) - require.Equal(t, resources[0].GetInstanceType(), instanceType) + require.Len(t, state.Resources, 1) + require.Equal(t, state.Resources[0].GetInstanceType(), instanceType) }) } } @@ -531,7 +553,7 @@ func TestInstanceIDAssociation(t *testing.T) { t.Parallel() instanceID, err := cryptorand.String(12) require.NoError(t, err) - resources, _, err := terraform.ConvertResourcesAndParameters([]*tfjson.StateModule{{ + state, err := terraform.ConvertState([]*tfjson.StateModule{{ Resources: []*tfjson.StateResource{{ Address: "coder_agent.dev", Type: "coder_agent", @@ -563,9 +585,9 @@ func TestInstanceIDAssociation(t *testing.T) { } `) require.NoError(t, err) - require.Len(t, resources, 1) - require.Len(t, resources[0].Agents, 1) - require.Equal(t, resources[0].Agents[0].GetInstanceId(), instanceID) + require.Len(t, state.Resources, 1) + require.Len(t, state.Resources[0].Agents, 1) + require.Equal(t, state.Resources[0].Agents[0].GetInstanceId(), instanceID) }) } } diff --git a/provisioner/terraform/testdata/git-auth-providers/git-auth-providers.tf b/provisioner/terraform/testdata/git-auth-providers/git-auth-providers.tf new file mode 100644 index 0000000000000..e76479c459043 --- /dev/null +++ b/provisioner/terraform/testdata/git-auth-providers/git-auth-providers.tf @@ -0,0 +1,27 @@ +terraform { + required_providers { + coder = { + source = "coder/coder" + version = "0.6.13" + } + } +} + +data "coder_git_auth" "github" { + id = "github" +} + +data "coder_git_auth" "gitlab" { + id = "gitlab" +} + +resource "coder_agent" "main" { + os = "linux" + arch = "amd64" +} + +resource "null_resource" "dev" { + depends_on = [ + coder_agent.main + ] +} diff --git a/provisioner/terraform/testdata/git-auth-providers/git-auth-providers.tfplan.dot b/provisioner/terraform/testdata/git-auth-providers/git-auth-providers.tfplan.dot new file mode 100644 index 0000000000000..3d0775104e9c8 --- /dev/null +++ b/provisioner/terraform/testdata/git-auth-providers/git-auth-providers.tfplan.dot @@ -0,0 +1,24 @@ +digraph { + compound = "true" + newrank = "true" + subgraph "root" { + "[root] coder_agent.main (expand)" [label = "coder_agent.main", shape = "box"] + "[root] data.coder_git_auth.github (expand)" [label = "data.coder_git_auth.github", shape = "box"] + "[root] data.coder_git_auth.gitlab (expand)" [label = "data.coder_git_auth.gitlab", shape = "box"] + "[root] null_resource.dev (expand)" [label = "null_resource.dev", shape = "box"] + "[root] provider[\"registry.terraform.io/coder/coder\"]" [label = "provider[\"registry.terraform.io/coder/coder\"]", shape = "diamond"] + "[root] provider[\"registry.terraform.io/hashicorp/null\"]" [label = "provider[\"registry.terraform.io/hashicorp/null\"]", shape = "diamond"] + "[root] coder_agent.main (expand)" -> "[root] provider[\"registry.terraform.io/coder/coder\"]" + "[root] data.coder_git_auth.github (expand)" -> "[root] provider[\"registry.terraform.io/coder/coder\"]" + "[root] data.coder_git_auth.gitlab (expand)" -> "[root] provider[\"registry.terraform.io/coder/coder\"]" + "[root] null_resource.dev (expand)" -> "[root] coder_agent.main (expand)" + "[root] null_resource.dev (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/null\"]" + "[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] coder_agent.main (expand)" + "[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] data.coder_git_auth.github (expand)" + "[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] data.coder_git_auth.gitlab (expand)" + "[root] provider[\"registry.terraform.io/hashicorp/null\"] (close)" -> "[root] null_resource.dev (expand)" + "[root] root" -> "[root] provider[\"registry.terraform.io/coder/coder\"] (close)" + "[root] root" -> "[root] provider[\"registry.terraform.io/hashicorp/null\"] (close)" + } +} + diff --git a/provisioner/terraform/testdata/git-auth-providers/git-auth-providers.tfplan.json b/provisioner/terraform/testdata/git-auth-providers/git-auth-providers.tfplan.json new file mode 100644 index 0000000000000..05d34d9265d50 --- /dev/null +++ b/provisioner/terraform/testdata/git-auth-providers/git-auth-providers.tfplan.json @@ -0,0 +1,212 @@ +{ + "format_version": "1.1", + "terraform_version": "1.3.7", + "planned_values": { + "root_module": { + "resources": [ + { + "address": "coder_agent.main", + "mode": "managed", + "type": "coder_agent", + "name": "main", + "provider_name": "registry.terraform.io/coder/coder", + "schema_version": 0, + "values": { + "arch": "amd64", + "auth": "token", + "connection_timeout": 120, + "dir": null, + "env": null, + "login_before_ready": true, + "motd_file": null, + "os": "linux", + "shutdown_script": null, + "shutdown_script_timeout": 300, + "startup_script": null, + "startup_script_timeout": 300, + "troubleshooting_url": null + }, + "sensitive_values": {} + }, + { + "address": "null_resource.dev", + "mode": "managed", + "type": "null_resource", + "name": "dev", + "provider_name": "registry.terraform.io/hashicorp/null", + "schema_version": 0, + "values": { + "triggers": null + }, + "sensitive_values": {} + } + ] + } + }, + "resource_changes": [ + { + "address": "coder_agent.main", + "mode": "managed", + "type": "coder_agent", + "name": "main", + "provider_name": "registry.terraform.io/coder/coder", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "arch": "amd64", + "auth": "token", + "connection_timeout": 120, + "dir": null, + "env": null, + "login_before_ready": true, + "motd_file": null, + "os": "linux", + "shutdown_script": null, + "shutdown_script_timeout": 300, + "startup_script": null, + "startup_script_timeout": 300, + "troubleshooting_url": null + }, + "after_unknown": { + "id": true, + "init_script": true, + "token": true + }, + "before_sensitive": false, + "after_sensitive": { + "token": true + } + } + }, + { + "address": "null_resource.dev", + "mode": "managed", + "type": "null_resource", + "name": "dev", + "provider_name": "registry.terraform.io/hashicorp/null", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "triggers": null + }, + "after_unknown": { + "id": true + }, + "before_sensitive": false, + "after_sensitive": {} + } + } + ], + "prior_state": { + "format_version": "1.0", + "terraform_version": "1.3.7", + "values": { + "root_module": { + "resources": [ + { + "address": "data.coder_git_auth.github", + "mode": "data", + "type": "coder_git_auth", + "name": "github", + "provider_name": "registry.terraform.io/coder/coder", + "schema_version": 0, + "values": { + "access_token": "", + "id": "github" + }, + "sensitive_values": {} + }, + { + "address": "data.coder_git_auth.gitlab", + "mode": "data", + "type": "coder_git_auth", + "name": "gitlab", + "provider_name": "registry.terraform.io/coder/coder", + "schema_version": 0, + "values": { + "access_token": "", + "id": "gitlab" + }, + "sensitive_values": {} + } + ] + } + } + }, + "configuration": { + "provider_config": { + "coder": { + "name": "coder", + "full_name": "registry.terraform.io/coder/coder", + "version_constraint": "0.6.13" + }, + "null": { + "name": "null", + "full_name": "registry.terraform.io/hashicorp/null" + } + }, + "root_module": { + "resources": [ + { + "address": "coder_agent.main", + "mode": "managed", + "type": "coder_agent", + "name": "main", + "provider_config_key": "coder", + "expressions": { + "arch": { + "constant_value": "amd64" + }, + "os": { + "constant_value": "linux" + } + }, + "schema_version": 0 + }, + { + "address": "null_resource.dev", + "mode": "managed", + "type": "null_resource", + "name": "dev", + "provider_config_key": "null", + "schema_version": 0, + "depends_on": [ + "coder_agent.main" + ] + }, + { + "address": "data.coder_git_auth.github", + "mode": "data", + "type": "coder_git_auth", + "name": "github", + "provider_config_key": "coder", + "expressions": { + "id": { + "constant_value": "github" + } + }, + "schema_version": 0 + }, + { + "address": "data.coder_git_auth.gitlab", + "mode": "data", + "type": "coder_git_auth", + "name": "gitlab", + "provider_config_key": "coder", + "expressions": { + "id": { + "constant_value": "gitlab" + } + }, + "schema_version": 0 + } + ] + } + } +} diff --git a/provisioner/terraform/testdata/git-auth-providers/git-auth-providers.tfstate.dot b/provisioner/terraform/testdata/git-auth-providers/git-auth-providers.tfstate.dot new file mode 100644 index 0000000000000..3d0775104e9c8 --- /dev/null +++ b/provisioner/terraform/testdata/git-auth-providers/git-auth-providers.tfstate.dot @@ -0,0 +1,24 @@ +digraph { + compound = "true" + newrank = "true" + subgraph "root" { + "[root] coder_agent.main (expand)" [label = "coder_agent.main", shape = "box"] + "[root] data.coder_git_auth.github (expand)" [label = "data.coder_git_auth.github", shape = "box"] + "[root] data.coder_git_auth.gitlab (expand)" [label = "data.coder_git_auth.gitlab", shape = "box"] + "[root] null_resource.dev (expand)" [label = "null_resource.dev", shape = "box"] + "[root] provider[\"registry.terraform.io/coder/coder\"]" [label = "provider[\"registry.terraform.io/coder/coder\"]", shape = "diamond"] + "[root] provider[\"registry.terraform.io/hashicorp/null\"]" [label = "provider[\"registry.terraform.io/hashicorp/null\"]", shape = "diamond"] + "[root] coder_agent.main (expand)" -> "[root] provider[\"registry.terraform.io/coder/coder\"]" + "[root] data.coder_git_auth.github (expand)" -> "[root] provider[\"registry.terraform.io/coder/coder\"]" + "[root] data.coder_git_auth.gitlab (expand)" -> "[root] provider[\"registry.terraform.io/coder/coder\"]" + "[root] null_resource.dev (expand)" -> "[root] coder_agent.main (expand)" + "[root] null_resource.dev (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/null\"]" + "[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] coder_agent.main (expand)" + "[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] data.coder_git_auth.github (expand)" + "[root] provider[\"registry.terraform.io/coder/coder\"] (close)" -> "[root] data.coder_git_auth.gitlab (expand)" + "[root] provider[\"registry.terraform.io/hashicorp/null\"] (close)" -> "[root] null_resource.dev (expand)" + "[root] root" -> "[root] provider[\"registry.terraform.io/coder/coder\"] (close)" + "[root] root" -> "[root] provider[\"registry.terraform.io/hashicorp/null\"] (close)" + } +} + diff --git a/provisioner/terraform/testdata/git-auth-providers/git-auth-providers.tfstate.json b/provisioner/terraform/testdata/git-auth-providers/git-auth-providers.tfstate.json new file mode 100644 index 0000000000000..268ed84b45ae3 --- /dev/null +++ b/provisioner/terraform/testdata/git-auth-providers/git-auth-providers.tfstate.json @@ -0,0 +1,79 @@ +{ + "format_version": "1.0", + "terraform_version": "1.3.7", + "values": { + "root_module": { + "resources": [ + { + "address": "coder_agent.main", + "mode": "managed", + "type": "coder_agent", + "name": "main", + "provider_name": "registry.terraform.io/coder/coder", + "schema_version": 0, + "values": { + "arch": "amd64", + "auth": "token", + "connection_timeout": 120, + "dir": null, + "env": null, + "id": "78b29f93-097d-403b-ab56-0bc943d427cc", + "init_script": "", + "login_before_ready": true, + "motd_file": null, + "os": "linux", + "shutdown_script": null, + "shutdown_script_timeout": 300, + "startup_script": null, + "startup_script_timeout": 300, + "token": "a57838e5-355c-471a-9a85-f81314fbaec6", + "troubleshooting_url": null + }, + "sensitive_values": {} + }, + { + "address": "data.coder_git_auth.github", + "mode": "data", + "type": "coder_git_auth", + "name": "github", + "provider_name": "registry.terraform.io/coder/coder", + "schema_version": 0, + "values": { + "access_token": "", + "id": "github" + }, + "sensitive_values": {} + }, + { + "address": "data.coder_git_auth.gitlab", + "mode": "data", + "type": "coder_git_auth", + "name": "gitlab", + "provider_name": "registry.terraform.io/coder/coder", + "schema_version": 0, + "values": { + "access_token": "", + "id": "gitlab" + }, + "sensitive_values": {} + }, + { + "address": "null_resource.dev", + "mode": "managed", + "type": "null_resource", + "name": "dev", + "provider_name": "registry.terraform.io/hashicorp/null", + "schema_version": 0, + "values": { + "id": "1416347524569828366", + "triggers": null + }, + "sensitive_values": {}, + "depends_on": [ + "coder_agent.main" + ] + } + ] + } + } +} diff --git a/provisionerd/proto/provisionerd.pb.go b/provisionerd/proto/provisionerd.pb.go index e39e7c9ca27a8..f41912a1fa239 100644 --- a/provisionerd/proto/provisionerd.pb.go +++ b/provisionerd/proto/provisionerd.pb.go @@ -1213,9 +1213,10 @@ type CompletedJob_TemplateImport struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - 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"` - RichParameters []*proto.RichParameter `protobuf:"bytes,3,rep,name=rich_parameters,json=richParameters,proto3" json:"rich_parameters,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"` + RichParameters []*proto.RichParameter `protobuf:"bytes,3,rep,name=rich_parameters,json=richParameters,proto3" json:"rich_parameters,omitempty"` + GitAuthProviders []string `protobuf:"bytes,4,rep,name=git_auth_providers,json=gitAuthProviders,proto3" json:"git_auth_providers,omitempty"` } func (x *CompletedJob_TemplateImport) Reset() { @@ -1271,6 +1272,13 @@ func (x *CompletedJob_TemplateImport) GetRichParameters() []*proto.RichParameter return nil } +func (x *CompletedJob_TemplateImport) GetGitAuthProviders() []string { + if x != nil { + return x.GitAuthProviders + } + return nil +} + type CompletedJob_TemplateDryRun struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1435,7 +1443,7 @@ var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{ 0x73, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x10, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x1a, 0x10, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x22, 0xaa, 0x05, 0x0a, 0x0c, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, + 0x65, 0x22, 0xd8, 0x05, 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, 0x54, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, @@ -1459,7 +1467,7 @@ var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{ 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, 0xd3, 0x01, 0x0a, 0x0e, 0x54, 0x65, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x1a, 0x81, 0x02, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 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, @@ -1472,98 +1480,100 @@ var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{ 0x63, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x52, - 0x0e, 0x72, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x1a, - 0x45, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, - 0x6e, 0x12, 0x33, 0x0a, 0x09, 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, 0x09, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xb0, - 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, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, - 0x74, 0x22, 0xcf, 0x02, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, + 0x0e, 0x72, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, + 0x2c, 0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x67, 0x69, 0x74, + 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x1a, 0x45, 0x0a, + 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, + 0x33, 0x0a, 0x09, 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, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xb0, 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, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, + 0xcf, 0x02, 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, 0x4c, 0x0a, + 0x12, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, + 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x4c, 0x0a, 0x14, 0x75, + 0x73, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x75, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, 0x69, 0x61, + 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, + 0x64, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, + 0x65, 0x22, 0xbc, 0x01, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x6e, 0x63, 0x65, + 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x63, 0x61, 0x6e, 0x63, 0x65, + 0x6c, 0x65, 0x64, 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, 0x43, 0x0a, 0x0f, 0x76, + 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x0e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x22, 0x4a, 0x0a, 0x12, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 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, - 0x4c, 0x0a, 0x12, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x72, 0x69, - 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x4c, 0x0a, - 0x14, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, - 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x75, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, - 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, - 0x65, 0x61, 0x64, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, 0x65, 0x61, - 0x64, 0x6d, 0x65, 0x22, 0xbc, 0x01, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, - 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x63, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x65, 0x64, 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, 0x43, 0x0a, - 0x0f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x0e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x22, 0x4a, 0x0a, 0x12, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, - 0x61, 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, - 0x1d, 0x0a, 0x0a, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x09, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x22, 0x68, - 0x0a, 0x13, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x73, - 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x0f, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, - 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x06, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 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, 0xec, - 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, 0x52, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, - 0x61, 0x12, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, - 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 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, 0x37, 0x0a, 0x07, 0x46, 0x61, 0x69, 0x6c, 0x4a, 0x6f, 0x62, 0x12, - 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x46, - 0x61, 0x69, 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, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x1d, 0x0a, + 0x0a, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x09, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x22, 0x68, 0x0a, 0x13, + 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x02, 0x6f, 0x6b, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x73, 0x5f, 0x63, + 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x63, + 0x72, 0x65, 0x64, 0x69, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x12, 0x16, + 0x0a, 0x06, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, + 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 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, 0xec, 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, 0x52, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x12, + 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, + 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 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, 0x37, 0x0a, 0x07, 0x46, 0x61, 0x69, 0x6c, 0x4a, 0x6f, 0x62, 0x12, 0x17, 0x2e, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x46, 0x61, 0x69, + 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3e, 0x0a, 0x0b, 0x43, + 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, + 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x2b, 0x5a, 0x29, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, + 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x72, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/provisionerd/proto/provisionerd.proto b/provisionerd/proto/provisionerd.proto index 90ea7443ff5a2..12283c6e41da9 100644 --- a/provisionerd/proto/provisionerd.proto +++ b/provisionerd/proto/provisionerd.proto @@ -69,6 +69,7 @@ message CompletedJob { repeated provisioner.Resource start_resources = 1; repeated provisioner.Resource stop_resources = 2; repeated provisioner.RichParameter rich_parameters = 3; + repeated string git_auth_providers = 4; } message TemplateDryRun { repeated provisioner.Resource resources = 1; diff --git a/provisionerd/runner/runner.go b/provisionerd/runner/runner.go index 8b418948053b4..958a6956fbb07 100644 --- a/provisionerd/runner/runner.go +++ b/provisionerd/runner/runner.go @@ -575,7 +575,7 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p Stage: "Detecting persistent resources", CreatedAt: time.Now().UnixMilli(), }) - startResources, parameters, err := r.runTemplateImportProvision(ctx, updateResponse.ParameterValues, updateResponse.VariableValues, &sdkproto.Provision_Metadata{ + startProvision, err := r.runTemplateImportProvision(ctx, updateResponse.ParameterValues, updateResponse.VariableValues, &sdkproto.Provision_Metadata{ CoderUrl: r.job.GetTemplateImport().Metadata.CoderUrl, WorkspaceTransition: sdkproto.WorkspaceTransition_START, }) @@ -590,7 +590,7 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p Stage: "Detecting ephemeral resources", CreatedAt: time.Now().UnixMilli(), }) - stopResources, _, err := r.runTemplateImportProvision(ctx, updateResponse.ParameterValues, updateResponse.VariableValues, &sdkproto.Provision_Metadata{ + stopProvision, err := r.runTemplateImportProvision(ctx, updateResponse.ParameterValues, updateResponse.VariableValues, &sdkproto.Provision_Metadata{ CoderUrl: r.job.GetTemplateImport().Metadata.CoderUrl, WorkspaceTransition: sdkproto.WorkspaceTransition_STOP, }) @@ -602,9 +602,10 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p JobId: r.job.JobId, Type: &proto.CompletedJob_TemplateImport_{ TemplateImport: &proto.CompletedJob_TemplateImport{ - StartResources: startResources, - StopResources: stopResources, - RichParameters: parameters, + StartResources: startProvision.Resources, + StopResources: stopProvision.Resources, + RichParameters: startProvision.Parameters, + GitAuthProviders: startProvision.GitAuthProviders, }, }, }, nil @@ -655,16 +656,22 @@ func (r *Runner) runTemplateImportParse(ctx context.Context) ([]*sdkproto.Parame } } +type templateImportProvision struct { + Resources []*sdkproto.Resource + Parameters []*sdkproto.RichParameter + GitAuthProviders []string +} + // Performs a dry-run provision when importing a template. // This is used to detect resources that would be provisioned for a workspace in various states. // It doesn't define values for rich parameters as they're unknown during template import. -func (r *Runner) runTemplateImportProvision(ctx context.Context, values []*sdkproto.ParameterValue, variableValues []*sdkproto.VariableValue, metadata *sdkproto.Provision_Metadata) ([]*sdkproto.Resource, []*sdkproto.RichParameter, error) { +func (r *Runner) runTemplateImportProvision(ctx context.Context, values []*sdkproto.ParameterValue, variableValues []*sdkproto.VariableValue, metadata *sdkproto.Provision_Metadata) (*templateImportProvision, error) { return r.runTemplateImportProvisionWithRichParameters(ctx, values, variableValues, nil, metadata) } // Performs a dry-run provision with provided rich parameters. // This is used to detect resources that would be provisioned for a workspace in various states. -func (r *Runner) runTemplateImportProvisionWithRichParameters(ctx context.Context, values []*sdkproto.ParameterValue, variableValues []*sdkproto.VariableValue, richParameterValues []*sdkproto.RichParameterValue, metadata *sdkproto.Provision_Metadata) ([]*sdkproto.Resource, []*sdkproto.RichParameter, error) { +func (r *Runner) runTemplateImportProvisionWithRichParameters(ctx context.Context, values []*sdkproto.ParameterValue, variableValues []*sdkproto.VariableValue, richParameterValues []*sdkproto.RichParameterValue, metadata *sdkproto.Provision_Metadata) (*templateImportProvision, error) { ctx, span := r.startTrace(ctx, tracing.FuncName()) defer span.End() @@ -679,7 +686,7 @@ func (r *Runner) runTemplateImportProvisionWithRichParameters(ctx context.Contex // to send the cancel to the provisioner stream, err := r.provisioner.Provision(ctx) if err != nil { - return nil, nil, xerrors.Errorf("provision: %w", err) + return nil, xerrors.Errorf("provision: %w", err) } defer stream.Close() go func() { @@ -708,13 +715,13 @@ func (r *Runner) runTemplateImportProvisionWithRichParameters(ctx context.Contex }, }) if err != nil { - return nil, nil, xerrors.Errorf("start provision: %w", err) + return nil, xerrors.Errorf("start provision: %w", err) } for { msg, err := stream.Recv() if err != nil { - return nil, nil, xerrors.Errorf("recv import provision: %w", err) + return nil, xerrors.Errorf("recv import provision: %w", err) } switch msgType := msg.Type.(type) { case *sdkproto.Provision_Response_Log: @@ -735,12 +742,12 @@ func (r *Runner) runTemplateImportProvisionWithRichParameters(ctx context.Contex slog.F("error", msgType.Complete.Error), ) - return nil, nil, xerrors.New(msgType.Complete.Error) + return nil, xerrors.New(msgType.Complete.Error) } if len(msgType.Complete.Parameters) > 0 && len(values) > 0 { r.logger.Info(context.Background(), "template uses rich parameters which can't be used together with legacy parameters") - return nil, nil, xerrors.Errorf("invalid use of rich parameters") + return nil, xerrors.Errorf("invalid use of rich parameters") } r.logger.Info(context.Background(), "parse dry-run provision successful", @@ -749,9 +756,13 @@ func (r *Runner) runTemplateImportProvisionWithRichParameters(ctx context.Contex slog.F("state_length", len(msgType.Complete.State)), ) - return msgType.Complete.Resources, msgType.Complete.Parameters, nil + return &templateImportProvision{ + Resources: msgType.Complete.Resources, + Parameters: msgType.Complete.Parameters, + GitAuthProviders: msgType.Complete.GitAuthProviders, + }, nil default: - return nil, nil, xerrors.Errorf("invalid message type %q received from provisioner", + return nil, xerrors.Errorf("invalid message type %q received from provisioner", reflect.TypeOf(msg.Type).String()) } } @@ -790,7 +801,7 @@ func (r *Runner) runTemplateDryRun(ctx context.Context) (*proto.CompletedJob, *p } // Run the template import provision task since it's already a dry run. - resources, _, err := r.runTemplateImportProvisionWithRichParameters(ctx, + provision, err := r.runTemplateImportProvisionWithRichParameters(ctx, r.job.GetTemplateDryRun().GetParameterValues(), r.job.GetTemplateDryRun().GetVariableValues(), r.job.GetTemplateDryRun().GetRichParameterValues(), @@ -804,7 +815,7 @@ func (r *Runner) runTemplateDryRun(ctx context.Context) (*proto.CompletedJob, *p JobId: r.job.JobId, Type: &proto.CompletedJob_TemplateDryRun_{ TemplateDryRun: &proto.CompletedJob_TemplateDryRun{ - Resources: resources, + Resources: provision.Resources, }, }, }, nil diff --git a/provisionersdk/proto/provisioner.pb.go b/provisionersdk/proto/provisioner.pb.go index 205949cfb0a7b..f9212fc0db4dc 100644 --- a/provisionersdk/proto/provisioner.pb.go +++ b/provisionersdk/proto/provisioner.pb.go @@ -1163,6 +1163,61 @@ func (x *InstanceIdentityAuth) GetInstanceId() string { return "" } +type GitAuthProvider struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + AccessToken string `protobuf:"bytes,2,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` +} + +func (x *GitAuthProvider) Reset() { + *x = GitAuthProvider{} + if protoimpl.UnsafeEnabled { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GitAuthProvider) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GitAuthProvider) ProtoMessage() {} + +func (x *GitAuthProvider) ProtoReflect() protoreflect.Message { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[12] + 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 GitAuthProvider.ProtoReflect.Descriptor instead. +func (*GitAuthProvider) Descriptor() ([]byte, []int) { + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{12} +} + +func (x *GitAuthProvider) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *GitAuthProvider) GetAccessToken() string { + if x != nil { + return x.AccessToken + } + return "" +} + // Agent represents a running agent on the workspace. type Agent struct { state protoimpl.MessageState @@ -1192,7 +1247,7 @@ type Agent struct { func (x *Agent) Reset() { *x = Agent{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[12] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1205,7 +1260,7 @@ func (x *Agent) String() string { func (*Agent) ProtoMessage() {} func (x *Agent) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[12] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1218,7 +1273,7 @@ func (x *Agent) ProtoReflect() protoreflect.Message { // Deprecated: Use Agent.ProtoReflect.Descriptor instead. func (*Agent) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{12} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{13} } func (x *Agent) GetId() string { @@ -1371,7 +1426,7 @@ type App struct { func (x *App) Reset() { *x = App{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[13] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1384,7 +1439,7 @@ func (x *App) String() string { func (*App) ProtoMessage() {} func (x *App) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[13] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1397,7 +1452,7 @@ func (x *App) ProtoReflect() protoreflect.Message { // Deprecated: Use App.ProtoReflect.Descriptor instead. func (*App) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{13} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{14} } func (x *App) GetSlug() string { @@ -1477,7 +1532,7 @@ type Healthcheck struct { func (x *Healthcheck) Reset() { *x = Healthcheck{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[14] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1490,7 +1545,7 @@ func (x *Healthcheck) String() string { func (*Healthcheck) ProtoMessage() {} func (x *Healthcheck) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[14] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1503,7 +1558,7 @@ func (x *Healthcheck) ProtoReflect() protoreflect.Message { // Deprecated: Use Healthcheck.ProtoReflect.Descriptor instead. func (*Healthcheck) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{14} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{15} } func (x *Healthcheck) GetUrl() string { @@ -1546,7 +1601,7 @@ type Resource struct { func (x *Resource) Reset() { *x = Resource{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[15] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1559,7 +1614,7 @@ func (x *Resource) String() string { func (*Resource) ProtoMessage() {} func (x *Resource) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[15] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1572,7 +1627,7 @@ func (x *Resource) ProtoReflect() protoreflect.Message { // Deprecated: Use Resource.ProtoReflect.Descriptor instead. func (*Resource) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{15} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{16} } func (x *Resource) GetName() string { @@ -1641,7 +1696,7 @@ type Parse struct { func (x *Parse) Reset() { *x = Parse{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[16] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1654,7 +1709,7 @@ func (x *Parse) String() string { func (*Parse) ProtoMessage() {} func (x *Parse) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[16] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1667,7 +1722,7 @@ func (x *Parse) ProtoReflect() protoreflect.Message { // Deprecated: Use Parse.ProtoReflect.Descriptor instead. func (*Parse) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{16} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{17} } // Provision consumes source-code from a directory to produce resources. @@ -1681,7 +1736,7 @@ type Provision struct { func (x *Provision) Reset() { *x = Provision{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[17] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1694,7 +1749,7 @@ func (x *Provision) String() string { func (*Provision) ProtoMessage() {} func (x *Provision) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[17] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1707,7 +1762,7 @@ func (x *Provision) ProtoReflect() protoreflect.Message { // Deprecated: Use Provision.ProtoReflect.Descriptor instead. func (*Provision) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{17} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{18} } type Resource_Metadata struct { @@ -1724,7 +1779,7 @@ type Resource_Metadata struct { func (x *Resource_Metadata) Reset() { *x = Resource_Metadata{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[19] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1737,7 +1792,7 @@ func (x *Resource_Metadata) String() string { func (*Resource_Metadata) ProtoMessage() {} func (x *Resource_Metadata) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[19] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1750,7 +1805,7 @@ func (x *Resource_Metadata) ProtoReflect() protoreflect.Message { // Deprecated: Use Resource_Metadata.ProtoReflect.Descriptor instead. func (*Resource_Metadata) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{15, 0} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{16, 0} } func (x *Resource_Metadata) GetKey() string { @@ -1792,7 +1847,7 @@ type Parse_Request struct { func (x *Parse_Request) Reset() { *x = Parse_Request{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[20] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1805,7 +1860,7 @@ func (x *Parse_Request) String() string { func (*Parse_Request) ProtoMessage() {} func (x *Parse_Request) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[20] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1818,7 +1873,7 @@ func (x *Parse_Request) ProtoReflect() protoreflect.Message { // Deprecated: Use Parse_Request.ProtoReflect.Descriptor instead. func (*Parse_Request) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{16, 0} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{17, 0} } func (x *Parse_Request) GetDirectory() string { @@ -1840,7 +1895,7 @@ type Parse_Complete struct { func (x *Parse_Complete) Reset() { *x = Parse_Complete{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[21] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1853,7 +1908,7 @@ func (x *Parse_Complete) String() string { func (*Parse_Complete) ProtoMessage() {} func (x *Parse_Complete) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[21] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1866,7 +1921,7 @@ func (x *Parse_Complete) ProtoReflect() protoreflect.Message { // Deprecated: Use Parse_Complete.ProtoReflect.Descriptor instead. func (*Parse_Complete) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{16, 1} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{17, 1} } func (x *Parse_Complete) GetTemplateVariables() []*TemplateVariable { @@ -1898,7 +1953,7 @@ type Parse_Response struct { func (x *Parse_Response) Reset() { *x = Parse_Response{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[22] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1911,7 +1966,7 @@ func (x *Parse_Response) String() string { func (*Parse_Response) ProtoMessage() {} func (x *Parse_Response) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[22] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1924,7 +1979,7 @@ func (x *Parse_Response) ProtoReflect() protoreflect.Message { // Deprecated: Use Parse_Response.ProtoReflect.Descriptor instead. func (*Parse_Response) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{16, 2} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{17, 2} } func (m *Parse_Response) GetType() isParse_Response_Type { @@ -1981,7 +2036,7 @@ type Provision_Metadata struct { func (x *Provision_Metadata) Reset() { *x = Provision_Metadata{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[23] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1994,7 +2049,7 @@ func (x *Provision_Metadata) String() string { func (*Provision_Metadata) ProtoMessage() {} func (x *Provision_Metadata) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[23] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2007,7 +2062,7 @@ func (x *Provision_Metadata) ProtoReflect() protoreflect.Message { // Deprecated: Use Provision_Metadata.ProtoReflect.Descriptor instead. func (*Provision_Metadata) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{17, 0} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{18, 0} } func (x *Provision_Metadata) GetCoderUrl() string { @@ -2074,7 +2129,7 @@ type Provision_Config struct { func (x *Provision_Config) Reset() { *x = Provision_Config{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[24] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2087,7 +2142,7 @@ func (x *Provision_Config) String() string { func (*Provision_Config) ProtoMessage() {} func (x *Provision_Config) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[24] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2100,7 +2155,7 @@ func (x *Provision_Config) ProtoReflect() protoreflect.Message { // Deprecated: Use Provision_Config.ProtoReflect.Descriptor instead. func (*Provision_Config) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{17, 1} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{18, 1} } func (x *Provision_Config) GetDirectory() string { @@ -2133,12 +2188,13 @@ type Provision_Plan struct { ParameterValues []*ParameterValue `protobuf:"bytes,2,rep,name=parameter_values,json=parameterValues,proto3" json:"parameter_values,omitempty"` RichParameterValues []*RichParameterValue `protobuf:"bytes,3,rep,name=rich_parameter_values,json=richParameterValues,proto3" json:"rich_parameter_values,omitempty"` VariableValues []*VariableValue `protobuf:"bytes,4,rep,name=variable_values,json=variableValues,proto3" json:"variable_values,omitempty"` + GitAuthProviders []*GitAuthProvider `protobuf:"bytes,5,rep,name=git_auth_providers,json=gitAuthProviders,proto3" json:"git_auth_providers,omitempty"` } func (x *Provision_Plan) Reset() { *x = Provision_Plan{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[25] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2151,7 +2207,7 @@ func (x *Provision_Plan) String() string { func (*Provision_Plan) ProtoMessage() {} func (x *Provision_Plan) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[25] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2164,7 +2220,7 @@ func (x *Provision_Plan) ProtoReflect() protoreflect.Message { // Deprecated: Use Provision_Plan.ProtoReflect.Descriptor instead. func (*Provision_Plan) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{17, 2} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{18, 2} } func (x *Provision_Plan) GetConfig() *Provision_Config { @@ -2195,6 +2251,13 @@ func (x *Provision_Plan) GetVariableValues() []*VariableValue { return nil } +func (x *Provision_Plan) GetGitAuthProviders() []*GitAuthProvider { + if x != nil { + return x.GitAuthProviders + } + return nil +} + type Provision_Apply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2207,7 +2270,7 @@ type Provision_Apply struct { func (x *Provision_Apply) Reset() { *x = Provision_Apply{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[26] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2220,7 +2283,7 @@ func (x *Provision_Apply) String() string { func (*Provision_Apply) ProtoMessage() {} func (x *Provision_Apply) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[26] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2233,7 +2296,7 @@ func (x *Provision_Apply) ProtoReflect() protoreflect.Message { // Deprecated: Use Provision_Apply.ProtoReflect.Descriptor instead. func (*Provision_Apply) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{17, 3} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{18, 3} } func (x *Provision_Apply) GetConfig() *Provision_Config { @@ -2259,7 +2322,7 @@ type Provision_Cancel struct { func (x *Provision_Cancel) Reset() { *x = Provision_Cancel{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[27] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2272,7 +2335,7 @@ func (x *Provision_Cancel) String() string { func (*Provision_Cancel) ProtoMessage() {} func (x *Provision_Cancel) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[27] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2285,7 +2348,7 @@ func (x *Provision_Cancel) ProtoReflect() protoreflect.Message { // Deprecated: Use Provision_Cancel.ProtoReflect.Descriptor instead. func (*Provision_Cancel) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{17, 4} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{18, 4} } type Provision_Request struct { @@ -2304,7 +2367,7 @@ type Provision_Request struct { func (x *Provision_Request) Reset() { *x = Provision_Request{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[28] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2317,7 +2380,7 @@ func (x *Provision_Request) String() string { func (*Provision_Request) ProtoMessage() {} func (x *Provision_Request) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[28] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2330,7 +2393,7 @@ func (x *Provision_Request) ProtoReflect() protoreflect.Message { // Deprecated: Use Provision_Request.ProtoReflect.Descriptor instead. func (*Provision_Request) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{17, 5} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{18, 5} } func (m *Provision_Request) GetType() isProvision_Request_Type { @@ -2399,7 +2462,7 @@ type Provision_Complete struct { func (x *Provision_Complete) Reset() { *x = Provision_Complete{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[29] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2412,7 +2475,7 @@ func (x *Provision_Complete) String() string { func (*Provision_Complete) ProtoMessage() {} func (x *Provision_Complete) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[29] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2425,7 +2488,7 @@ func (x *Provision_Complete) ProtoReflect() protoreflect.Message { // Deprecated: Use Provision_Complete.ProtoReflect.Descriptor instead. func (*Provision_Complete) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{17, 6} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{18, 6} } func (x *Provision_Complete) GetState() []byte { @@ -2485,7 +2548,7 @@ type Provision_Response struct { func (x *Provision_Response) Reset() { *x = Provision_Response{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[30] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2498,7 +2561,7 @@ func (x *Provision_Response) String() string { func (*Provision_Response) ProtoMessage() {} func (x *Provision_Response) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[30] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2511,7 +2574,7 @@ func (x *Provision_Response) ProtoReflect() protoreflect.Message { // Deprecated: Use Provision_Response.ProtoReflect.Descriptor instead. func (*Provision_Response) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{17, 7} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{18, 7} } func (m *Provision_Response) GetType() isProvision_Response_Type { @@ -2690,230 +2753,239 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{ 0x70, 0x75, 0x74, 0x22, 0x37, 0x0a, 0x14, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x41, 0x75, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x22, 0x8e, 0x05, 0x0a, - 0x05, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x03, 0x65, 0x6e, - 0x76, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x76, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x75, 0x70, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x12, 0x29, 0x0a, 0x10, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x79, - 0x73, 0x74, 0x65, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6f, 0x70, 0x65, 0x72, - 0x61, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x22, 0x0a, 0x0c, 0x61, - 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, - 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x24, 0x0a, - 0x04, 0x61, 0x70, 0x70, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x52, 0x04, 0x61, - 0x70, 0x70, 0x73, 0x12, 0x16, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x21, 0x0a, 0x0b, 0x69, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, - 0x48, 0x00, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x3c, - 0x0a, 0x1a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x0b, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x18, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, - 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x2f, 0x0a, 0x13, - 0x74, 0x72, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x73, 0x68, 0x6f, 0x6f, 0x74, 0x69, 0x6e, 0x67, 0x5f, - 0x75, 0x72, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x74, 0x72, 0x6f, 0x75, 0x62, - 0x6c, 0x65, 0x73, 0x68, 0x6f, 0x6f, 0x74, 0x69, 0x6e, 0x67, 0x55, 0x72, 0x6c, 0x12, 0x1b, 0x0a, - 0x09, 0x6d, 0x6f, 0x74, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x6d, 0x6f, 0x74, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6c, 0x6f, - 0x67, 0x69, 0x6e, 0x5f, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x79, - 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x42, 0x65, 0x66, - 0x6f, 0x72, 0x65, 0x52, 0x65, 0x61, 0x64, 0x79, 0x12, 0x43, 0x0a, 0x1e, 0x73, 0x74, 0x61, 0x72, - 0x74, 0x75, 0x70, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, - 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x1b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x54, - 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x1a, 0x36, 0x0a, - 0x08, 0x45, 0x6e, 0x76, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68, 0x22, 0xb5, 0x02, - 0x0a, 0x03, 0x41, 0x70, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, - 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, - 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, - 0x73, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x09, 0x73, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x3a, 0x0a, 0x0b, 0x68, 0x65, - 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x48, 0x65, - 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x6c, 0x74, - 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x41, 0x0a, 0x0d, 0x73, 0x68, 0x61, 0x72, 0x69, 0x6e, - 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x53, - 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x0c, 0x73, 0x68, 0x61, - 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, 0x78, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x22, 0x59, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, - 0x68, 0x65, 0x63, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, - 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, - 0x61, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, - 0x22, 0xf1, 0x02, 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, 0x12, 0x2a, 0x0a, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x73, 0x12, 0x3a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, - 0x04, 0x68, 0x69, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x68, 0x69, 0x64, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x61, - 0x69, 0x6c, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, - 0x64, 0x61, 0x69, 0x6c, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x1a, 0x69, 0x0a, 0x08, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1c, 0x0a, - 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x69, - 0x73, 0x5f, 0x6e, 0x75, 0x6c, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, - 0x4e, 0x75, 0x6c, 0x6c, 0x22, 0xcb, 0x02, 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, 0xa3, 0x01, 0x0a, 0x08, 0x43, 0x6f, 0x6d, 0x70, - 0x6c, 0x65, 0x74, 0x65, 0x12, 0x4c, 0x0a, 0x12, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, - 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, - 0x65, 0x73, 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, 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, 0xf5, 0x0a, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x1a, 0xd1, 0x02, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, - 0x09, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x53, 0x0a, 0x14, 0x77, 0x6f, - 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x25, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, - 0x21, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, - 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, - 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x64, - 0x12, 0x32, 0x0a, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, - 0x6e, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x45, - 0x6d, 0x61, 0x69, 0x6c, 0x1a, 0x79, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 0x14, 0x0a, 0x05, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x12, 0x3b, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, - 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, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, - 0x9f, 0x02, 0x0a, 0x04, 0x50, 0x6c, 0x61, 0x6e, 0x12, 0x35, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 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, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 0x53, 0x0a, 0x15, 0x72, 0x69, 0x63, 0x68, 0x5f, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, - 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x72, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, - 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x52, 0x0e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x73, 0x1a, 0x52, 0x0a, 0x05, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x12, 0x35, 0x0a, 0x06, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, + 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x22, 0x44, 0x0a, 0x0f, + 0x47, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x22, 0x8e, 0x05, 0x0a, 0x05, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x2d, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x67, 0x65, 0x6e, + 0x74, 0x2e, 0x45, 0x6e, 0x76, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x12, + 0x25, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x79, 0x73, 0x74, 0x65, + 0x6d, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, + 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, + 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x79, 0x12, 0x24, 0x0a, 0x04, 0x61, 0x70, 0x70, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, + 0x41, 0x70, 0x70, 0x52, 0x04, 0x61, 0x70, 0x70, 0x73, 0x12, 0x16, 0x0a, 0x05, 0x74, 0x6f, 0x6b, + 0x65, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x12, 0x21, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x49, 0x64, 0x12, 0x3c, 0x0a, 0x1a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, + 0x64, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x18, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, + 0x64, 0x73, 0x12, 0x2f, 0x0a, 0x13, 0x74, 0x72, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x73, 0x68, 0x6f, + 0x6f, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x12, 0x74, 0x72, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x73, 0x68, 0x6f, 0x6f, 0x74, 0x69, 0x6e, 0x67, + 0x55, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x6f, 0x74, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x65, + 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x6f, 0x74, 0x64, 0x46, 0x69, 0x6c, 0x65, + 0x12, 0x2c, 0x0a, 0x12, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, + 0x5f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6c, 0x6f, + 0x67, 0x69, 0x6e, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x61, 0x64, 0x79, 0x12, 0x43, + 0x0a, 0x1e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, + 0x18, 0x0f, 0x20, 0x01, 0x28, 0x05, 0x52, 0x1b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, + 0x6e, 0x64, 0x73, 0x1a, 0x36, 0x0a, 0x08, 0x45, 0x6e, 0x76, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x61, + 0x75, 0x74, 0x68, 0x22, 0xb5, 0x02, 0x0a, 0x03, 0x41, 0x70, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x73, + 0x6c, 0x75, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x12, + 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, + 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, + 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, + 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x12, 0x3a, 0x0a, 0x0b, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, + 0x0b, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x41, 0x0a, 0x0d, + 0x73, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x72, 0x2e, 0x41, 0x70, 0x70, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, + 0x6c, 0x52, 0x0c, 0x73, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, + 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x22, 0x59, 0x0a, 0x0b, 0x48, + 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, + 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, + 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x68, 0x72, + 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x22, 0xf1, 0x02, 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, 0x12, 0x2a, 0x0a, 0x06, 0x61, + 0x67, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, + 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x3a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x69, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x04, 0x68, 0x69, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x1a, + 0x69, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, + 0x65, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x73, 0x5f, 0x6e, 0x75, 0x6c, 0x6c, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4e, 0x75, 0x6c, 0x6c, 0x22, 0xcb, 0x02, 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, 0xa3, 0x01, + 0x0a, 0x08, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x4c, 0x0a, 0x12, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, + 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, + 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 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, 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, 0xc1, 0x0b, 0x0a, 0x09, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0xd1, 0x02, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x55, 0x72, 0x6c, + 0x12, 0x53, 0x0a, 0x14, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x57, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77, + 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, + 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x77, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x12, 0x77, 0x6f, 0x72, 0x6b, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, + 0x77, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x1a, 0x79, 0x0a, 0x06, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 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, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3b, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 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, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x1a, 0x08, 0x0a, 0x06, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x1a, - 0xb3, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x04, 0x70, - 0x6c, 0x61, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x34, - 0x0a, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x48, 0x00, 0x52, 0x05, 0x61, - 0x70, 0x70, 0x6c, 0x79, 0x12, 0x37, 0x0a, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x06, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x1a, 0xe9, 0x01, 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, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, - 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x33, - 0x0a, 0x09, 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, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, - 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, - 0x74, 0x65, 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, - 0x2c, 0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x67, 0x69, 0x74, - 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, - 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x70, 0x6c, 0x61, - 0x6e, 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, 0x2a, 0x3b, 0x0a, 0x0f, 0x41, - 0x70, 0x70, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, - 0x0a, 0x05, 0x4f, 0x57, 0x4e, 0x45, 0x52, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x55, 0x54, - 0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, - 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x02, 0x2a, 0x37, 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x54, - 0x4f, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4f, 0x59, 0x10, - 0x02, 0x32, 0xa3, 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, 0x50, 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, 0x28, 0x01, 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, + 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0xeb, 0x02, 0x0a, 0x04, 0x50, 0x6c, 0x61, 0x6e, 0x12, 0x35, + 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, + 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, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 0x53, 0x0a, + 0x15, 0x72, 0x69, 0x63, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x72, + 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, + 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, + 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x4a, 0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, + 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x72, 0x2e, 0x47, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x73, 0x1a, 0x52, 0x0a, 0x05, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x12, 0x35, 0x0a, 0x06, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 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, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x1a, 0x08, 0x0a, 0x06, 0x43, 0x61, 0x6e, 0x63, 0x65, + 0x6c, 0x1a, 0xb3, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, + 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, + 0x12, 0x34, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x48, 0x00, 0x52, + 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x12, 0x37, 0x0a, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x43, + 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, + 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x1a, 0xe9, 0x01, 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, 0x14, 0x0a, 0x05, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x12, 0x33, 0x0a, 0x09, 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, 0x09, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, + 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x65, 0x74, 0x65, 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, + 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x67, + 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, + 0x12, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x70, + 0x6c, 0x61, 0x6e, 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, 0x2a, 0x3b, 0x0a, + 0x0f, 0x41, 0x70, 0x70, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, + 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x57, 0x4e, 0x45, 0x52, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, + 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, + 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x02, 0x2a, 0x37, 0x0a, 0x13, 0x57, 0x6f, + 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, + 0x53, 0x54, 0x4f, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4f, + 0x59, 0x10, 0x02, 0x32, 0xa3, 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, 0x50, 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, 0x28, 0x01, 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 ( @@ -2929,7 +3001,7 @@ func file_provisionersdk_proto_provisioner_proto_rawDescGZIP() []byte { } var file_provisionersdk_proto_provisioner_proto_enumTypes = make([]protoimpl.EnumInfo, 6) -var file_provisionersdk_proto_provisioner_proto_msgTypes = make([]protoimpl.MessageInfo, 31) +var file_provisionersdk_proto_provisioner_proto_msgTypes = make([]protoimpl.MessageInfo, 32) var file_provisionersdk_proto_provisioner_proto_goTypes = []interface{}{ (LogLevel)(0), // 0: provisioner.LogLevel (AppSharingLevel)(0), // 1: provisioner.AppSharingLevel @@ -2949,25 +3021,26 @@ var file_provisionersdk_proto_provisioner_proto_goTypes = []interface{}{ (*VariableValue)(nil), // 15: provisioner.VariableValue (*Log)(nil), // 16: provisioner.Log (*InstanceIdentityAuth)(nil), // 17: provisioner.InstanceIdentityAuth - (*Agent)(nil), // 18: provisioner.Agent - (*App)(nil), // 19: provisioner.App - (*Healthcheck)(nil), // 20: provisioner.Healthcheck - (*Resource)(nil), // 21: provisioner.Resource - (*Parse)(nil), // 22: provisioner.Parse - (*Provision)(nil), // 23: provisioner.Provision - nil, // 24: provisioner.Agent.EnvEntry - (*Resource_Metadata)(nil), // 25: provisioner.Resource.Metadata - (*Parse_Request)(nil), // 26: provisioner.Parse.Request - (*Parse_Complete)(nil), // 27: provisioner.Parse.Complete - (*Parse_Response)(nil), // 28: provisioner.Parse.Response - (*Provision_Metadata)(nil), // 29: provisioner.Provision.Metadata - (*Provision_Config)(nil), // 30: provisioner.Provision.Config - (*Provision_Plan)(nil), // 31: provisioner.Provision.Plan - (*Provision_Apply)(nil), // 32: provisioner.Provision.Apply - (*Provision_Cancel)(nil), // 33: provisioner.Provision.Cancel - (*Provision_Request)(nil), // 34: provisioner.Provision.Request - (*Provision_Complete)(nil), // 35: provisioner.Provision.Complete - (*Provision_Response)(nil), // 36: provisioner.Provision.Response + (*GitAuthProvider)(nil), // 18: provisioner.GitAuthProvider + (*Agent)(nil), // 19: provisioner.Agent + (*App)(nil), // 20: provisioner.App + (*Healthcheck)(nil), // 21: provisioner.Healthcheck + (*Resource)(nil), // 22: provisioner.Resource + (*Parse)(nil), // 23: provisioner.Parse + (*Provision)(nil), // 24: provisioner.Provision + nil, // 25: provisioner.Agent.EnvEntry + (*Resource_Metadata)(nil), // 26: provisioner.Resource.Metadata + (*Parse_Request)(nil), // 27: provisioner.Parse.Request + (*Parse_Complete)(nil), // 28: provisioner.Parse.Complete + (*Parse_Response)(nil), // 29: provisioner.Parse.Response + (*Provision_Metadata)(nil), // 30: provisioner.Provision.Metadata + (*Provision_Config)(nil), // 31: provisioner.Provision.Config + (*Provision_Plan)(nil), // 32: provisioner.Provision.Plan + (*Provision_Apply)(nil), // 33: provisioner.Provision.Apply + (*Provision_Cancel)(nil), // 34: provisioner.Provision.Cancel + (*Provision_Request)(nil), // 35: provisioner.Provision.Request + (*Provision_Complete)(nil), // 36: provisioner.Provision.Complete + (*Provision_Response)(nil), // 37: provisioner.Provision.Response } var file_provisionersdk_proto_provisioner_proto_depIdxs = []int32{ 3, // 0: provisioner.ParameterSource.scheme:type_name -> provisioner.ParameterSource.Scheme @@ -2978,39 +3051,40 @@ var file_provisionersdk_proto_provisioner_proto_depIdxs = []int32{ 5, // 5: provisioner.ParameterSchema.validation_type_system:type_name -> provisioner.ParameterSchema.TypeSystem 12, // 6: provisioner.RichParameter.options:type_name -> provisioner.RichParameterOption 0, // 7: provisioner.Log.level:type_name -> provisioner.LogLevel - 24, // 8: provisioner.Agent.env:type_name -> provisioner.Agent.EnvEntry - 19, // 9: provisioner.Agent.apps:type_name -> provisioner.App - 20, // 10: provisioner.App.healthcheck:type_name -> provisioner.Healthcheck + 25, // 8: provisioner.Agent.env:type_name -> provisioner.Agent.EnvEntry + 20, // 9: provisioner.Agent.apps:type_name -> provisioner.App + 21, // 10: provisioner.App.healthcheck:type_name -> provisioner.Healthcheck 1, // 11: provisioner.App.sharing_level:type_name -> provisioner.AppSharingLevel - 18, // 12: provisioner.Resource.agents:type_name -> provisioner.Agent - 25, // 13: provisioner.Resource.metadata:type_name -> provisioner.Resource.Metadata + 19, // 12: provisioner.Resource.agents:type_name -> provisioner.Agent + 26, // 13: provisioner.Resource.metadata:type_name -> provisioner.Resource.Metadata 11, // 14: provisioner.Parse.Complete.template_variables:type_name -> provisioner.TemplateVariable 10, // 15: provisioner.Parse.Complete.parameter_schemas:type_name -> provisioner.ParameterSchema 16, // 16: provisioner.Parse.Response.log:type_name -> provisioner.Log - 27, // 17: provisioner.Parse.Response.complete:type_name -> provisioner.Parse.Complete + 28, // 17: provisioner.Parse.Response.complete:type_name -> provisioner.Parse.Complete 2, // 18: provisioner.Provision.Metadata.workspace_transition:type_name -> provisioner.WorkspaceTransition - 29, // 19: provisioner.Provision.Config.metadata:type_name -> provisioner.Provision.Metadata - 30, // 20: provisioner.Provision.Plan.config:type_name -> provisioner.Provision.Config + 30, // 19: provisioner.Provision.Config.metadata:type_name -> provisioner.Provision.Metadata + 31, // 20: provisioner.Provision.Plan.config:type_name -> provisioner.Provision.Config 9, // 21: provisioner.Provision.Plan.parameter_values:type_name -> provisioner.ParameterValue 14, // 22: provisioner.Provision.Plan.rich_parameter_values:type_name -> provisioner.RichParameterValue 15, // 23: provisioner.Provision.Plan.variable_values:type_name -> provisioner.VariableValue - 30, // 24: provisioner.Provision.Apply.config:type_name -> provisioner.Provision.Config - 31, // 25: provisioner.Provision.Request.plan:type_name -> provisioner.Provision.Plan - 32, // 26: provisioner.Provision.Request.apply:type_name -> provisioner.Provision.Apply - 33, // 27: provisioner.Provision.Request.cancel:type_name -> provisioner.Provision.Cancel - 21, // 28: provisioner.Provision.Complete.resources:type_name -> provisioner.Resource - 13, // 29: provisioner.Provision.Complete.parameters:type_name -> provisioner.RichParameter - 16, // 30: provisioner.Provision.Response.log:type_name -> provisioner.Log - 35, // 31: provisioner.Provision.Response.complete:type_name -> provisioner.Provision.Complete - 26, // 32: provisioner.Provisioner.Parse:input_type -> provisioner.Parse.Request - 34, // 33: provisioner.Provisioner.Provision:input_type -> provisioner.Provision.Request - 28, // 34: provisioner.Provisioner.Parse:output_type -> provisioner.Parse.Response - 36, // 35: provisioner.Provisioner.Provision:output_type -> provisioner.Provision.Response - 34, // [34:36] is the sub-list for method output_type - 32, // [32:34] is the sub-list for method input_type - 32, // [32:32] is the sub-list for extension type_name - 32, // [32:32] is the sub-list for extension extendee - 0, // [0:32] is the sub-list for field type_name + 18, // 24: provisioner.Provision.Plan.git_auth_providers:type_name -> provisioner.GitAuthProvider + 31, // 25: provisioner.Provision.Apply.config:type_name -> provisioner.Provision.Config + 32, // 26: provisioner.Provision.Request.plan:type_name -> provisioner.Provision.Plan + 33, // 27: provisioner.Provision.Request.apply:type_name -> provisioner.Provision.Apply + 34, // 28: provisioner.Provision.Request.cancel:type_name -> provisioner.Provision.Cancel + 22, // 29: provisioner.Provision.Complete.resources:type_name -> provisioner.Resource + 13, // 30: provisioner.Provision.Complete.parameters:type_name -> provisioner.RichParameter + 16, // 31: provisioner.Provision.Response.log:type_name -> provisioner.Log + 36, // 32: provisioner.Provision.Response.complete:type_name -> provisioner.Provision.Complete + 27, // 33: provisioner.Provisioner.Parse:input_type -> provisioner.Parse.Request + 35, // 34: provisioner.Provisioner.Provision:input_type -> provisioner.Provision.Request + 29, // 35: provisioner.Provisioner.Parse:output_type -> provisioner.Parse.Response + 37, // 36: provisioner.Provisioner.Provision:output_type -> provisioner.Provision.Response + 35, // [35:37] is the sub-list for method output_type + 33, // [33:35] is the sub-list for method input_type + 33, // [33:33] is the sub-list for extension type_name + 33, // [33:33] is the sub-list for extension extendee + 0, // [0:33] is the sub-list for field type_name } func init() { file_provisionersdk_proto_provisioner_proto_init() } @@ -3164,7 +3238,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Agent); i { + switch v := v.(*GitAuthProvider); i { case 0: return &v.state case 1: @@ -3176,7 +3250,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*App); i { + switch v := v.(*Agent); i { case 0: return &v.state case 1: @@ -3188,7 +3262,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Healthcheck); i { + switch v := v.(*App); i { case 0: return &v.state case 1: @@ -3200,7 +3274,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Resource); i { + switch v := v.(*Healthcheck); i { case 0: return &v.state case 1: @@ -3212,7 +3286,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Parse); i { + switch v := v.(*Resource); i { case 0: return &v.state case 1: @@ -3224,6 +3298,18 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Parse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_provisionersdk_proto_provisioner_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Provision); i { case 0: return &v.state @@ -3235,7 +3321,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { return nil } } - file_provisionersdk_proto_provisioner_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + file_provisionersdk_proto_provisioner_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Resource_Metadata); i { case 0: return &v.state @@ -3247,7 +3333,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { return nil } } - file_provisionersdk_proto_provisioner_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + file_provisionersdk_proto_provisioner_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Parse_Request); i { case 0: return &v.state @@ -3259,7 +3345,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { return nil } } - file_provisionersdk_proto_provisioner_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + file_provisionersdk_proto_provisioner_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Parse_Complete); i { case 0: return &v.state @@ -3271,7 +3357,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { return nil } } - file_provisionersdk_proto_provisioner_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + file_provisionersdk_proto_provisioner_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Parse_Response); i { case 0: return &v.state @@ -3283,7 +3369,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { return nil } } - file_provisionersdk_proto_provisioner_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + file_provisionersdk_proto_provisioner_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Provision_Metadata); i { case 0: return &v.state @@ -3295,7 +3381,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { return nil } } - file_provisionersdk_proto_provisioner_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + file_provisionersdk_proto_provisioner_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Provision_Config); i { case 0: return &v.state @@ -3307,7 +3393,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { return nil } } - file_provisionersdk_proto_provisioner_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + file_provisionersdk_proto_provisioner_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Provision_Plan); i { case 0: return &v.state @@ -3319,7 +3405,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { return nil } } - file_provisionersdk_proto_provisioner_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + file_provisionersdk_proto_provisioner_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Provision_Apply); i { case 0: return &v.state @@ -3331,7 +3417,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { return nil } } - file_provisionersdk_proto_provisioner_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + file_provisionersdk_proto_provisioner_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Provision_Cancel); i { case 0: return &v.state @@ -3343,7 +3429,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { return nil } } - file_provisionersdk_proto_provisioner_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + file_provisionersdk_proto_provisioner_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Provision_Request); i { case 0: return &v.state @@ -3355,7 +3441,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { return nil } } - file_provisionersdk_proto_provisioner_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + file_provisionersdk_proto_provisioner_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Provision_Complete); i { case 0: return &v.state @@ -3367,7 +3453,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { return nil } } - file_provisionersdk_proto_provisioner_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + file_provisionersdk_proto_provisioner_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Provision_Response); i { case 0: return &v.state @@ -3380,20 +3466,20 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } } - file_provisionersdk_proto_provisioner_proto_msgTypes[12].OneofWrappers = []interface{}{ + file_provisionersdk_proto_provisioner_proto_msgTypes[13].OneofWrappers = []interface{}{ (*Agent_Token)(nil), (*Agent_InstanceId)(nil), } - file_provisionersdk_proto_provisioner_proto_msgTypes[22].OneofWrappers = []interface{}{ + file_provisionersdk_proto_provisioner_proto_msgTypes[23].OneofWrappers = []interface{}{ (*Parse_Response_Log)(nil), (*Parse_Response_Complete)(nil), } - file_provisionersdk_proto_provisioner_proto_msgTypes[28].OneofWrappers = []interface{}{ + file_provisionersdk_proto_provisioner_proto_msgTypes[29].OneofWrappers = []interface{}{ (*Provision_Request_Plan)(nil), (*Provision_Request_Apply)(nil), (*Provision_Request_Cancel)(nil), } - file_provisionersdk_proto_provisioner_proto_msgTypes[30].OneofWrappers = []interface{}{ + file_provisionersdk_proto_provisioner_proto_msgTypes[31].OneofWrappers = []interface{}{ (*Provision_Response_Log)(nil), (*Provision_Response_Complete)(nil), } @@ -3403,7 +3489,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_provisionersdk_proto_provisioner_proto_rawDesc, NumEnums: 6, - NumMessages: 31, + NumMessages: 32, NumExtensions: 0, NumServices: 1, }, diff --git a/provisionersdk/proto/provisioner.proto b/provisionersdk/proto/provisioner.proto index 227ff0d909279..cd20b98365d46 100644 --- a/provisionersdk/proto/provisioner.proto +++ b/provisionersdk/proto/provisioner.proto @@ -118,6 +118,11 @@ message InstanceIdentityAuth { string instance_id = 1; } +message GitAuthProvider { + string id = 1; + string access_token = 2; +} + // Agent represents a running agent on the workspace. message Agent { string id = 1; @@ -235,6 +240,7 @@ message Provision { repeated ParameterValue parameter_values = 2; repeated RichParameterValue rich_parameter_values = 3; repeated VariableValue variable_values = 4; + repeated GitAuthProvider git_auth_providers = 5; } message Apply { From 3d67a0dff1f8455f4e4a9038f60502a37df9d626 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Wed, 22 Feb 2023 23:27:05 +0000 Subject: [PATCH 03/20] Add git auth providers to the API --- .../provisionerdserver/provisionerdserver.go | 28 ++++++++++------- .../provisionerdserver_test.go | 30 ++++++++++++------- coderd/templateversions.go | 19 ++++++------ codersdk/templateversions.go | 19 ++++++------ 4 files changed, 57 insertions(+), 39 deletions(-) diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index 3b734c0ef02d7..f87ea2130f004 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -29,6 +29,7 @@ import ( "github.com/coder/coder/coderd/database/dbauthz" "github.com/coder/coder/coderd/parameter" "github.com/coder/coder/coderd/telemetry" + "github.com/coder/coder/coderd/util/slice" "github.com/coder/coder/codersdk" "github.com/coder/coder/provisioner" "github.com/coder/coder/provisionerd/proto" @@ -42,16 +43,17 @@ var ( ) type Server struct { - AccessURL *url.URL - ID uuid.UUID - Logger slog.Logger - Provisioners []database.ProvisionerType - Tags json.RawMessage - Database database.Store - Pubsub database.Pubsub - Telemetry telemetry.Reporter - QuotaCommitter *atomic.Pointer[proto.QuotaCommitter] - Auditor *atomic.Pointer[audit.Auditor] + AccessURL *url.URL + ID uuid.UUID + Logger slog.Logger + Provisioners []database.ProvisionerType + GitAuthProviders []string + Tags json.RawMessage + Database database.Store + Pubsub database.Pubsub + Telemetry telemetry.Reporter + QuotaCommitter *atomic.Pointer[proto.QuotaCommitter] + Auditor *atomic.Pointer[audit.Auditor] AcquireJobDebounce time.Duration } @@ -810,6 +812,12 @@ func (server *Server) CompleteJob(ctx context.Context, completed *proto.Complete } } + for _, gitAuthProvider := range jobType.TemplateImport.GitAuthProviders { + if !slice.Contains(server.GitAuthProviders, gitAuthProvider) { + return nil, xerrors.Errorf("git auth provider %q is not configured", gitAuthProvider) + } + } + err = server.Database.UpdateTemplateVersionGitAuthProvidersByJobID(ctx, database.UpdateTemplateVersionGitAuthProvidersByJobIDParams{ JobID: jobID, GitAuthProviders: jobType.TemplateImport.GitAuthProviders, diff --git a/coderd/provisionerdserver/provisionerdserver_test.go b/coderd/provisionerdserver/provisionerdserver_test.go index 74f3ce6e65171..708e5474781a2 100644 --- a/coderd/provisionerdserver/provisionerdserver_test.go +++ b/coderd/provisionerdserver/provisionerdserver_test.go @@ -756,18 +756,26 @@ func TestCompleteJob(t *testing.T) { Types: []database.ProvisionerType{database.ProvisionerTypeEcho}, }) require.NoError(t, err) - _, err = srv.CompleteJob(ctx, &proto.CompletedJob{ - JobId: job.ID.String(), - Type: &proto.CompletedJob_TemplateImport_{ - TemplateImport: &proto.CompletedJob_TemplateImport{ - StartResources: []*sdkproto.Resource{{ - Name: "hello", - Type: "aws_instance", - }}, - StopResources: []*sdkproto.Resource{}, + completeJob := func() error { + _, err = srv.CompleteJob(ctx, &proto.CompletedJob{ + JobId: job.ID.String(), + Type: &proto.CompletedJob_TemplateImport_{ + TemplateImport: &proto.CompletedJob_TemplateImport{ + StartResources: []*sdkproto.Resource{{ + Name: "hello", + Type: "aws_instance", + }}, + StopResources: []*sdkproto.Resource{}, + GitAuthProviders: []string{"github"}, + }, }, - }, - }) + }) + return err + } + err = completeJob() + require.ErrorContains(t, err, `git auth provider "github" is not configured`) + srv.GitAuthProviders = []string{"github"} + err = completeJob() require.NoError(t, err) }) t.Run("WorkspaceBuild", func(t *testing.T) { diff --git a/coderd/templateversions.go b/coderd/templateversions.go index 04d7e25070e1f..6b11dd3bdd4f5 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -1461,15 +1461,16 @@ func convertTemplateVersion(version database.TemplateVersion, job codersdk.Provi } return codersdk.TemplateVersion{ - ID: version.ID, - TemplateID: &version.TemplateID.UUID, - OrganizationID: version.OrganizationID, - CreatedAt: version.CreatedAt, - UpdatedAt: version.UpdatedAt, - Name: version.Name, - Job: job, - Readme: version.Readme, - CreatedBy: createdBy, + ID: version.ID, + TemplateID: &version.TemplateID.UUID, + OrganizationID: version.OrganizationID, + CreatedAt: version.CreatedAt, + UpdatedAt: version.UpdatedAt, + GitAuthProviders: version.GitAuthProviders, + Name: version.Name, + Job: job, + Readme: version.Readme, + CreatedBy: createdBy, } } diff --git a/codersdk/templateversions.go b/codersdk/templateversions.go index 18b7a43f37ae4..508f3a494d684 100644 --- a/codersdk/templateversions.go +++ b/codersdk/templateversions.go @@ -13,15 +13,16 @@ import ( // TemplateVersion represents a single version of a template. type TemplateVersion struct { - ID uuid.UUID `json:"id" format:"uuid"` - TemplateID *uuid.UUID `json:"template_id,omitempty" format:"uuid"` - OrganizationID uuid.UUID `json:"organization_id,omitempty" format:"uuid"` - CreatedAt time.Time `json:"created_at" format:"date-time"` - UpdatedAt time.Time `json:"updated_at" format:"date-time"` - Name string `json:"name"` - Job ProvisionerJob `json:"job"` - Readme string `json:"readme"` - CreatedBy User `json:"created_by"` + ID uuid.UUID `json:"id" format:"uuid"` + TemplateID *uuid.UUID `json:"template_id,omitempty" format:"uuid"` + OrganizationID uuid.UUID `json:"organization_id,omitempty" format:"uuid"` + CreatedAt time.Time `json:"created_at" format:"date-time"` + UpdatedAt time.Time `json:"updated_at" format:"date-time"` + GitAuthProviders []string `json:"git_auth_providers"` + Name string `json:"name"` + Job ProvisionerJob `json:"job"` + Readme string `json:"readme"` + CreatedBy User `json:"created_by"` } type ValidationMonotonicOrder string From 604cf5b83e1380aaff6d4bb4ce5c839a0f26c07d Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 23 Feb 2023 00:40:09 +0000 Subject: [PATCH 04/20] Add gitauth endpoint to query authenticated state --- coderd/coderd.go | 7 ++ coderd/database/dbauthz/querier.go | 4 +- coderd/database/dbauthz/querier_test.go | 2 +- coderd/database/dbfake/databasefake.go | 8 +- coderd/database/querier.go | 2 +- coderd/database/queries.sql.go | 20 +++- coderd/database/queries/gitauth.sql | 4 +- .../provisionerdserver/provisionerdserver.go | 9 +- .../provisionerdserver_test.go | 14 ++- coderd/templateversions.go | 113 ++++++++++++++++-- coderd/templateversions_test.go | 19 +++ coderd/workspaceagents.go | 57 +++++---- codersdk/templateversions.go | 40 +++++-- site/src/api/typesGenerated.ts | 8 ++ 14 files changed, 241 insertions(+), 66 deletions(-) diff --git a/coderd/coderd.go b/coderd/coderd.go index 34f4f61f5fdf3..876023b0dc0af 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -479,6 +479,7 @@ func New(options *Options) *API { r.Get("/schema", api.templateVersionSchema) r.Get("/parameters", api.templateVersionParameters) r.Get("/rich-parameters", api.templateVersionRichParameters) + r.Get("/gitauth", api.templateVersionGitAuth) r.Get("/variables", api.templateVersionVariables) r.Get("/resources", api.templateVersionResources) r.Get("/logs", api.templateVersionLogs) @@ -795,12 +796,18 @@ func (api *API) CreateInMemoryProvisionerDaemon(ctx context.Context, debounce ti } mux := drpcmux.New() + + gitAuthProviders := make([]string, 0, len(api.GitAuthConfigs)) + for _, cfg := range api.GitAuthConfigs { + gitAuthProviders = append(gitAuthProviders, cfg.ID) + } err = proto.DRPCRegisterProvisionerDaemon(mux, &provisionerdserver.Server{ AccessURL: api.AccessURL, ID: daemon.ID, Database: api.Database, Pubsub: api.Pubsub, Provisioners: daemon.Provisioners, + GitAuthProviders: gitAuthProviders, Telemetry: api.Telemetry, Tags: tags, QuotaCommitter: &api.QuotaCommitter, diff --git a/coderd/database/dbauthz/querier.go b/coderd/database/dbauthz/querier.go index a71a79614e6d3..1981951f6e717 100644 --- a/coderd/database/dbauthz/querier.go +++ b/coderd/database/dbauthz/querier.go @@ -1121,11 +1121,11 @@ func (q *querier) InsertGitAuthLink(ctx context.Context, arg database.InsertGitA return insert(q.log, q.auth, rbac.ResourceUserData.WithOwner(arg.UserID.String()).WithID(arg.UserID), q.db.InsertGitAuthLink)(ctx, arg) } -func (q *querier) UpdateGitAuthLink(ctx context.Context, arg database.UpdateGitAuthLinkParams) error { +func (q *querier) UpdateGitAuthLink(ctx context.Context, arg database.UpdateGitAuthLinkParams) (database.GitAuthLink, error) { fetch := func(ctx context.Context, arg database.UpdateGitAuthLinkParams) (database.GitAuthLink, error) { return q.db.GetGitAuthLink(ctx, database.GetGitAuthLinkParams{UserID: arg.UserID, ProviderID: arg.ProviderID}) } - return update(q.log, q.auth, fetch, q.db.UpdateGitAuthLink)(ctx, arg) + return updateWithReturn(q.log, q.auth, fetch, q.db.UpdateGitAuthLink)(ctx, arg) } func (q *querier) UpdateUserLink(ctx context.Context, arg database.UpdateUserLinkParams) (database.UserLink, error) { diff --git a/coderd/database/dbauthz/querier_test.go b/coderd/database/dbauthz/querier_test.go index 72b92c924f62d..88b26450590d7 100644 --- a/coderd/database/dbauthz/querier_test.go +++ b/coderd/database/dbauthz/querier_test.go @@ -883,7 +883,7 @@ func (s *MethodTestSuite) TestUser() { check.Args(database.UpdateGitAuthLinkParams{ ProviderID: link.ProviderID, UserID: link.UserID, - }).Asserts(link, rbac.ActionUpdate).Returns() + }).Asserts(link, rbac.ActionUpdate).Returns(link) })) s.Run("UpdateUserLink", s.Subtest(func(db database.Store, check *expects) { link := dbgen.UserLink(s.T(), db, database.UserLink{}) diff --git a/coderd/database/dbfake/databasefake.go b/coderd/database/dbfake/databasefake.go index 966b8b737ec8e..ae05fcece11d2 100644 --- a/coderd/database/dbfake/databasefake.go +++ b/coderd/database/dbfake/databasefake.go @@ -4294,9 +4294,9 @@ func (q *fakeQuerier) InsertGitAuthLink(_ context.Context, arg database.InsertGi return gitAuthLink, nil } -func (q *fakeQuerier) UpdateGitAuthLink(_ context.Context, arg database.UpdateGitAuthLinkParams) error { +func (q *fakeQuerier) UpdateGitAuthLink(_ context.Context, arg database.UpdateGitAuthLinkParams) (database.GitAuthLink, error) { if err := validateDatabaseType(arg); err != nil { - return err + return database.GitAuthLink{}, err } q.mutex.Lock() @@ -4313,8 +4313,10 @@ func (q *fakeQuerier) UpdateGitAuthLink(_ context.Context, arg database.UpdateGi gitAuthLink.OAuthRefreshToken = arg.OAuthRefreshToken gitAuthLink.OAuthExpiry = arg.OAuthExpiry q.gitAuthLinks[index] = gitAuthLink + + return gitAuthLink, nil } - return nil + return database.GitAuthLink{}, sql.ErrNoRows } func (q *fakeQuerier) GetQuotaAllowanceForUser(_ context.Context, userID uuid.UUID) (int64, error) { diff --git a/coderd/database/querier.go b/coderd/database/querier.go index d9a614078626c..c7100690ecad1 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -175,7 +175,7 @@ type sqlcQuerier interface { ParameterValue(ctx context.Context, id uuid.UUID) (ParameterValue, error) ParameterValues(ctx context.Context, arg ParameterValuesParams) ([]ParameterValue, error) UpdateAPIKeyByID(ctx context.Context, arg UpdateAPIKeyByIDParams) error - UpdateGitAuthLink(ctx context.Context, arg UpdateGitAuthLinkParams) error + UpdateGitAuthLink(ctx context.Context, arg UpdateGitAuthLinkParams) (GitAuthLink, error) UpdateGitSSHKey(ctx context.Context, arg UpdateGitSSHKeyParams) (GitSSHKey, error) UpdateGroupByID(ctx context.Context, arg UpdateGroupByIDParams) (Group, error) UpdateMemberRoles(ctx context.Context, arg UpdateMemberRolesParams) (OrganizationMember, error) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 8ad1f4378e528..8a8e618022437 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -836,13 +836,13 @@ func (q *sqlQuerier) InsertGitAuthLink(ctx context.Context, arg InsertGitAuthLin return i, err } -const updateGitAuthLink = `-- name: UpdateGitAuthLink :exec +const updateGitAuthLink = `-- name: UpdateGitAuthLink :one UPDATE git_auth_links SET updated_at = $3, oauth_access_token = $4, oauth_refresh_token = $5, oauth_expiry = $6 -WHERE provider_id = $1 AND user_id = $2 +WHERE provider_id = $1 AND user_id = $2 RETURNING provider_id, user_id, created_at, updated_at, oauth_access_token, oauth_refresh_token, oauth_expiry ` type UpdateGitAuthLinkParams struct { @@ -854,8 +854,8 @@ type UpdateGitAuthLinkParams struct { OAuthExpiry time.Time `db:"oauth_expiry" json:"oauth_expiry"` } -func (q *sqlQuerier) UpdateGitAuthLink(ctx context.Context, arg UpdateGitAuthLinkParams) error { - _, err := q.db.ExecContext(ctx, updateGitAuthLink, +func (q *sqlQuerier) UpdateGitAuthLink(ctx context.Context, arg UpdateGitAuthLinkParams) (GitAuthLink, error) { + row := q.db.QueryRowContext(ctx, updateGitAuthLink, arg.ProviderID, arg.UserID, arg.UpdatedAt, @@ -863,7 +863,17 @@ func (q *sqlQuerier) UpdateGitAuthLink(ctx context.Context, arg UpdateGitAuthLin arg.OAuthRefreshToken, arg.OAuthExpiry, ) - return err + var i GitAuthLink + err := row.Scan( + &i.ProviderID, + &i.UserID, + &i.CreatedAt, + &i.UpdatedAt, + &i.OAuthAccessToken, + &i.OAuthRefreshToken, + &i.OAuthExpiry, + ) + return i, err } const deleteGitSSHKey = `-- name: DeleteGitSSHKey :exec diff --git a/coderd/database/queries/gitauth.sql b/coderd/database/queries/gitauth.sql index 52a6fa76c48f4..a35de98a08908 100644 --- a/coderd/database/queries/gitauth.sql +++ b/coderd/database/queries/gitauth.sql @@ -20,10 +20,10 @@ INSERT INTO git_auth_links ( $7 ) RETURNING *; --- name: UpdateGitAuthLink :exec +-- name: UpdateGitAuthLink :one UPDATE git_auth_links SET updated_at = $3, oauth_access_token = $4, oauth_refresh_token = $5, oauth_expiry = $6 -WHERE provider_id = $1 AND user_id = $2; +WHERE provider_id = $1 AND user_id = $2 RETURNING *; diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index f87ea2130f004..6d67153ac06da 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -812,9 +812,15 @@ func (server *Server) CompleteJob(ctx context.Context, completed *proto.Complete } } + var completedError sql.NullString + for _, gitAuthProvider := range jobType.TemplateImport.GitAuthProviders { if !slice.Contains(server.GitAuthProviders, gitAuthProvider) { - return nil, xerrors.Errorf("git auth provider %q is not configured", gitAuthProvider) + completedError = sql.NullString{ + String: fmt.Sprintf("git auth provider %q is not configured", gitAuthProvider), + Valid: true, + } + break } } @@ -834,6 +840,7 @@ func (server *Server) CompleteJob(ctx context.Context, completed *proto.Complete Time: database.Now(), Valid: true, }, + Error: completedError, }) if err != nil { return nil, xerrors.Errorf("update provisioner job: %w", err) diff --git a/coderd/provisionerdserver/provisionerdserver_test.go b/coderd/provisionerdserver/provisionerdserver_test.go index 708e5474781a2..8137b926ff384 100644 --- a/coderd/provisionerdserver/provisionerdserver_test.go +++ b/coderd/provisionerdserver/provisionerdserver_test.go @@ -756,7 +756,7 @@ func TestCompleteJob(t *testing.T) { Types: []database.ProvisionerType{database.ProvisionerTypeEcho}, }) require.NoError(t, err) - completeJob := func() error { + completeJob := func() { _, err = srv.CompleteJob(ctx, &proto.CompletedJob{ JobId: job.ID.String(), Type: &proto.CompletedJob_TemplateImport_{ @@ -770,13 +770,17 @@ func TestCompleteJob(t *testing.T) { }, }, }) - return err + require.NoError(t, err) } - err = completeJob() - require.ErrorContains(t, err, `git auth provider "github" is not configured`) + completeJob() + job, err = srv.Database.GetProvisionerJobByID(ctx, job.ID) + require.NoError(t, err) + require.Contains(t, job.Error.String, `git auth provider "github" is not configured`) srv.GitAuthProviders = []string{"github"} - err = completeJob() + completeJob() + job, err = srv.Database.GetProvisionerJobByID(ctx, job.ID) require.NoError(t, err) + require.False(t, job.Error.Valid) }) t.Run("WorkspaceBuild", func(t *testing.T) { t.Parallel() diff --git a/coderd/templateversions.go b/coderd/templateversions.go index 6b11dd3bdd4f5..2604c3554c375 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -19,6 +19,7 @@ import ( "github.com/coder/coder/coderd/audit" "github.com/coder/coder/coderd/database" + "github.com/coder/coder/coderd/gitauth" "github.com/coder/coder/coderd/httpapi" "github.com/coder/coder/coderd/httpmw" "github.com/coder/coder/coderd/parameter" @@ -243,6 +244,99 @@ func (api *API) templateVersionRichParameters(rw http.ResponseWriter, r *http.Re httpapi.Write(ctx, rw, http.StatusOK, templateVersionParameters) } +// @Summary Get git auth by template version +// @ID get-git-auth-by-template-version +// @Security CoderSessionToken +// @Produce json +// @Tags Templates +// @Param templateversion path string true "Template version ID" format(uuid) +// @Success 200 {array} codersdk.GitAuth +// @Router /templateversions/{templateversion}/gitauth [get] +func (api *API) templateVersionGitAuth(rw http.ResponseWriter, r *http.Request) { + ctx := r.Context() + var ( + apiKey = httpmw.APIKey(r) + templateVersion = httpmw.TemplateVersionParam(r) + template = httpmw.TemplateParam(r) + ) + + if !api.Authorize(r, rbac.ActionRead, templateVersion.RBACObject(template)) { + httpapi.ResourceNotFound(rw) + return + } + + rawProviders := templateVersion.GitAuthProviders + providers := make([]codersdk.GitAuth, 0) + for _, rawProvider := range rawProviders { + var config *gitauth.Config + for _, provider := range api.GitAuthConfigs { + if provider.ID == rawProvider { + config = provider + break + } + } + if config == nil { + httpapi.Write(ctx, rw, http.StatusNotFound, codersdk.Response{ + Message: fmt.Sprintf("The template version references a Git auth provider %q that no longer exists.", rawProvider), + Detail: "You'll need to update the template version to use a different provider.", + }) + return + } + + // This is the URL that will redirect the user with a state token. + redirectURL, err := api.AccessURL.Parse(fmt.Sprintf("/gitauth/%s", config.ID)) + if err != nil { + httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ + Message: "Failed to parse access URL.", + Detail: err.Error(), + }) + return + } + + provider := codersdk.GitAuth{ + ID: config.ID, + Type: config.Type, + AuthenticateURL: redirectURL.String(), + } + + authLink, err := api.Database.GetGitAuthLink(ctx, database.GetGitAuthLinkParams{ + ProviderID: config.ID, + UserID: apiKey.UserID, + }) + // If there isn't an auth link, then the user just isn't authenticated. + if errors.Is(err, sql.ErrNoRows) { + providers = append(providers, provider) + continue + } + if err != nil { + httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ + Message: "Internal error fetching Git auth link.", + Detail: err.Error(), + }) + return + } + + _, updated, err := refreshGitToken(ctx, api.Database, apiKey.UserID, config, authLink) + if err != nil { + httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ + Message: "Failed to refresh git auth token.", + Detail: err.Error(), + }) + return + } + // If the token couldn't be validated, then we assume the user isn't + // authenticated and return early. + if !updated { + providers = append(providers, provider) + continue + } + provider.Authenticated = true + providers = append(providers, provider) + } + + httpapi.Write(ctx, rw, http.StatusOK, providers) +} + // @Summary Get template variables by template version // @ID get-template-variables-by-template-version // @Security CoderSessionToken @@ -1461,16 +1555,15 @@ func convertTemplateVersion(version database.TemplateVersion, job codersdk.Provi } return codersdk.TemplateVersion{ - ID: version.ID, - TemplateID: &version.TemplateID.UUID, - OrganizationID: version.OrganizationID, - CreatedAt: version.CreatedAt, - UpdatedAt: version.UpdatedAt, - GitAuthProviders: version.GitAuthProviders, - Name: version.Name, - Job: job, - Readme: version.Readme, - CreatedBy: createdBy, + ID: version.ID, + TemplateID: &version.TemplateID.UUID, + OrganizationID: version.OrganizationID, + CreatedAt: version.CreatedAt, + UpdatedAt: version.UpdatedAt, + Name: version.Name, + Job: job, + Readme: version.Readme, + CreatedBy: createdBy, } } diff --git a/coderd/templateversions_test.go b/coderd/templateversions_test.go index 9e7a7d46bda9f..4648f8fae4706 100644 --- a/coderd/templateversions_test.go +++ b/coderd/templateversions_test.go @@ -434,6 +434,25 @@ func TestTemplateVersionParameters(t *testing.T) { }) } +func TestTemplateVersionsGitAuth(t *testing.T) { + t.Parallel() + t.Run("WithoutAny", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, nil) + user := coderdtest.CreateFirstUser(t, client) + version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + _, err := client.TemplateVersionGitAuth(ctx, version.ID) + require.NoError(t, err) + }) + // t.Run("", func(t *testing.T) { + + // }) +} + func TestTemplateVersionResources(t *testing.T) { t.Parallel() t.Run("ListRunning", func(t *testing.T) { diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index 51769a66a556b..6348db47fdd48 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -1310,14 +1310,29 @@ func (api *API) workspaceAgentsGitAuth(rw http.ResponseWriter, r *http.Request) return } - // If the token is expired and refresh is disabled, we prompt - // the user to authenticate again. - if gitAuthConfig.NoRefresh && gitAuthLink.OAuthExpiry.Before(database.Now()) { + gitAuthLink, updated, err := refreshGitToken(ctx, api.Database, workspace.OwnerID, gitAuthConfig, gitAuthLink) + if err != nil { + httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ + Message: "Failed to refresh git auth token.", + Detail: err.Error(), + }) + return + } + if !updated { httpapi.Write(ctx, rw, http.StatusOK, agentsdk.GitAuthResponse{ URL: redirectURL.String(), }) return } + httpapi.Write(ctx, rw, http.StatusOK, formatGitAuthAccessToken(gitAuthConfig.Type, gitAuthLink.OAuthAccessToken)) +} + +func refreshGitToken(ctx context.Context, db database.Store, owner uuid.UUID, gitAuthConfig *gitauth.Config, gitAuthLink database.GitAuthLink) (database.GitAuthLink, bool, error) { + // If the token is expired and refresh is disabled, we prompt + // the user to authenticate again. + if gitAuthConfig.NoRefresh && gitAuthLink.OAuthExpiry.Before(database.Now()) { + return gitAuthLink, false, nil + } token, err := gitAuthConfig.TokenSource(ctx, &oauth2.Token{ AccessToken: gitAuthLink.OAuthAccessToken, @@ -1325,49 +1340,35 @@ func (api *API) workspaceAgentsGitAuth(rw http.ResponseWriter, r *http.Request) Expiry: gitAuthLink.OAuthExpiry, }).Token() if err != nil { - httpapi.Write(ctx, rw, http.StatusOK, agentsdk.GitAuthResponse{ - URL: redirectURL.String(), - }) - return + return gitAuthLink, false, nil } if gitAuthConfig.ValidateURL != "" { valid, err := validateGitToken(ctx, gitAuthConfig.ValidateURL, token.AccessToken) if err != nil { - httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Failed to validate Git authentication token.", - Detail: err.Error(), - }) - return + return gitAuthLink, false, xerrors.Errorf("validate git auth token: %w", err) } if !valid { // The token is no longer valid! - httpapi.Write(ctx, rw, http.StatusOK, agentsdk.GitAuthResponse{ - URL: redirectURL.String(), - }) - return + return gitAuthLink, false, nil } } if token.AccessToken != gitAuthLink.OAuthAccessToken { // Update it - err = api.Database.UpdateGitAuthLink(ctx, database.UpdateGitAuthLinkParams{ + gitAuthLink, err = db.UpdateGitAuthLink(ctx, database.UpdateGitAuthLinkParams{ ProviderID: gitAuthConfig.ID, - UserID: workspace.OwnerID, + UserID: owner, UpdatedAt: database.Now(), OAuthAccessToken: token.AccessToken, OAuthRefreshToken: token.RefreshToken, OAuthExpiry: token.Expiry, }) if err != nil { - httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Failed to update git auth link.", - Detail: err.Error(), - }) - return + return gitAuthLink, false, xerrors.Errorf("update git auth link: %w", err) } } - httpapi.Write(ctx, rw, http.StatusOK, formatGitAuthAccessToken(gitAuthConfig.Type, token.AccessToken)) + return gitAuthLink, true, nil } // validateGitToken ensures the git token provided is valid @@ -1456,7 +1457,7 @@ func (api *API) gitAuthCallback(gitAuthConfig *gitauth.Config) http.HandlerFunc return } } else { - err = api.Database.UpdateGitAuthLink(ctx, database.UpdateGitAuthLinkParams{ + _, err = api.Database.UpdateGitAuthLink(ctx, database.UpdateGitAuthLinkParams{ ProviderID: gitAuthConfig.ID, UserID: apiKey.UserID, UpdatedAt: database.Now(), @@ -1482,8 +1483,12 @@ func (api *API) gitAuthCallback(gitAuthConfig *gitauth.Config) http.HandlerFunc return } + redirect := state.Redirect + if redirect == "" { + redirect = "/gitauth" + } // This is a nicely rendered screen on the frontend - http.Redirect(rw, r, "/gitauth", http.StatusTemporaryRedirect) + http.Redirect(rw, r, redirect, http.StatusTemporaryRedirect) } } diff --git a/codersdk/templateversions.go b/codersdk/templateversions.go index 508f3a494d684..a761a3a480f3e 100644 --- a/codersdk/templateversions.go +++ b/codersdk/templateversions.go @@ -11,18 +11,24 @@ import ( "github.com/google/uuid" ) +type GitAuth struct { + ID string `json:"id"` + Type GitProvider `json:"type"` + AuthenticateURL string `json:"authenticate_url"` + Authenticated bool `json:"authenticated"` +} + // TemplateVersion represents a single version of a template. type TemplateVersion struct { - ID uuid.UUID `json:"id" format:"uuid"` - TemplateID *uuid.UUID `json:"template_id,omitempty" format:"uuid"` - OrganizationID uuid.UUID `json:"organization_id,omitempty" format:"uuid"` - CreatedAt time.Time `json:"created_at" format:"date-time"` - UpdatedAt time.Time `json:"updated_at" format:"date-time"` - GitAuthProviders []string `json:"git_auth_providers"` - Name string `json:"name"` - Job ProvisionerJob `json:"job"` - Readme string `json:"readme"` - CreatedBy User `json:"created_by"` + ID uuid.UUID `json:"id" format:"uuid"` + TemplateID *uuid.UUID `json:"template_id,omitempty" format:"uuid"` + OrganizationID uuid.UUID `json:"organization_id,omitempty" format:"uuid"` + CreatedAt time.Time `json:"created_at" format:"date-time"` + UpdatedAt time.Time `json:"updated_at" format:"date-time"` + Name string `json:"name"` + Job ProvisionerJob `json:"job"` + Readme string `json:"readme"` + CreatedBy User `json:"created_by"` } type ValidationMonotonicOrder string @@ -109,6 +115,20 @@ func (c *Client) TemplateVersionRichParameters(ctx context.Context, version uuid return params, json.NewDecoder(res.Body).Decode(¶ms) } +// TemplateVersionGitAuth returns git authentication for the requested template version. +func (c *Client) TemplateVersionGitAuth(ctx context.Context, version uuid.UUID) ([]GitAuth, error) { + res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/templateversions/%s/gitauth", version), nil) + if err != nil { + return nil, err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return nil, ReadBodyAsError(res) + } + var gitAuth []GitAuth + return gitAuth, json.NewDecoder(res.Body).Decode(&gitAuth) +} + // TemplateVersionSchema returns schemas for a template version by ID. func (c *Client) TemplateVersionSchema(ctx context.Context, version uuid.UUID) ([]ParameterSchema, error) { res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/templateversions/%s/schema", version), nil) diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index c7159fb50a8f8..dd911905a64d6 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -387,6 +387,14 @@ export interface GetUsersResponse { readonly count: number } +// From codersdk/templateversions.go +export interface GitAuth { + readonly id: string + readonly type: GitProvider + readonly authenticate_url: string + readonly authenticated: boolean +} + // From codersdk/deployment.go export interface GitAuthConfig { readonly id: string From 85db3c2ad1ba6e1b875d2a03d45fb76f587b858b Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 23 Feb 2023 16:29:48 +0000 Subject: [PATCH 05/20] Add endpoint to query git state --- coderd/apidoc/docs.go | 55 +++++++++++++ coderd/apidoc/swagger.json | 51 ++++++++++++ coderd/gitauth/config.go | 8 +- coderd/gitauth/config_test.go | 14 ++-- coderd/templateversions.go | 6 +- codersdk/templateversions.go | 18 ++--- codersdk/workspaceagents.go | 9 +-- docs/api/schemas.md | 20 +++++ docs/api/templates.md | 54 +++++++++++++ site/src/api/api.ts | 9 +++ site/src/api/typesGenerated.ts | 25 ++++-- .../components/GitAuth/GitAuth.stories.tsx | 57 ++++++++++++++ site/src/components/GitAuth/GitAuth.tsx | 77 +++++++++++++++++++ site/src/components/Icons/AzureDevOpsIcon.tsx | 25 ++++++ site/src/components/Icons/BitbucketIcon.tsx | 32 ++++++++ site/src/components/Icons/GitlabIcon.tsx | 29 +++++++ .../CreateWorkspacePage.tsx | 4 + .../CreateWorkspacePageView.tsx | 33 ++++++++ .../createWorkspaceXService.ts | 43 ++++++++++- 19 files changed, 532 insertions(+), 37 deletions(-) create mode 100644 site/src/components/GitAuth/GitAuth.stories.tsx create mode 100644 site/src/components/GitAuth/GitAuth.tsx create mode 100644 site/src/components/Icons/AzureDevOpsIcon.tsx create mode 100644 site/src/components/Icons/BitbucketIcon.tsx create mode 100644 site/src/components/Icons/GitlabIcon.tsx diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index d4e1236849534..d0e8e05668b3e 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -2485,6 +2485,44 @@ const docTemplate = `{ } } }, + "/templateversions/{templateversion}/gitauth": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Templates" + ], + "summary": "Get git auth by template version", + "operationId": "get-git-auth-by-template-version", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template version ID", + "name": "templateversion", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.TemplateVersionGitAuth" + } + } + } + } + } + }, "/templateversions/{templateversion}/logs": { "get": { "security": [ @@ -7648,6 +7686,23 @@ const docTemplate = `{ } } }, + "codersdk.TemplateVersionGitAuth": { + "type": "object", + "properties": { + "authenticate_url": { + "type": "string" + }, + "authenticated": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, "codersdk.TemplateVersionParameter": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 894a2d0b6cafd..82f6448b68258 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -2183,6 +2183,40 @@ } } }, + "/templateversions/{templateversion}/gitauth": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Templates"], + "summary": "Get git auth by template version", + "operationId": "get-git-auth-by-template-version", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template version ID", + "name": "templateversion", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.TemplateVersionGitAuth" + } + } + } + } + } + }, "/templateversions/{templateversion}/logs": { "get": { "security": [ @@ -6877,6 +6911,23 @@ } } }, + "codersdk.TemplateVersionGitAuth": { + "type": "object", + "properties": { + "authenticate_url": { + "type": "string" + }, + "authenticated": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, "codersdk.TemplateVersionParameter": { "type": "object", "properties": { diff --git a/coderd/gitauth/config.go b/coderd/gitauth/config.go index 91fe41ee51db5..2ed1d5321a16c 100644 --- a/coderd/gitauth/config.go +++ b/coderd/gitauth/config.go @@ -42,13 +42,13 @@ func ConvertConfig(entries []codersdk.GitAuthConfig, accessURL *url.URL) ([]*Con for _, entry := range entries { var typ codersdk.GitProvider switch entry.Type { - case codersdk.GitProviderAzureDevops: + case string(codersdk.GitProviderAzureDevops): typ = codersdk.GitProviderAzureDevops - case codersdk.GitProviderBitBucket: + case string(codersdk.GitProviderBitBucket): typ = codersdk.GitProviderBitBucket - case codersdk.GitProviderGitHub: + case string(codersdk.GitProviderGitHub): typ = codersdk.GitProviderGitHub - case codersdk.GitProviderGitLab: + case string(codersdk.GitProviderGitLab): typ = codersdk.GitProviderGitLab default: return nil, xerrors.Errorf("unknown git provider type: %q", entry.Type) diff --git a/coderd/gitauth/config_test.go b/coderd/gitauth/config_test.go index 0ae1d579d4896..db41fb64a0b24 100644 --- a/coderd/gitauth/config_test.go +++ b/coderd/gitauth/config_test.go @@ -26,37 +26,37 @@ func TestConvertYAML(t *testing.T) { }, { Name: "InvalidID", Input: []codersdk.GitAuthConfig{{ - Type: codersdk.GitProviderGitHub, + Type: string(codersdk.GitProviderGitHub), ID: "$hi$", }}, Error: "doesn't have a valid id", }, { Name: "NoClientID", Input: []codersdk.GitAuthConfig{{ - Type: codersdk.GitProviderGitHub, + Type: string(codersdk.GitProviderGitHub), }}, Error: "client_id must be provided", }, { Name: "NoClientSecret", Input: []codersdk.GitAuthConfig{{ - Type: codersdk.GitProviderGitHub, + Type: string(codersdk.GitProviderGitHub), ClientID: "example", }}, Error: "client_secret must be provided", }, { Name: "DuplicateType", Input: []codersdk.GitAuthConfig{{ - Type: codersdk.GitProviderGitHub, + Type: string(codersdk.GitProviderGitHub), ClientID: "example", ClientSecret: "example", }, { - Type: codersdk.GitProviderGitHub, + Type: string(codersdk.GitProviderGitHub), }}, Error: "multiple github git auth providers provided", }, { Name: "InvalidRegex", Input: []codersdk.GitAuthConfig{{ - Type: codersdk.GitProviderGitHub, + Type: string(codersdk.GitProviderGitHub), ClientID: "example", ClientSecret: "example", Regex: `\K`, @@ -79,7 +79,7 @@ func TestConvertYAML(t *testing.T) { t.Run("CustomScopesAndEndpoint", func(t *testing.T) { t.Parallel() config, err := gitauth.ConvertConfig([]codersdk.GitAuthConfig{{ - Type: codersdk.GitProviderGitLab, + Type: string(codersdk.GitProviderGitLab), ClientID: "id", ClientSecret: "secret", AuthURL: "https://auth.com", diff --git a/coderd/templateversions.go b/coderd/templateversions.go index 2604c3554c375..d7455dd40005b 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -250,7 +250,7 @@ func (api *API) templateVersionRichParameters(rw http.ResponseWriter, r *http.Re // @Produce json // @Tags Templates // @Param templateversion path string true "Template version ID" format(uuid) -// @Success 200 {array} codersdk.GitAuth +// @Success 200 {array} codersdk.TemplateVersionGitAuth // @Router /templateversions/{templateversion}/gitauth [get] func (api *API) templateVersionGitAuth(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -266,7 +266,7 @@ func (api *API) templateVersionGitAuth(rw http.ResponseWriter, r *http.Request) } rawProviders := templateVersion.GitAuthProviders - providers := make([]codersdk.GitAuth, 0) + providers := make([]codersdk.TemplateVersionGitAuth, 0) for _, rawProvider := range rawProviders { var config *gitauth.Config for _, provider := range api.GitAuthConfigs { @@ -293,7 +293,7 @@ func (api *API) templateVersionGitAuth(rw http.ResponseWriter, r *http.Request) return } - provider := codersdk.GitAuth{ + provider := codersdk.TemplateVersionGitAuth{ ID: config.ID, Type: config.Type, AuthenticateURL: redirectURL.String(), diff --git a/codersdk/templateversions.go b/codersdk/templateversions.go index a761a3a480f3e..a915f7e218615 100644 --- a/codersdk/templateversions.go +++ b/codersdk/templateversions.go @@ -11,13 +11,6 @@ import ( "github.com/google/uuid" ) -type GitAuth struct { - ID string `json:"id"` - Type GitProvider `json:"type"` - AuthenticateURL string `json:"authenticate_url"` - Authenticated bool `json:"authenticated"` -} - // TemplateVersion represents a single version of a template. type TemplateVersion struct { ID uuid.UUID `json:"id" format:"uuid"` @@ -31,6 +24,13 @@ type TemplateVersion struct { CreatedBy User `json:"created_by"` } +type TemplateVersionGitAuth struct { + ID string `json:"id"` + Type GitProvider `json:"type"` + AuthenticateURL string `json:"authenticate_url"` + Authenticated bool `json:"authenticated"` +} + type ValidationMonotonicOrder string const ( @@ -116,7 +116,7 @@ func (c *Client) TemplateVersionRichParameters(ctx context.Context, version uuid } // TemplateVersionGitAuth returns git authentication for the requested template version. -func (c *Client) TemplateVersionGitAuth(ctx context.Context, version uuid.UUID) ([]GitAuth, error) { +func (c *Client) TemplateVersionGitAuth(ctx context.Context, version uuid.UUID) ([]TemplateVersionGitAuth, error) { res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/templateversions/%s/gitauth", version), nil) if err != nil { return nil, err @@ -125,7 +125,7 @@ func (c *Client) TemplateVersionGitAuth(ctx context.Context, version uuid.UUID) if res.StatusCode != http.StatusOK { return nil, ReadBodyAsError(res) } - var gitAuth []GitAuth + var gitAuth []TemplateVersionGitAuth return gitAuth, json.NewDecoder(res.Body).Decode(&gitAuth) } diff --git a/codersdk/workspaceagents.go b/codersdk/workspaceagents.go index 9fbb9eb9200c6..912645f7b1c43 100644 --- a/codersdk/workspaceagents.go +++ b/codersdk/workspaceagents.go @@ -277,12 +277,11 @@ func (c *Client) WorkspaceAgentListeningPorts(ctx context.Context, agentID uuid. // GitProvider is a constant that represents the // type of providers that are supported within Coder. -// @typescript-ignore GitProvider type GitProvider string const ( - GitProviderAzureDevops = "azure-devops" - GitProviderGitHub = "github" - GitProviderGitLab = "gitlab" - GitProviderBitBucket = "bitbucket" + GitProviderAzureDevops GitProvider = "azure-devops" + GitProviderGitHub GitProvider = "github" + GitProviderGitLab GitProvider = "gitlab" + GitProviderBitBucket GitProvider = "bitbucket" ) diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 948a0e6304197..f13efa14891ee 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -4570,6 +4570,26 @@ Parameter represents a set value for the scope. | `template_id` | string | false | | | | `updated_at` | string | false | | | +## codersdk.TemplateVersionGitAuth + +```json +{ + "authenticate_url": "string", + "authenticated": true, + "id": "string", + "type": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------------ | ------- | -------- | ------------ | ----------- | +| `authenticate_url` | string | false | | | +| `authenticated` | boolean | false | | | +| `id` | string | false | | | +| `type` | string | false | | | + ## codersdk.TemplateVersionParameter ```json diff --git a/docs/api/templates.md b/docs/api/templates.md index b48b805d7c58e..e4e2eebc9a15b 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -1722,6 +1722,60 @@ Status Code **200** To perform this operation, you must be authenticated. [Learn more](authentication.md). +## Get git auth by template version + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/gitauth \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /templateversions/{templateversion}/gitauth` + +### Parameters + +| Name | In | Type | Required | Description | +| ----------------- | ---- | ------------ | -------- | ------------------- | +| `templateversion` | path | string(uuid) | true | Template version ID | + +### Example responses + +> 200 Response + +```json +[ + { + "authenticate_url": "string", + "authenticated": true, + "id": "string", + "type": "string" + } +] +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------- | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.TemplateVersionGitAuth](schemas.md#codersdktemplateversiongitauth) | + +

Response Schema

+ +Status Code **200** + +| Name | Type | Required | Restrictions | Description | +| -------------------- | ------- | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» authenticate_url` | string | false | | | +| `» authenticated` | boolean | false | | | +| `» id` | string | false | | | +| `» type` | string | false | | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## Get logs by template version ### Code samples diff --git a/site/src/api/api.ts b/site/src/api/api.ts index 37d89bf22288f..539987d42c918 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -280,6 +280,15 @@ export const createTemplateVersion = async ( return response.data } +export const getTemplateVersionGitAuth = async ( + versionId: string, +): Promise => { + const response = await axios.get( + `/api/v2/templateversions/${versionId}/gitauth`, + ) + return response.data +} + export const getTemplateVersionParameters = async ( versionId: string, ): Promise => { diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index dd911905a64d6..51503ef3b7774 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -387,14 +387,6 @@ export interface GetUsersResponse { readonly count: number } -// From codersdk/templateversions.go -export interface GitAuth { - readonly id: string - readonly type: GitProvider - readonly authenticate_url: string - readonly authenticated: boolean -} - // From codersdk/deployment.go export interface GitAuthConfig { readonly id: string @@ -762,6 +754,14 @@ export interface TemplateVersion { readonly created_by: User } +// From codersdk/templateversions.go +export interface TemplateVersionGitAuth { + readonly id: string + readonly type: GitProvider + readonly authenticate_url: string + readonly authenticated: boolean +} + // From codersdk/templateversions.go export interface TemplateVersionParameter { readonly name: string @@ -1141,6 +1141,15 @@ export const FeatureNames: FeatureName[] = [ "user_limit", ] +// From codersdk/workspaceagents.go +export type GitProvider = "azure-devops" | "bitbucket" | "github" | "gitlab" +export const GitProviders: GitProvider[] = [ + "azure-devops", + "bitbucket", + "github", + "gitlab", +] + // From codersdk/provisionerdaemons.go export type LogLevel = "debug" | "error" | "info" | "trace" | "warn" export const LogLevels: LogLevel[] = ["debug", "error", "info", "trace", "warn"] diff --git a/site/src/components/GitAuth/GitAuth.stories.tsx b/site/src/components/GitAuth/GitAuth.stories.tsx new file mode 100644 index 0000000000000..03db20a180cd3 --- /dev/null +++ b/site/src/components/GitAuth/GitAuth.stories.tsx @@ -0,0 +1,57 @@ +import { Story } from "@storybook/react" +import { GitAuth, GitAuthProps } from "./GitAuth" + +export default { + title: "components/GitAuth", + component: GitAuth, +} + +const Template: Story = (args) => + +export const GithubNotAuthenticated = Template.bind({}) +GithubNotAuthenticated.args = { + type: "github", + authenticated: false, +} + +export const GithubAuthenticated = Template.bind({}) +GithubNotAuthenticated.args = { + type: "github", + authenticated: true, +} + +export const GitlabNotAuthenticated = Template.bind({}) +GithubNotAuthenticated.args = { + type: "gitlab", + authenticated: false, +} + +export const GitlabAuthenticated = Template.bind({}) +GithubNotAuthenticated.args = { + type: "gitlab", + authenticated: true, +} + +export const AzureDevOpsNotAuthenticated = Template.bind({}) +GithubNotAuthenticated.args = { + type: "azure-devops", + authenticated: false, +} + +export const AzureDevOpsAuthenticated = Template.bind({}) +GithubNotAuthenticated.args = { + type: "azure-devops", + authenticated: true, +} + +export const BitbucketNotAuthenticated = Template.bind({}) +GithubNotAuthenticated.args = { + type: "bitbucket", + authenticated: false, +} + +export const BitbucketAuthenticated = Template.bind({}) +GithubNotAuthenticated.args = { + type: "bitbucket", + authenticated: true, +} diff --git a/site/src/components/GitAuth/GitAuth.tsx b/site/src/components/GitAuth/GitAuth.tsx new file mode 100644 index 0000000000000..2350e947990fb --- /dev/null +++ b/site/src/components/GitAuth/GitAuth.tsx @@ -0,0 +1,77 @@ +import { FC } from "react" +import * as TypesGen from "api/typesGenerated" +import { makeStyles } from "@material-ui/core/styles" +import { useLocation } from "react-router-dom" +import GitHub from "@material-ui/icons/GitHub" +import { GitlabIcon } from "components/Icons/GitlabIcon" +import { AzureDevOpsIcon } from "components/Icons/AzureDevOpsIcon" +import { BitbucketIcon } from "components/Icons/BitbucketIcon" +import { Typography } from "components/Typography/Typography" +import Link from "@material-ui/core/Link" +import Button from "@material-ui/core/Button" +import { Tooltip } from "@material-ui/core" + +export interface GitAuthProps { + type: TypesGen.GitProvider + authenticated: boolean + authenticateURL: string +} + +export const GitAuth: FC = ({ + type, + authenticated, + authenticateURL, +}) => { + const styles = useStyles() + + let prettyName: string + let icon: JSX.Element + switch (type) { + case "azure-devops": + prettyName = "Azure DevOps" + icon = + break + case "bitbucket": + prettyName = "Bitbucket" + icon = + break + case "github": + prettyName = "GitHub" + icon = + break + case "gitlab": + prettyName = "GitLab" + icon = + break + } + const location = useLocation() + const redirectURL = `${authenticateURL}?redirect=${location.pathname}` + + return ( + + + + + + + ) +} + +const useStyles = makeStyles(() => ({ + link: { + textDecoration: "none", + }, + root: { + display: "flex", + gap: 16, + alignItems: "center", + }, +})) diff --git a/site/src/components/Icons/AzureDevOpsIcon.tsx b/site/src/components/Icons/AzureDevOpsIcon.tsx new file mode 100644 index 0000000000000..5ac7b453b17f3 --- /dev/null +++ b/site/src/components/Icons/AzureDevOpsIcon.tsx @@ -0,0 +1,25 @@ +import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon" + +export const AzureDevOpsIcon = (props: SvgIconProps): JSX.Element => ( + + + + + + + + + + + + +) diff --git a/site/src/components/Icons/BitbucketIcon.tsx b/site/src/components/Icons/BitbucketIcon.tsx new file mode 100644 index 0000000000000..f3b436baa2462 --- /dev/null +++ b/site/src/components/Icons/BitbucketIcon.tsx @@ -0,0 +1,32 @@ +import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon" + +export const BitbucketIcon = (props: SvgIconProps): JSX.Element => ( + + + + + + + + + + + + + + + +) diff --git a/site/src/components/Icons/GitlabIcon.tsx b/site/src/components/Icons/GitlabIcon.tsx new file mode 100644 index 0000000000000..6a99d63fae0fa --- /dev/null +++ b/site/src/components/Icons/GitlabIcon.tsx @@ -0,0 +1,29 @@ +import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon" + +export const GitlabIcon = (props: SvgIconProps): JSX.Element => ( + + + + + + + + + + + + + +) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx index 0dc0f65482548..56204f7de446f 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx @@ -33,8 +33,10 @@ const CreateWorkspacePage: FC = () => { templates, templateParameters, templateSchema, + templateGitAuth, selectedTemplate, getTemplateSchemaError, + getTemplateGitAuthError, getTemplatesError, createWorkspaceError, permissions, @@ -61,11 +63,13 @@ const CreateWorkspacePage: FC = () => { selectedTemplate={selectedTemplate} templateParameters={orderedTemplateParameters(templateParameters)} templateSchema={templateSchema} + templateGitAuth={templateGitAuth} createWorkspaceErrors={{ [CreateWorkspaceErrors.GET_TEMPLATES_ERROR]: getTemplatesError, [CreateWorkspaceErrors.GET_TEMPLATE_SCHEMA_ERROR]: getTemplateSchemaError, [CreateWorkspaceErrors.CREATE_WORKSPACE_ERROR]: createWorkspaceError, + [CreateWorkspaceErrors.GET_TEMPLATE_GITAUTH_ERROR]: getTemplateGitAuthError, }} canCreateForUser={permissions?.createWorkspaceForUser} owner={owner} diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index 959252a1b9140..761fa9c04c2a4 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -15,10 +15,12 @@ import { makeStyles } from "@material-ui/core/styles" import { FullPageHorizontalForm } from "components/FullPageForm/FullPageHorizontalForm" import { SelectedTemplate } from "./SelectedTemplate" import { Loader } from "components/Loader/Loader" +import { GitAuth } from "components/GitAuth/GitAuth" export enum CreateWorkspaceErrors { GET_TEMPLATES_ERROR = "getTemplatesError", GET_TEMPLATE_SCHEMA_ERROR = "getTemplateSchemaError", + GET_TEMPLATE_GITAUTH_ERROR = "getTemplateGitAuthError", CREATE_WORKSPACE_ERROR = "createWorkspaceError", } @@ -32,6 +34,7 @@ export interface CreateWorkspacePageViewProps { selectedTemplate?: TypesGen.Template templateParameters?: TypesGen.TemplateVersionParameter[] templateSchema?: TypesGen.ParameterSchema[] + templateGitAuth?: TypesGen.TemplateVersionGitAuth[] createWorkspaceErrors: Partial> canCreateForUser?: boolean owner: TypesGen.User | null @@ -220,6 +223,36 @@ export const CreateWorkspacePageView: FC< )} + {/* Template git auth */} + {props.templateGitAuth && props.templateGitAuth.length > 0 && ( +
+
+

+ Git Authentication +

+

+ This workspace requires authenticating with Git providers to + create a workspace. +

+
+ + + {props.templateGitAuth.map((auth, index) => ( + + ))} + +
+ )} + {/* Template params */} {props.templateSchema && props.templateSchema.length > 0 && (
diff --git a/site/src/xServices/createWorkspace/createWorkspaceXService.ts b/site/src/xServices/createWorkspace/createWorkspaceXService.ts index 433c341b3c41c..686e859e39710 100644 --- a/site/src/xServices/createWorkspace/createWorkspaceXService.ts +++ b/site/src/xServices/createWorkspace/createWorkspaceXService.ts @@ -2,6 +2,7 @@ import { checkAuthorization, createWorkspace, getTemplates, + getTemplateVersionGitAuth, getTemplateVersionRichParameters, getTemplateVersionSchema, } from "api/api" @@ -9,6 +10,7 @@ import { CreateWorkspaceRequest, ParameterSchema, Template, + TemplateVersionGitAuth, TemplateVersionParameter, User, Workspace, @@ -23,11 +25,13 @@ type CreateWorkspaceContext = { selectedTemplate?: Template templateParameters?: TemplateVersionParameter[] templateSchema?: ParameterSchema[] + templateGitAuth?: TemplateVersionGitAuth[] createWorkspaceRequest?: CreateWorkspaceRequest createdWorkspace?: Workspace createWorkspaceError?: Error | unknown getTemplatesError?: Error | unknown getTemplateParametersError?: Error | unknown + getTemplateGitAuthError?: Error | unknown getTemplateSchemaError?: Error | unknown permissions?: Record checkPermissionsError?: Error | unknown @@ -56,6 +60,9 @@ export const createWorkspaceMachine = createMachine( getTemplates: { data: Template[] } + getTemplateGitAuth: { + data: TemplateVersionGitAuth[] + } getTemplateParameters: { data: TemplateVersionParameter[] } @@ -109,7 +116,7 @@ export const createWorkspaceMachine = createMachine( src: "getTemplateParameters", onDone: { actions: ["assignTemplateParameters"], - target: "checkingPermissions", + target: "gettingTemplateGitAuth", }, onError: { actions: ["assignGetTemplateParametersError"], @@ -117,6 +124,20 @@ export const createWorkspaceMachine = createMachine( }, }, }, + gettingTemplateGitAuth: { + entry: "clearTemplateGitAuthError", + invoke: { + src: "getTemplateGitAuth", + onDone: { + actions: ["assignTemplateGitAuth"], + target: "checkingPermissions", + }, + onError: { + actions: ["assignTemplateGitAuthError"], + target: "error", + }, + }, + }, checkingPermissions: { entry: "clearCheckPermissionsError", invoke: { @@ -166,6 +187,17 @@ export const createWorkspaceMachine = createMachine( { services: { getTemplates: (context) => getTemplates(context.organizationId), + getTemplateGitAuth: (context) => { + const { selectedTemplate } = context + + if (!selectedTemplate) { + throw new Error("No selected template") + } + + return getTemplateVersionGitAuth( + selectedTemplate.active_version_id, + ) + }, getTemplateParameters: (context) => { const { selectedTemplate } = context @@ -285,6 +317,15 @@ export const createWorkspaceMachine = createMachine( clearGetTemplateSchemaError: assign({ getTemplateSchemaError: (_) => undefined, }), + clearTemplateGitAuthError: assign({ + getTemplateGitAuthError: (_) => undefined, + }), + assignTemplateGitAuthError: assign({ + getTemplateGitAuthError: (_, event) => event.data, + }), + assignTemplateGitAuth: assign({ + templateGitAuth: (_, event) => event.data, + }), }, }, ) From e4188921d7f81b49d2aca18e78510a296441d96a Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 23 Feb 2023 23:45:07 +0000 Subject: [PATCH 06/20] Use BroadcastChannel to automatically authenticate with Git --- site/package.json | 2 +- .../components/GitAuth/GitAuth.stories.tsx | 14 +- site/src/components/GitAuth/GitAuth.tsx | 96 +++- .../CreateWorkspacePage.tsx | 3 +- .../CreateWorkspacePageView.stories.tsx | 22 + .../CreateWorkspacePageView.tsx | 6 +- site/src/pages/GitAuthPage/GitAuthPage.tsx | 19 +- .../createWorkspaceXService.ts | 518 +++++++++--------- 8 files changed, 390 insertions(+), 290 deletions(-) diff --git a/site/package.json b/site/package.json index bacd0fde6198d..4f5487e551404 100644 --- a/site/package.json +++ b/site/package.json @@ -136,6 +136,6 @@ "chrome 66", "firefox 63", "edge 79", - "safari 13.1" + "safari 15.4" ] } diff --git a/site/src/components/GitAuth/GitAuth.stories.tsx b/site/src/components/GitAuth/GitAuth.stories.tsx index 03db20a180cd3..cda012212d14d 100644 --- a/site/src/components/GitAuth/GitAuth.stories.tsx +++ b/site/src/components/GitAuth/GitAuth.stories.tsx @@ -15,43 +15,43 @@ GithubNotAuthenticated.args = { } export const GithubAuthenticated = Template.bind({}) -GithubNotAuthenticated.args = { +GithubAuthenticated.args = { type: "github", authenticated: true, } export const GitlabNotAuthenticated = Template.bind({}) -GithubNotAuthenticated.args = { +GitlabNotAuthenticated.args = { type: "gitlab", authenticated: false, } export const GitlabAuthenticated = Template.bind({}) -GithubNotAuthenticated.args = { +GitlabAuthenticated.args = { type: "gitlab", authenticated: true, } export const AzureDevOpsNotAuthenticated = Template.bind({}) -GithubNotAuthenticated.args = { +AzureDevOpsNotAuthenticated.args = { type: "azure-devops", authenticated: false, } export const AzureDevOpsAuthenticated = Template.bind({}) -GithubNotAuthenticated.args = { +AzureDevOpsAuthenticated.args = { type: "azure-devops", authenticated: true, } export const BitbucketNotAuthenticated = Template.bind({}) -GithubNotAuthenticated.args = { +BitbucketNotAuthenticated.args = { type: "bitbucket", authenticated: false, } export const BitbucketAuthenticated = Template.bind({}) -GithubNotAuthenticated.args = { +BitbucketAuthenticated.args = { type: "bitbucket", authenticated: true, } diff --git a/site/src/components/GitAuth/GitAuth.tsx b/site/src/components/GitAuth/GitAuth.tsx index 2350e947990fb..44c5b1f72b937 100644 --- a/site/src/components/GitAuth/GitAuth.tsx +++ b/site/src/components/GitAuth/GitAuth.tsx @@ -1,15 +1,14 @@ -import { FC } from "react" -import * as TypesGen from "api/typesGenerated" +import Button from "@material-ui/core/Button" import { makeStyles } from "@material-ui/core/styles" -import { useLocation } from "react-router-dom" +import { SvgIconProps } from "@material-ui/core/SvgIcon" +import Tooltip from "@material-ui/core/Tooltip" import GitHub from "@material-ui/icons/GitHub" -import { GitlabIcon } from "components/Icons/GitlabIcon" +import * as TypesGen from "api/typesGenerated" import { AzureDevOpsIcon } from "components/Icons/AzureDevOpsIcon" import { BitbucketIcon } from "components/Icons/BitbucketIcon" +import { GitlabIcon } from "components/Icons/GitlabIcon" import { Typography } from "components/Typography/Typography" -import Link from "@material-ui/core/Link" -import Button from "@material-ui/core/Button" -import { Tooltip } from "@material-ui/core" +import { FC } from "react" export interface GitAuthProps { type: TypesGen.GitProvider @@ -25,43 +24,70 @@ export const GitAuth: FC = ({ const styles = useStyles() let prettyName: string - let icon: JSX.Element + let Icon: (props: SvgIconProps) => JSX.Element switch (type) { case "azure-devops": prettyName = "Azure DevOps" - icon = + Icon = AzureDevOpsIcon break case "bitbucket": prettyName = "Bitbucket" - icon = + Icon = BitbucketIcon break case "github": prettyName = "GitHub" - icon = + Icon = GitHub break case "gitlab": prettyName = "GitLab" - icon = + Icon = GitlabIcon break + default: + throw new Error("invalid git provider: " + type) } - const location = useLocation() - const redirectURL = `${authenticateURL}?redirect=${location.pathname}` + // The Git auth page uses a BroadcastChannel to notify listening + // pages for Git auth updates if the "notify" query parameter is specified. + const redirectURL = `${authenticateURL}?redirect=${encodeURIComponent( + `/gitauth?notify`, + )}` return ( - - - - + + { + event.preventDefault() + // If the user is already authenticated, we don't want to redirect them + if (authenticated || authenticateURL === "") { + return + } + window.open(redirectURL, "_blank", "width=900,height=600") + }} + > + + - ) } @@ -70,8 +96,22 @@ const useStyles = makeStyles(() => ({ textDecoration: "none", }, root: { + padding: 4, display: "flex", - gap: 16, + gap: 12, alignItems: "center", + textAlign: "left", + }, + button: { + height: "unset", + }, + text: { + display: "flex", + flexDirection: "column", + gap: 4, + }, + icon: { + width: 32, + height: 32, }, })) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx index 56204f7de446f..5a9e63edac6cb 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx @@ -69,7 +69,8 @@ const CreateWorkspacePage: FC = () => { [CreateWorkspaceErrors.GET_TEMPLATE_SCHEMA_ERROR]: getTemplateSchemaError, [CreateWorkspaceErrors.CREATE_WORKSPACE_ERROR]: createWorkspaceError, - [CreateWorkspaceErrors.GET_TEMPLATE_GITAUTH_ERROR]: getTemplateGitAuthError, + [CreateWorkspaceErrors.GET_TEMPLATE_GITAUTH_ERROR]: + getTemplateGitAuthError, }} canCreateForUser={permissions?.createWorkspaceForUser} owner={owner} diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx index 35b0bead3320b..be8f907ea7f64 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx @@ -169,3 +169,25 @@ RichParameters.args = { ], createWorkspaceErrors: {}, } + +export const GitAuth = Template.bind({}) +GitAuth.args = { + templates: [MockTemplate], + selectedTemplate: MockTemplate, + createWorkspaceErrors: {}, + templateParameters: [], + templateGitAuth: [ + { + id: "github", + type: "github", + authenticated: false, + authenticate_url: "", + }, + { + id: "gitlab", + type: "gitlab", + authenticated: true, + authenticate_url: "", + }, + ], +} diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index 761fa9c04c2a4..f01de3568a948 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -231,14 +231,14 @@ export const CreateWorkspacePageView: FC< Git Authentication

- This workspace requires authenticating with Git providers to - create a workspace. + This template requires authentication to automatically + perform Git operations on create.

{props.templateGitAuth.map((auth, index) => ( diff --git a/site/src/pages/GitAuthPage/GitAuthPage.tsx b/site/src/pages/GitAuthPage/GitAuthPage.tsx index 27eacef1bd257..350f2cdbfc2d0 100644 --- a/site/src/pages/GitAuthPage/GitAuthPage.tsx +++ b/site/src/pages/GitAuthPage/GitAuthPage.tsx @@ -2,11 +2,26 @@ import Button from "@material-ui/core/Button" import { makeStyles } from "@material-ui/core/styles" import { SignInLayout } from "components/SignInLayout/SignInLayout" import { Welcome } from "components/Welcome/Welcome" -import { FC } from "react" -import { Link as RouterLink } from "react-router-dom" +import { FC, useEffect } from "react" +import { Link as RouterLink, useSearchParams } from "react-router-dom" +import { REFRESH_GITAUTH_BROADCAST_CHANNEL } from "xServices/createWorkspace/createWorkspaceXService" const GitAuthPage: FC = () => { const styles = useStyles() + const [searchParams] = useSearchParams() + const shouldNotify = searchParams.has("notify") + useEffect(() => { + if (!shouldNotify) { + return + } + + // This is used to notify the parent window that the Git auth token has been refreshed. + // It's critical in the create workspace flow! + const bc = new BroadcastChannel(REFRESH_GITAUTH_BROADCAST_CHANNEL) + // The message doesn't matter, any message refreshes the page! + bc.postMessage("noop") + window.close() + }, [shouldNotify]) return ( diff --git a/site/src/xServices/createWorkspace/createWorkspaceXService.ts b/site/src/xServices/createWorkspace/createWorkspaceXService.ts index 686e859e39710..5d5fd2a8d4c03 100644 --- a/site/src/xServices/createWorkspace/createWorkspaceXService.ts +++ b/site/src/xServices/createWorkspace/createWorkspaceXService.ts @@ -17,6 +17,8 @@ import { } from "api/typesGenerated" import { assign, createMachine } from "xstate" +export const REFRESH_GITAUTH_BROADCAST_CHANNEL = "gitauth_refresh" + type CreateWorkspaceContext = { organizationId: string owner: User | null @@ -48,284 +50,304 @@ type SelectOwnerEvent = { owner: User | null } -export const createWorkspaceMachine = createMachine( - { - id: "createWorkspaceState", - predictableActionArguments: true, - tsTypes: {} as import("./createWorkspaceXService.typegen").Typegen0, - schema: { - context: {} as CreateWorkspaceContext, - events: {} as CreateWorkspaceEvent | SelectOwnerEvent, - services: {} as { - getTemplates: { - data: Template[] - } - getTemplateGitAuth: { - data: TemplateVersionGitAuth[] - } - getTemplateParameters: { - data: TemplateVersionParameter[] - } - getTemplateSchema: { - data: ParameterSchema[] - } - createWorkspace: { - data: Workspace - } +type RefreshGitAuthEvent = { + type: "REFRESH_GITAUTH" +} + +export const createWorkspaceMachine = + /** @xstate-layout N4IgpgJg5mDOIC5QGMBOYCGAXMB1A9qgNawAOGyYAyltmAHQxZYCWAdlACpgC2pANnVgBiCPjYN2AN3xEGaTDgLEyFarRyMwzdl14ChCafmTYW4gNoAGALrWbiUKXywWrcY5AAPRABYATAA0IACeiACMvgCs9ADssQBsVgDM-v5JyQnJUQkAvrnBCnTKJOSUNHRaOhzcfII4ImIS9MZy9EVKhKVqFZpMrDX69XBGbDKm7mz2FuEOSCDOrpOePgjJAJzr9AAcVr4J677bCeFWB3vBYQgBvvQJB-5R6+GxVulPUfmF6MVdquUaBj9XS1AwNYRgVCoQj0MEAM0IPHaP06KjK6kqwMGdUMxgm5imtnsnkWbgJK0QGy2u32h2Op3OvkuiH8vis9HCDxO0XC2z5CX8XxAHTwf3RvSB2gGehxOCoyAAFrwMKJxJIxrJ5CjRWieoCqtLQcN5UqeBhRuMzJYibYSS4yR55qtTv52bFeYlfPEor4XuFmddwlscqz1mdfMlfZlfEKRSV-hi+lKQUM6CblRCoTD4YjkYodd0AZjk9iwdRFcqLSYrYS7Lb5qTlk6Im83R6El7Yj6-QH4gl6M8ctsoht7skrJ8CsLtfHxfqsTKywAFDCoDA8bSQxpqloatpxsV64vVRfDFdrjc4VCwKv4611uZOe1N0DOgXhOKRjZf-axR4Btl2XWWJngSKJtgCHJwnSWMZ0PIskxPI06HPddN2vTNoVQWF6gRVAkQPXUEMlJDUxwVDLy3W8a2mesnyWclmwQTl-A-WIv3WH8Ej-KIAyyW4Em2Z5XV5fx1m48JYPzWcj0Qw0yLAABxNwAEEAFcsAVVVmlaLVpPgxMSPk2UlNUjSFWoyZaMfBZn0Y18WSDfsfUEvlfEHViA2SbZ-HoDZwPSe4omCwUp0IwtDINFMTOUrB1M0zDs1w3NwoTCUotLYZYviiy8Rom0bMbezvEc8T6BcvkII8-1Qj8dZfP2R4fXEziUliKTfiIyKK2QIhdCXSEeBYWBXHEbcdL3eQlV6gb8OG0a2FgYkGzsx0HIQfwfJiKJXmSZJoO88IRwAqwP22aD3OeLshP2DrUQi9Ker6jhZqGkaCRESEsJw7A8II6aiFe+aPuW+iHTYCkNqiKxtgHVi+XWYK2SZWqNr5HZslAiNoJSSdvn0rr0rhFh+H4frV3XEQAGEACUAFEVM4OmAH1cAAeRpgBpKglxUqm6dB2yGLWkq0cecrdv2-xDuO1GXluRH9s5H1wKObi7oLNL9WJ0nyYvEQqDpgAZOmqc4Zm2dwAA5OmacFoqRdWcd0asSWkZuoJUbSftpc2vZ6rZDsXg1mTiLzMwOFDsBtPVGR9zgwn9Q6XQo8sglrLtYWIaYzbxZ2lIpZl5IA3O+hLvWeleSiVj6pDgzHpRFODMS7Cc3w8P7q1ypk8jgy0-ve3Vuz9bc+2yWDvO2WrjE2HMcybZYiOHJYm2fIpzYfAIDgTxUrnOhM-ByGAFoEgDE-6CsS+3n8d0nl9aW8enAmHvnEtTyEA+X1F4cOWryM0k5JyTiAZWS3DSKBdIC9zjZDronY8xkyzpjNJ-YqqxXjORXuEfaEYAh9gAtLO4aQNgenqkdSMsCX7wOisuCmlFrwoMdhETazl9hCQ2NLKwFd1gnRiCkMM51og8k2BQruclqFZTMppBhw9RZBldvQTa0FzgdnuF5Y4ZdgrumyI8JyC8RF700E9fqg1gZjWkZDUBWwDgjjElgnawE1GxAUUJPYglwIjjeDGMKCdKGaB1mTF6tD4ArSzpDfabwdiXxApseqtjT6o1SE4txrVXTpHSF4-GnVfF6QjlAKO5ic5RCOn5LsbwjpHWeJyAMZVThHUgvcCSvp9GyRyTgCABT1rhN8rsV2MTYmgQDC6BR7xuznByCcZpYcvqEA6aLSxdxFa2OyCBWIAYimwxXvwoMrp3GrzXkAA */ + createMachine( + { + id: "createWorkspaceState", + predictableActionArguments: true, + tsTypes: {} as import("./createWorkspaceXService.typegen").Typegen0, + schema: { + context: {} as CreateWorkspaceContext, + events: {} as + | CreateWorkspaceEvent + | SelectOwnerEvent + | RefreshGitAuthEvent, + services: {} as { + getTemplates: { + data: Template[] + } + getTemplateGitAuth: { + data: TemplateVersionGitAuth[] + } + getTemplateParameters: { + data: TemplateVersionParameter[] + } + getTemplateSchema: { + data: ParameterSchema[] + } + createWorkspace: { + data: Workspace + } + }, }, - }, - initial: "gettingTemplates", - states: { - gettingTemplates: { - entry: "clearGetTemplatesError", - invoke: { - src: "getTemplates", - onDone: [ - { - actions: ["assignTemplates"], - cond: "areTemplatesEmpty", - }, - { - actions: ["assignTemplates", "assignSelectedTemplate"], - target: "gettingTemplateSchema", + initial: "gettingTemplates", + states: { + gettingTemplates: { + entry: "clearGetTemplatesError", + invoke: { + src: "getTemplates", + onDone: [ + { + actions: ["assignTemplates"], + cond: "areTemplatesEmpty", + }, + { + actions: ["assignTemplates", "assignSelectedTemplate"], + target: "gettingTemplateSchema", + }, + ], + onError: { + actions: ["assignGetTemplatesError"], + target: "error", }, - ], - onError: { - actions: ["assignGetTemplatesError"], - target: "error", }, }, - }, - gettingTemplateSchema: { - entry: "clearGetTemplateSchemaError", - invoke: { - src: "getTemplateSchema", - onDone: { - actions: ["assignTemplateSchema"], - target: "gettingTemplateParameters", - }, - onError: { - actions: ["assignGetTemplateSchemaError"], - target: "error", + gettingTemplateSchema: { + entry: "clearGetTemplateSchemaError", + invoke: { + src: "getTemplateSchema", + onDone: { + actions: ["assignTemplateSchema"], + target: "gettingTemplateParameters", + }, + onError: { + actions: ["assignGetTemplateSchemaError"], + target: "error", + }, }, }, - }, - gettingTemplateParameters: { - entry: "clearGetTemplateParametersError", - invoke: { - src: "getTemplateParameters", - onDone: { - actions: ["assignTemplateParameters"], - target: "gettingTemplateGitAuth", - }, - onError: { - actions: ["assignGetTemplateParametersError"], - target: "error", + gettingTemplateParameters: { + entry: "clearGetTemplateParametersError", + invoke: { + src: "getTemplateParameters", + onDone: { + actions: ["assignTemplateParameters"], + target: "checkingPermissions", + }, + onError: { + actions: ["assignGetTemplateParametersError"], + target: "error", + }, }, }, - }, - gettingTemplateGitAuth: { - entry: "clearTemplateGitAuthError", - invoke: { - src: "getTemplateGitAuth", - onDone: { - actions: ["assignTemplateGitAuth"], - target: "checkingPermissions", - }, - onError: { - actions: ["assignTemplateGitAuthError"], - target: "error", + checkingPermissions: { + entry: "clearCheckPermissionsError", + invoke: { + src: "checkPermissions", + id: "checkPermissions", + onDone: { + actions: "assignPermissions", + target: "gettingTemplateGitAuth", + }, + onError: { + actions: ["assignCheckPermissionsError"], + }, }, }, - }, - checkingPermissions: { - entry: "clearCheckPermissionsError", - invoke: { - src: "checkPermissions", - id: "checkPermissions", - onDone: { - actions: "assignPermissions", - target: "fillingParams", - }, - onError: { - actions: ["assignCheckPermissionsError"], + gettingTemplateGitAuth: { + entry: "clearTemplateGitAuthError", + invoke: { + src: "getTemplateGitAuth", + onDone: { + actions: ["assignTemplateGitAuth"], + target: "fillingParams", + }, + onError: { + actions: ["assignTemplateGitAuthError"], + target: "error", + }, }, }, - }, - fillingParams: { - on: { - CREATE_WORKSPACE: { - actions: ["assignCreateWorkspaceRequest", "assignOwner"], - target: "creatingWorkspace", + fillingParams: { + invoke: { + id: "listenForRefreshGitAuth", + src: () => (callback) => { + const bc = new BroadcastChannel(REFRESH_GITAUTH_BROADCAST_CHANNEL) + bc.addEventListener("message", () => { + callback("REFRESH_GITAUTH") + }) + return () => bc.close() + }, }, - SELECT_OWNER: { - actions: ["assignOwner"], - target: ["fillingParams"], + on: { + CREATE_WORKSPACE: { + actions: ["assignCreateWorkspaceRequest", "assignOwner"], + target: "creatingWorkspace", + }, + SELECT_OWNER: { + actions: ["assignOwner"], + target: ["fillingParams"], + }, + REFRESH_GITAUTH: { + target: "gettingTemplateGitAuth", + }, }, }, - }, - creatingWorkspace: { - entry: "clearCreateWorkspaceError", - invoke: { - src: "createWorkspace", - onDone: { - actions: ["onCreateWorkspace"], - target: "created", - }, - onError: { - actions: ["assignCreateWorkspaceError"], - target: "fillingParams", + creatingWorkspace: { + entry: "clearCreateWorkspaceError", + invoke: { + src: "createWorkspace", + onDone: { + actions: ["onCreateWorkspace"], + target: "created", + }, + onError: { + actions: ["assignCreateWorkspaceError"], + target: "fillingParams", + }, }, }, + created: { + type: "final", + }, + error: {}, }, - created: { - type: "final", - }, - error: {}, }, - }, - { - services: { - getTemplates: (context) => getTemplates(context.organizationId), - getTemplateGitAuth: (context) => { - const { selectedTemplate } = context + { + services: { + getTemplates: (context) => getTemplates(context.organizationId), + getTemplateGitAuth: (context) => { + const { selectedTemplate } = context - if (!selectedTemplate) { - throw new Error("No selected template") - } + if (!selectedTemplate) { + throw new Error("No selected template") + } - return getTemplateVersionGitAuth( - selectedTemplate.active_version_id, - ) - }, - getTemplateParameters: (context) => { - const { selectedTemplate } = context + return getTemplateVersionGitAuth(selectedTemplate.active_version_id) + }, + getTemplateParameters: (context) => { + const { selectedTemplate } = context - if (!selectedTemplate) { - throw new Error("No selected template") - } + if (!selectedTemplate) { + throw new Error("No selected template") + } - return getTemplateVersionRichParameters( - selectedTemplate.active_version_id, - ) - }, - getTemplateSchema: (context) => { - const { selectedTemplate } = context + return getTemplateVersionRichParameters( + selectedTemplate.active_version_id, + ) + }, + getTemplateSchema: (context) => { + const { selectedTemplate } = context - if (!selectedTemplate) { - throw new Error("No selected template") - } + if (!selectedTemplate) { + throw new Error("No selected template") + } - return getTemplateVersionSchema(selectedTemplate.active_version_id) - }, - checkPermissions: async (context) => { - if (!context.organizationId) { - throw new Error("No organization ID") - } + return getTemplateVersionSchema(selectedTemplate.active_version_id) + }, + checkPermissions: async (context) => { + if (!context.organizationId) { + throw new Error("No organization ID") + } - // HACK: below, we pass in * for the owner_id, which is a hacky way of checking if the - // current user can create a workspace on behalf of anyone within the org (only org owners should be able to do this). - // This pattern should not be replicated outside of this narrow use case. - const permissionsToCheck = { - createWorkspaceForUser: { - object: { - resource_type: "workspace", - organization_id: `${context.organizationId}`, - owner_id: "*", + // HACK: below, we pass in * for the owner_id, which is a hacky way of checking if the + // current user can create a workspace on behalf of anyone within the org (only org owners should be able to do this). + // This pattern should not be replicated outside of this narrow use case. + const permissionsToCheck = { + createWorkspaceForUser: { + object: { + resource_type: "workspace", + organization_id: `${context.organizationId}`, + owner_id: "*", + }, + action: "create", }, - action: "create", - }, - } + } - return checkAuthorization({ - checks: permissionsToCheck, - }) - }, - createWorkspace: (context) => { - const { createWorkspaceRequest, organizationId, owner } = context + return checkAuthorization({ + checks: permissionsToCheck, + }) + }, + createWorkspace: (context) => { + const { createWorkspaceRequest, organizationId, owner } = context - if (!createWorkspaceRequest) { - throw new Error("No create workspace request") - } + if (!createWorkspaceRequest) { + throw new Error("No create workspace request") + } - return createWorkspace( - organizationId, - owner?.id ?? "me", - createWorkspaceRequest, - ) - }, - }, - guards: { - areTemplatesEmpty: (_, event) => event.data.length === 0, - }, - actions: { - assignTemplates: assign({ - templates: (_, event) => event.data, - }), - assignSelectedTemplate: assign({ - selectedTemplate: (ctx, event) => { - const templates = event.data.filter( - (template) => template.name === ctx.templateName, + return createWorkspace( + organizationId, + owner?.id ?? "me", + createWorkspaceRequest, ) - return templates.length > 0 ? templates[0] : undefined }, - }), - assignTemplateParameters: assign({ - templateParameters: (_, event) => event.data, - }), - assignTemplateSchema: assign({ - // Only show parameters that are allowed to be overridden. - // CLI code: https://github.com/coder/coder/blob/main/cli/create.go#L152-L155 - templateSchema: (_, event) => event.data, - }), - assignPermissions: assign({ - permissions: (_, event) => event.data as Record, - }), - assignCheckPermissionsError: assign({ - checkPermissionsError: (_, event) => event.data, - }), - clearCheckPermissionsError: assign({ - checkPermissionsError: (_) => undefined, - }), - assignCreateWorkspaceRequest: assign({ - createWorkspaceRequest: (_, event) => event.request, - }), - assignOwner: assign({ - owner: (_, event) => event.owner, - }), - assignCreateWorkspaceError: assign({ - createWorkspaceError: (_, event) => event.data, - }), - clearCreateWorkspaceError: assign({ - createWorkspaceError: (_) => undefined, - }), - assignGetTemplatesError: assign({ - getTemplatesError: (_, event) => event.data, - }), - clearGetTemplatesError: assign({ - getTemplatesError: (_) => undefined, - }), - assignGetTemplateParametersError: assign({ - getTemplateParametersError: (_, event) => event.data, - }), - clearGetTemplateParametersError: assign({ - getTemplateParametersError: (_) => undefined, - }), - assignGetTemplateSchemaError: assign({ - getTemplateSchemaError: (_, event) => event.data, - }), - clearGetTemplateSchemaError: assign({ - getTemplateSchemaError: (_) => undefined, - }), - clearTemplateGitAuthError: assign({ - getTemplateGitAuthError: (_) => undefined, - }), - assignTemplateGitAuthError: assign({ - getTemplateGitAuthError: (_, event) => event.data, - }), - assignTemplateGitAuth: assign({ - templateGitAuth: (_, event) => event.data, - }), + }, + guards: { + areTemplatesEmpty: (_, event) => event.data.length === 0, + }, + actions: { + assignTemplates: assign({ + templates: (_, event) => event.data, + }), + assignSelectedTemplate: assign({ + selectedTemplate: (ctx, event) => { + const templates = event.data.filter( + (template) => template.name === ctx.templateName, + ) + return templates.length > 0 ? templates[0] : undefined + }, + }), + assignTemplateParameters: assign({ + templateParameters: (_, event) => event.data, + }), + assignTemplateSchema: assign({ + // Only show parameters that are allowed to be overridden. + // CLI code: https://github.com/coder/coder/blob/main/cli/create.go#L152-L155 + templateSchema: (_, event) => event.data, + }), + assignPermissions: assign({ + permissions: (_, event) => event.data as Record, + }), + assignCheckPermissionsError: assign({ + checkPermissionsError: (_, event) => event.data, + }), + clearCheckPermissionsError: assign({ + checkPermissionsError: (_) => undefined, + }), + assignCreateWorkspaceRequest: assign({ + createWorkspaceRequest: (_, event) => event.request, + }), + assignOwner: assign({ + owner: (_, event) => event.owner, + }), + assignCreateWorkspaceError: assign({ + createWorkspaceError: (_, event) => event.data, + }), + clearCreateWorkspaceError: assign({ + createWorkspaceError: (_) => undefined, + }), + assignGetTemplatesError: assign({ + getTemplatesError: (_, event) => event.data, + }), + clearGetTemplatesError: assign({ + getTemplatesError: (_) => undefined, + }), + assignGetTemplateParametersError: assign({ + getTemplateParametersError: (_, event) => event.data, + }), + clearGetTemplateParametersError: assign({ + getTemplateParametersError: (_) => undefined, + }), + assignGetTemplateSchemaError: assign({ + getTemplateSchemaError: (_, event) => event.data, + }), + clearGetTemplateSchemaError: assign({ + getTemplateSchemaError: (_) => undefined, + }), + clearTemplateGitAuthError: assign({ + getTemplateGitAuthError: (_) => undefined, + }), + assignTemplateGitAuthError: assign({ + getTemplateGitAuthError: (_, event) => event.data, + }), + assignTemplateGitAuth: assign({ + templateGitAuth: (_, event) => event.data, + }), + }, }, - }, -) + ) From 6b75f3e9caa65369bd2d247f74b4aa29a7dadfe6 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Fri, 24 Feb 2023 00:20:57 +0000 Subject: [PATCH 07/20] Add error validation for submitting the create workspace form --- site/src/components/GitAuth/GitAuth.tsx | 67 ++++++++++--------- .../CreateWorkspacePageView.tsx | 25 ++++++- 2 files changed, 57 insertions(+), 35 deletions(-) diff --git a/site/src/components/GitAuth/GitAuth.tsx b/site/src/components/GitAuth/GitAuth.tsx index 44c5b1f72b937..73773f0cb1629 100644 --- a/site/src/components/GitAuth/GitAuth.tsx +++ b/site/src/components/GitAuth/GitAuth.tsx @@ -1,5 +1,6 @@ import Button from "@material-ui/core/Button" -import { makeStyles } from "@material-ui/core/styles" +import FormHelperText from "@material-ui/core/FormHelperText" +import { makeStyles, Theme } from "@material-ui/core/styles" import { SvgIconProps } from "@material-ui/core/SvgIcon" import Tooltip from "@material-ui/core/Tooltip" import GitHub from "@material-ui/icons/GitHub" @@ -14,14 +15,18 @@ export interface GitAuthProps { type: TypesGen.GitProvider authenticated: boolean authenticateURL: string + error?: string } export const GitAuth: FC = ({ type, authenticated, authenticateURL, + error, }) => { - const styles = useStyles() + const styles = useStyles({ + error: typeof error !== "undefined", + }) let prettyName: string let Icon: (props: SvgIconProps) => JSX.Element @@ -57,41 +62,42 @@ export const GitAuth: FC = ({ authenticated ? "You're already authenticated! No action needed." : `` } > - { - event.preventDefault() - // If the user is already authenticated, we don't want to redirect them - if (authenticated || authenticateURL === "") { - return - } - window.open(redirectURL, "_blank", "width=900,height=600") - }} - > - - + + + {error && {error}} + ) } -const useStyles = makeStyles(() => ({ +const useStyles = makeStyles< + Theme, + { + error: boolean + } +>((theme) => ({ link: { textDecoration: "none", }, @@ -104,11 +110,8 @@ const useStyles = makeStyles(() => ({ }, button: { height: "unset", - }, - text: { - display: "flex", - flexDirection: "column", - gap: 4, + border: ({ error }) => + error ? `1px solid ${theme.palette.error.main}` : "unset", }, icon: { width: 32, diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index f01de3568a948..7865b1b7aec2a 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -6,7 +6,7 @@ import { RichParameterInput } from "components/RichParameterInput/RichParameterI import { Stack } from "components/Stack/Stack" import { UserAutocomplete } from "components/UserAutocomplete/UserAutocomplete" import { FormikContextType, FormikTouched, useFormik } from "formik" -import { FC, useState } from "react" +import { FC, useEffect, useState } from "react" import { useTranslation } from "react-i18next" import { getFormHelpers, nameValidator, onChangeTrimmed } from "util/formUtils" import * as Yup from "yup" @@ -58,6 +58,10 @@ export const CreateWorkspacePageView: FC< props.templateParameters, props.defaultParameterValues, ) + const [gitAuthErrors, setGitAuthErrors] = useState>({}) + useEffect(() => { + setGitAuthErrors({}) + }, [props.templateGitAuth]) const { t } = useTranslation("createWorkspacePage") @@ -78,6 +82,20 @@ export const CreateWorkspacePageView: FC< enableReinitialize: true, initialTouched: props.initialTouched, onSubmit: (request) => { + for (let i = 0; i < (props.templateGitAuth?.length || 0); i++) { + const auth = props.templateGitAuth?.[i] + if (!auth) { + continue + } + if (!auth.authenticated) { + setGitAuthErrors({ + [auth.id]: "You must authenticate to create a workspace!", + }) + form.setSubmitting(false) + return + } + } + if (!props.templateSchema) { throw new Error("No template schema loaded") } @@ -231,8 +249,8 @@ export const CreateWorkspacePageView: FC< Git Authentication

- This template requires authentication to automatically - perform Git operations on create. + This template requires authentication to automatically perform + Git operations on create.

@@ -247,6 +265,7 @@ export const CreateWorkspacePageView: FC< authenticateURL={auth.authenticate_url} authenticated={auth.authenticated} type={auth.type} + error={gitAuthErrors[auth.id]} /> ))}
From 1ad5db165c8e357a9812c5b095841b289514cc3c Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Sat, 25 Feb 2023 16:46:01 +0000 Subject: [PATCH 08/20] Fix panic on template dry-run --- provisioner/terraform/executor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provisioner/terraform/executor.go b/provisioner/terraform/executor.go index d3c3318340095..a7d3a0c08e069 100644 --- a/provisioner/terraform/executor.go +++ b/provisioner/terraform/executor.go @@ -364,7 +364,7 @@ func (e *executor) stateResources(ctx, killCtx context.Context) (*ConvertedState if err != nil { return nil, xerrors.Errorf("get terraform graph: %w", err) } - var converted *ConvertedState + converted := &ConvertedState{} if state.Values != nil { converted, err = ConvertState([]*tfjson.StateModule{ state.Values.RootModule, From 320c10d4a81915fb99be3cc46ca6ef21ad84adcc Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Sat, 25 Feb 2023 21:34:04 +0000 Subject: [PATCH 09/20] Add tests for the template version Git auth endpoint --- coderd/templateversions_test.go | 52 ++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/coderd/templateversions_test.go b/coderd/templateversions_test.go index 4648f8fae4706..00dca0d974b3a 100644 --- a/coderd/templateversions_test.go +++ b/coderd/templateversions_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "net/http" + "regexp" "testing" "github.com/google/uuid" @@ -14,6 +15,7 @@ import ( "github.com/coder/coder/coderd/audit" "github.com/coder/coder/coderd/coderdtest" "github.com/coder/coder/coderd/database" + "github.com/coder/coder/coderd/gitauth" "github.com/coder/coder/coderd/provisionerdserver" "github.com/coder/coder/codersdk" "github.com/coder/coder/examples" @@ -436,11 +438,12 @@ func TestTemplateVersionParameters(t *testing.T) { func TestTemplateVersionsGitAuth(t *testing.T) { t.Parallel() - t.Run("WithoutAny", func(t *testing.T) { + t.Run("Empty", func(t *testing.T) { t.Parallel() - client := coderdtest.New(t, nil) + client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) + coderdtest.AwaitTemplateVersionJob(t, client, version.ID) ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) defer cancel() @@ -448,9 +451,50 @@ func TestTemplateVersionsGitAuth(t *testing.T) { _, err := client.TemplateVersionGitAuth(ctx, version.ID) require.NoError(t, err) }) - // t.Run("", func(t *testing.T) { + t.Run("Authenticated", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, &coderdtest.Options{ + IncludeProvisionerDaemon: true, + GitAuthConfigs: []*gitauth.Config{{ + OAuth2Config: &oauth2Config{}, + ID: "github", + Regex: regexp.MustCompile(`github\.com`), + Type: codersdk.GitProviderGitHub, + }}, + }) + user := coderdtest.CreateFirstUser(t, client) + version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ + Parse: echo.ParseComplete, + ProvisionPlan: []*proto.Provision_Response{{ + Type: &proto.Provision_Response_Complete{ + Complete: &proto.Provision_Complete{ + GitAuthProviders: []string{"github"}, + }, + }, + }}, + }) + version = coderdtest.AwaitTemplateVersionJob(t, client, version.ID) + require.Empty(t, version.Job.Error) + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() - // }) + // Not authenticated to start! + providers, err := client.TemplateVersionGitAuth(ctx, version.ID) + require.NoError(t, err) + require.Len(t, providers, 1) + require.False(t, providers[0].Authenticated) + + // Perform the Git auth callback to authenticate the user... + resp := gitAuthCallback(t, "github", client) + _ = resp.Body.Close() + require.Equal(t, http.StatusTemporaryRedirect, resp.StatusCode) + + // Ensure that the returned Git auth for the template is authenticated! + providers, err = client.TemplateVersionGitAuth(ctx, version.ID) + require.NoError(t, err) + require.Len(t, providers, 1) + require.True(t, providers[0].Authenticated) + }) } func TestTemplateVersionResources(t *testing.T) { From 18488f8d4bccf489515a5e49891efa60cca7f147 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Sun, 26 Feb 2023 18:21:26 +0000 Subject: [PATCH 10/20] Show error if no gitauth is configured --- .../CreateWorkspacePageView.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx index 7865b1b7aec2a..d25911c2f1d9d 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx @@ -163,6 +163,20 @@ export const CreateWorkspacePageView: FC< } /> )} + {Boolean( + props.createWorkspaceErrors[ + CreateWorkspaceErrors.GET_TEMPLATE_GITAUTH_ERROR + ], + ) && ( + + )} ) } From 18523d5a0687cc90e87071594244224441729a5c Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Sun, 26 Feb 2023 22:05:50 +0000 Subject: [PATCH 11/20] Add gitauth to cliui --- cli/cliui/gitauth.go | 72 ++++++++++++++++++ cli/cliui/gitauth_test.go | 55 ++++++++++++++ cli/create.go | 10 +++ cli/create_test.go | 98 +++++++++++++++++++++++++ cmd/cliui/main.go | 33 +++++++++ coderd/coderdtest/coderdtest.go | 27 +++++++ coderd/templateversions.go | 8 ++ coderd/templateversions_test.go | 2 +- coderd/workspaceagents_test.go | 37 ++-------- codersdk/workspaceagents.go | 15 ++++ site/src/components/GitAuth/GitAuth.tsx | 9 +-- 11 files changed, 327 insertions(+), 39 deletions(-) create mode 100644 cli/cliui/gitauth.go create mode 100644 cli/cliui/gitauth_test.go diff --git a/cli/cliui/gitauth.go b/cli/cliui/gitauth.go new file mode 100644 index 0000000000000..7b4bd6f30e264 --- /dev/null +++ b/cli/cliui/gitauth.go @@ -0,0 +1,72 @@ +package cliui + +import ( + "context" + "fmt" + "io" + "time" + + "github.com/briandowns/spinner" + + "github.com/coder/coder/codersdk" +) + +type GitAuthOptions struct { + Fetch func(context.Context) ([]codersdk.TemplateVersionGitAuth, error) + FetchInterval time.Duration +} + +func GitAuth(ctx context.Context, writer io.Writer, opts GitAuthOptions) error { + if opts.FetchInterval == 0 { + opts.FetchInterval = 500 * time.Millisecond + } + gitAuth, err := opts.Fetch(ctx) + if err != nil { + return err + } + + spin := spinner.New(spinner.CharSets[78], 100*time.Millisecond, spinner.WithColor("fgHiGreen")) + spin.Writer = writer + spin.ForceOutput = true + spin.Suffix = " Waiting for Git authentication..." + defer spin.Stop() + + ticker := time.NewTicker(opts.FetchInterval) + defer ticker.Stop() + for _, auth := range gitAuth { + if auth.Authenticated { + return nil + } + + _, _ = fmt.Fprintf(writer, "You must authenticate with %s to create a workspace with this template. Visit:\n\n\t%s\n\n", auth.Type.Pretty(), auth.AuthenticateURL) + + ticker.Reset(opts.FetchInterval) + spin.Start() + for { + select { + case <-ctx.Done(): + return ctx.Err() + case <-ticker.C: + } + gitAuth, err := opts.Fetch(ctx) + if err != nil { + return err + } + var authed bool + for _, a := range gitAuth { + if !a.Authenticated || a.ID != auth.ID { + continue + } + authed = true + break + } + // The user authenticated with the provider! + if authed { + break + } + } + spin.Stop() + _, _ = fmt.Fprintf(writer, "Successfully authenticated with %s!\n\n", auth.Type.Pretty()) + } + return nil +} diff --git a/cli/cliui/gitauth_test.go b/cli/cliui/gitauth_test.go new file mode 100644 index 0000000000000..de2198798e8d3 --- /dev/null +++ b/cli/cliui/gitauth_test.go @@ -0,0 +1,55 @@ +package cliui_test + +import ( + "context" + "net/url" + "sync/atomic" + "testing" + "time" + + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" + + "github.com/coder/coder/cli/cliui" + "github.com/coder/coder/codersdk" + "github.com/coder/coder/pty/ptytest" + "github.com/coder/coder/testutil" +) + +func TestGitAuth(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort) + defer cancel() + + ptty := ptytest.New(t) + cmd := &cobra.Command{ + RunE: func(cmd *cobra.Command, args []string) error { + var fetched atomic.Bool + return cliui.GitAuth(cmd.Context(), cmd.OutOrStdout(), cliui.GitAuthOptions{ + Fetch: func(ctx context.Context) ([]codersdk.TemplateVersionGitAuth, error) { + defer fetched.Store(true) + return []codersdk.TemplateVersionGitAuth{{ + ID: "github", + Type: codersdk.GitProviderGitHub, + Authenticated: fetched.Load(), + AuthenticateURL: "https://example.com/gitauth/github?redirect=" + url.QueryEscape("/gitauth?notify"), + }}, nil + }, + FetchInterval: time.Millisecond, + }) + }, + } + cmd.SetOutput(ptty.Output()) + cmd.SetIn(ptty.Input()) + done := make(chan struct{}) + go func() { + defer close(done) + err := cmd.Execute() + assert.NoError(t, err) + }() + ptty.ExpectMatchContext(ctx, "You must authenticate with") + ptty.ExpectMatchContext(ctx, "https://example.com/gitauth/github") + ptty.ExpectMatchContext(ctx, "Successfully authenticated with GitHub") + <-done +} diff --git a/cli/create.go b/cli/create.go index 4b1f306267e81..72f84d123a3a4 100644 --- a/cli/create.go +++ b/cli/create.go @@ -1,6 +1,7 @@ package cli import ( + "context" "fmt" "io" "time" @@ -324,6 +325,15 @@ PromptRichParamLoop: _, _ = fmt.Fprintln(cmd.OutOrStdout()) } + err = cliui.GitAuth(ctx, cmd.OutOrStdout(), cliui.GitAuthOptions{ + Fetch: func(ctx context.Context) ([]codersdk.TemplateVersionGitAuth, error) { + return client.TemplateVersionGitAuth(ctx, templateVersion.ID) + }, + }) + if err != nil { + return nil, xerrors.Errorf("template version git auth: %w", err) + } + // Run a dry-run with the given parameters to check correctness dryRun, err := client.CreateTemplateVersionDryRun(cmd.Context(), templateVersion.ID, codersdk.CreateTemplateVersionDryRunRequest{ WorkspaceName: args.NewWorkspaceName, diff --git a/cli/create_test.go b/cli/create_test.go index 5445db51cdd38..dfa944e881d47 100644 --- a/cli/create_test.go +++ b/cli/create_test.go @@ -3,15 +3,21 @@ package cli_test import ( "context" "fmt" + "net/http" + "net/url" "os" + "regexp" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "golang.org/x/oauth2" "github.com/coder/coder/cli/clitest" "github.com/coder/coder/coderd/coderdtest" + "github.com/coder/coder/coderd/database" + "github.com/coder/coder/coderd/gitauth" "github.com/coder/coder/codersdk" "github.com/coder/coder/provisioner/echo" "github.com/coder/coder/provisionersdk/proto" @@ -603,6 +609,61 @@ func TestCreateValidateRichParameters(t *testing.T) { }) } +func TestCreateWithGitAuth(t *testing.T) { + t.Parallel() + echoResponses := &echo.Responses{ + Parse: echo.ParseComplete, + ProvisionPlan: []*proto.Provision_Response{ + { + Type: &proto.Provision_Response_Complete{ + Complete: &proto.Provision_Complete{ + GitAuthProviders: []string{"github"}, + }, + }, + }, + }, + ProvisionApply: []*proto.Provision_Response{{ + Type: &proto.Provision_Response_Complete{ + Complete: &proto.Provision_Complete{}, + }, + }}, + } + + client := coderdtest.New(t, &coderdtest.Options{ + GitAuthConfigs: []*gitauth.Config{{ + OAuth2Config: &oauth2Config{}, + ID: "github", + Regex: regexp.MustCompile(`github\.com`), + Type: codersdk.GitProviderGitHub, + }}, + IncludeProvisionerDaemon: true, + }) + user := coderdtest.CreateFirstUser(t, client) + version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, echoResponses) + coderdtest.AwaitTemplateVersionJob(t, client, version.ID) + template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) + + cmd, root := clitest.New(t, "create", "my-workspace", "--template", template.Name) + clitest.SetupConfig(t, client, root) + doneChan := make(chan struct{}) + pty := ptytest.New(t) + cmd.SetIn(pty.Input()) + cmd.SetOut(pty.Output()) + go func() { + defer close(doneChan) + err := cmd.Execute() + assert.NoError(t, err) + }() + + pty.ExpectMatch("You must authenticate with GitHub to create a workspace") + resp := coderdtest.RequestGitAuthCallback(t, "github", client) + _ = resp.Body.Close() + require.Equal(t, http.StatusTemporaryRedirect, resp.StatusCode) + pty.ExpectMatch("Confirm create?") + pty.WriteLine("yes") + <-doneChan +} + func createTestParseResponseWithDefault(defaultValue string) []*proto.Parse_Response { return []*proto.Parse_Response{{ Type: &proto.Parse_Response_Complete{ @@ -638,3 +699,40 @@ func createTestParseResponseWithDefault(defaultValue string) []*proto.Parse_Resp }, }} } + +type oauth2Config struct { + token *oauth2.Token +} + +func (*oauth2Config) AuthCodeURL(state string, _ ...oauth2.AuthCodeOption) string { + return "/?state=" + url.QueryEscape(state) +} + +func (o *oauth2Config) Exchange(context.Context, string, ...oauth2.AuthCodeOption) (*oauth2.Token, error) { + return &oauth2.Token{ + AccessToken: "token", + RefreshToken: "refresh", + Expiry: database.Now().Add(time.Hour), + }, nil +} + +func (o *oauth2Config) TokenSource(context.Context, *oauth2.Token) oauth2.TokenSource { + return &oauth2TokenSource{ + token: o.token, + } +} + +type oauth2TokenSource struct { + token *oauth2.Token +} + +func (o *oauth2TokenSource) Token() (*oauth2.Token, error) { + if o.token != nil { + return o.token, nil + } + return &oauth2.Token{ + AccessToken: "token", + RefreshToken: "refresh", + Expiry: database.Now().Add(time.Hour), + }, nil +} diff --git a/cmd/cliui/main.go b/cmd/cliui/main.go index b7a8e004ce31d..89559f4e59731 100644 --- a/cmd/cliui/main.go +++ b/cmd/cliui/main.go @@ -5,8 +5,10 @@ import ( "errors" "fmt" "io" + "net/url" "os" "strings" + "sync/atomic" "time" "github.com/spf13/cobra" @@ -235,6 +237,37 @@ func main() { }, }) + root.AddCommand(&cobra.Command{ + Use: "git-auth", + RunE: func(cmd *cobra.Command, args []string) error { + var count atomic.Int32 + var githubAuthed atomic.Bool + var gitlabAuthed atomic.Bool + go func() { + time.Sleep(time.Second) + githubAuthed.Store(true) + time.Sleep(time.Second * 2) + gitlabAuthed.Store(true) + }() + return cliui.GitAuth(cmd.Context(), cmd.OutOrStdout(), cliui.GitAuthOptions{ + Fetch: func(ctx context.Context) ([]codersdk.TemplateVersionGitAuth, error) { + count.Add(1) + return []codersdk.TemplateVersionGitAuth{{ + ID: "github", + Type: codersdk.GitProviderGitHub, + Authenticated: githubAuthed.Load(), + AuthenticateURL: "https://example.com/gitauth/github?redirect=" + url.QueryEscape("/gitauth?notify"), + }, { + ID: "gitlab", + Type: codersdk.GitProviderGitLab, + Authenticated: gitlabAuthed.Load(), + AuthenticateURL: "https://example.com/gitauth/gitlab?redirect=" + url.QueryEscape("/gitauth?notify"), + }}, nil + }, + }) + }, + }) + err := root.Execute() if err != nil { _, _ = fmt.Println(err.Error()) diff --git a/coderd/coderdtest/coderdtest.go b/coderd/coderdtest/coderdtest.go index 405c060bf8d3c..2fda13ca03aa5 100644 --- a/coderd/coderdtest/coderdtest.go +++ b/coderd/coderdtest/coderdtest.go @@ -717,6 +717,33 @@ func MustWorkspace(t *testing.T, client *codersdk.Client, workspaceID uuid.UUID) return ws } +// RequestGitAuthCallback makes a request with the proper OAuth2 state cookie +// to the git auth callback endpoint. +func RequestGitAuthCallback(t *testing.T, providerID string, client *codersdk.Client) *http.Response { + client.HTTPClient.CheckRedirect = func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + } + state := "somestate" + oauthURL, err := client.URL.Parse(fmt.Sprintf("/gitauth/%s/callback?code=asd&state=%s", providerID, state)) + require.NoError(t, err) + req, err := http.NewRequestWithContext(context.Background(), "GET", oauthURL.String(), nil) + require.NoError(t, err) + req.AddCookie(&http.Cookie{ + Name: codersdk.OAuth2StateCookie, + Value: state, + }) + req.AddCookie(&http.Cookie{ + Name: codersdk.SessionTokenCookie, + Value: client.SessionToken(), + }) + res, err := client.HTTPClient.Do(req) + require.NoError(t, err) + t.Cleanup(func() { + _ = res.Body.Close() + }) + return res +} + // NewGoogleInstanceIdentity returns a metadata client and ID token validator for faking // instance authentication for Google Cloud. // nolint:revive diff --git a/coderd/templateversions.go b/coderd/templateversions.go index d7455dd40005b..f258f995c0369 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -292,6 +292,14 @@ func (api *API) templateVersionGitAuth(rw http.ResponseWriter, r *http.Request) }) return } + query := redirectURL.Query() + // The frontend uses a BroadcastChannel to notify listening pages for + // Git auth updates if the "notify" query parameter is set. + // + // It's important we do this in the backend, because the same endpoint + // is used for CLI authentication. + query.Add("redirect", "/gitauth?notify") + redirectURL.RawQuery = query.Encode() provider := codersdk.TemplateVersionGitAuth{ ID: config.ID, diff --git a/coderd/templateversions_test.go b/coderd/templateversions_test.go index 00dca0d974b3a..7f9aecbf8e3b1 100644 --- a/coderd/templateversions_test.go +++ b/coderd/templateversions_test.go @@ -485,7 +485,7 @@ func TestTemplateVersionsGitAuth(t *testing.T) { require.False(t, providers[0].Authenticated) // Perform the Git auth callback to authenticate the user... - resp := gitAuthCallback(t, "github", client) + resp := coderdtest.RequestGitAuthCallback(t, "github", client) _ = resp.Body.Close() require.Equal(t, http.StatusTemporaryRedirect, resp.StatusCode) diff --git a/coderd/workspaceagents_test.go b/coderd/workspaceagents_test.go index b4ac803e08c19..110d74bdf8ae1 100644 --- a/coderd/workspaceagents_test.go +++ b/coderd/workspaceagents_test.go @@ -911,7 +911,7 @@ func TestWorkspaceAgentsGitAuth(t *testing.T) { Type: codersdk.GitProviderGitHub, }}, }) - resp := gitAuthCallback(t, "github", client) + resp := coderdtest.RequestGitAuthCallback(t, "github", client) require.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("AuthorizedCallback", func(t *testing.T) { @@ -926,14 +926,14 @@ func TestWorkspaceAgentsGitAuth(t *testing.T) { }}, }) _ = coderdtest.CreateFirstUser(t, client) - resp := gitAuthCallback(t, "github", client) + resp := coderdtest.RequestGitAuthCallback(t, "github", client) require.Equal(t, http.StatusTemporaryRedirect, resp.StatusCode) location, err := resp.Location() require.NoError(t, err) require.Equal(t, "/gitauth", location.Path) // Callback again to simulate updating the token. - resp = gitAuthCallback(t, "github", client) + resp = coderdtest.RequestGitAuthCallback(t, "github", client) require.Equal(t, http.StatusTemporaryRedirect, resp.StatusCode) }) t.Run("ValidateURL", func(t *testing.T) { @@ -983,7 +983,7 @@ func TestWorkspaceAgentsGitAuth(t *testing.T) { agentClient := agentsdk.New(client.URL) agentClient.SetSessionToken(authToken) - resp := gitAuthCallback(t, "github", client) + resp := coderdtest.RequestGitAuthCallback(t, "github", client) require.Equal(t, http.StatusTemporaryRedirect, resp.StatusCode) // If the validation URL says unauthorized, the callback @@ -1063,7 +1063,7 @@ func TestWorkspaceAgentsGitAuth(t *testing.T) { // In the configuration, we set our OAuth provider // to return an expired token. Coder consumes this // and stores it. - resp := gitAuthCallback(t, "github", client) + resp := coderdtest.RequestGitAuthCallback(t, "github", client) require.Equal(t, http.StatusTemporaryRedirect, resp.StatusCode) // Because the token is expired and `NoRefresh` is specified, @@ -1128,7 +1128,7 @@ func TestWorkspaceAgentsGitAuth(t *testing.T) { time.Sleep(250 * time.Millisecond) - resp := gitAuthCallback(t, "github", client) + resp := coderdtest.RequestGitAuthCallback(t, "github", client) require.Equal(t, http.StatusTemporaryRedirect, resp.StatusCode) token = <-tokenChan require.Equal(t, "token", token.Username) @@ -1197,31 +1197,6 @@ func TestWorkspaceAgentReportStats(t *testing.T) { }) } -func gitAuthCallback(t *testing.T, id string, client *codersdk.Client) *http.Response { - client.HTTPClient.CheckRedirect = func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - } - state := "somestate" - oauthURL, err := client.URL.Parse(fmt.Sprintf("/gitauth/%s/callback?code=asd&state=%s", id, state)) - require.NoError(t, err) - req, err := http.NewRequestWithContext(context.Background(), "GET", oauthURL.String(), nil) - require.NoError(t, err) - req.AddCookie(&http.Cookie{ - Name: codersdk.OAuth2StateCookie, - Value: state, - }) - req.AddCookie(&http.Cookie{ - Name: codersdk.SessionTokenCookie, - Value: client.SessionToken(), - }) - res, err := client.HTTPClient.Do(req) - require.NoError(t, err) - t.Cleanup(func() { - _ = res.Body.Close() - }) - return res -} - func TestWorkspaceAgent_LifecycleState(t *testing.T) { t.Parallel() diff --git a/codersdk/workspaceagents.go b/codersdk/workspaceagents.go index 766fdd20500cd..6dd1c1d178f17 100644 --- a/codersdk/workspaceagents.go +++ b/codersdk/workspaceagents.go @@ -293,6 +293,21 @@ func (c *Client) WorkspaceAgentListeningPorts(ctx context.Context, agentID uuid. // type of providers that are supported within Coder. type GitProvider string +func (g GitProvider) Pretty() string { + switch g { + case GitProviderAzureDevops: + return "Azure DevOps" + case GitProviderGitHub: + return "GitHub" + case GitProviderGitLab: + return "GitLab" + case GitProviderBitBucket: + return "Bitbucket" + default: + return string(g) + } +} + const ( GitProviderAzureDevops GitProvider = "azure-devops" GitProviderGitHub GitProvider = "github" diff --git a/site/src/components/GitAuth/GitAuth.tsx b/site/src/components/GitAuth/GitAuth.tsx index 73773f0cb1629..7998044677771 100644 --- a/site/src/components/GitAuth/GitAuth.tsx +++ b/site/src/components/GitAuth/GitAuth.tsx @@ -50,11 +50,6 @@ export const GitAuth: FC = ({ default: throw new Error("invalid git provider: " + type) } - // The Git auth page uses a BroadcastChannel to notify listening - // pages for Git auth updates if the "notify" query parameter is specified. - const redirectURL = `${authenticateURL}?redirect=${encodeURIComponent( - `/gitauth?notify`, - )}` return ( = ({ >
{ event.preventDefault() @@ -72,7 +67,7 @@ export const GitAuth: FC = ({ if (authenticated || authenticateURL === "") { return } - window.open(redirectURL, "_blank", "width=900,height=600") + window.open(authenticateURL, "_blank", "width=900,height=600") }} >