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..0cdbf45839bcf 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,31 @@ func createTestParseResponseWithDefault(defaultValue string) []*proto.Parse_Resp }, }} } + +type oauth2Config struct{} + +func (*oauth2Config) AuthCodeURL(state string, _ ...oauth2.AuthCodeOption) string { + return "/?state=" + url.QueryEscape(state) +} + +func (*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 (*oauth2Config) TokenSource(context.Context, *oauth2.Token) oauth2.TokenSource { + return &oauth2TokenSource{} +} + +type oauth2TokenSource struct{} + +func (*oauth2TokenSource) Token() (*oauth2.Token, error) { + 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..c25da0a84d660 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,41 @@ 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() { + // Sleep to display the loading indicator. + time.Sleep(time.Second) + // Swap to true to display success and move onto GitLab. + githubAuthed.Store(true) + // Show the loading indicator again... + time.Sleep(time.Second * 2) + // Complete the auth! + 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/apidoc/docs.go b/coderd/apidoc/docs.go index d4e1236849534..aa281e9195c09 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": [ @@ -6565,6 +6603,21 @@ const docTemplate = `{ } } }, + "codersdk.GitProvider": { + "type": "string", + "enum": [ + "azure-devops", + "github", + "gitlab", + "bitbucket" + ], + "x-enum-varnames": [ + "GitProviderAzureDevops", + "GitProviderGitHub", + "GitProviderGitLab", + "GitProviderBitBucket" + ] + }, "codersdk.GitSSHKey": { "type": "object", "properties": { @@ -7648,6 +7701,23 @@ const docTemplate = `{ } } }, + "codersdk.TemplateVersionGitAuth": { + "type": "object", + "properties": { + "authenticate_url": { + "type": "string" + }, + "authenticated": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/codersdk.GitProvider" + } + } + }, "codersdk.TemplateVersionParameter": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 894a2d0b6cafd..0f57dc755d0e9 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": [ @@ -5875,6 +5909,16 @@ } } }, + "codersdk.GitProvider": { + "type": "string", + "enum": ["azure-devops", "github", "gitlab", "bitbucket"], + "x-enum-varnames": [ + "GitProviderAzureDevops", + "GitProviderGitHub", + "GitProviderGitLab", + "GitProviderBitBucket" + ] + }, "codersdk.GitSSHKey": { "type": "object", "properties": { @@ -6877,6 +6921,23 @@ } } }, + "codersdk.TemplateVersionGitAuth": { + "type": "object", + "properties": { + "authenticate_url": { + "type": "string" + }, + "authenticated": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/codersdk.GitProvider" + } + } + }, "codersdk.TemplateVersionParameter": { "type": "object", "properties": { diff --git a/coderd/coderd.go b/coderd/coderd.go index bc8d39bb587fd..79306dcd03481 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -489,6 +489,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) @@ -805,12 +806,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/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/database/dbauthz/querier.go b/coderd/database/dbauthz/querier.go index ca2610e52ed63..691f680e42feb 100644 --- a/coderd/database/dbauthz/querier.go +++ b/coderd/database/dbauthz/querier.go @@ -889,6 +889,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) @@ -1103,11 +1125,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 11d03c9be3718..0f7e7c4ffa45d 100644 --- a/coderd/database/dbauthz/querier_test.go +++ b/coderd/database/dbauthz/querier_test.go @@ -736,6 +736,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() { @@ -881,9 +893,13 @@ func (s *MethodTestSuite) TestUser() { s.Run("UpdateGitAuthLink", s.Subtest(func(db database.Store, check *expects) { link := dbgen.GitAuthLink(s.T(), db, database.GitAuthLink{}) check.Args(database.UpdateGitAuthLinkParams{ - ProviderID: link.ProviderID, - UserID: link.UserID, - }).Asserts(link, rbac.ActionUpdate).Returns() + ProviderID: link.ProviderID, + UserID: link.UserID, + OAuthAccessToken: link.OAuthAccessToken, + OAuthRefreshToken: link.OAuthRefreshToken, + OAuthExpiry: link.OAuthExpiry, + UpdatedAt: link.UpdatedAt, + }).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 45cf402a98ac3..16c0fbe5bd3ba 100644 --- a/coderd/database/dbfake/databasefake.go +++ b/coderd/database/dbfake/databasefake.go @@ -3273,6 +3273,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 @@ -4287,9 +4307,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() @@ -4306,8 +4326,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/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 4c2c9da79ab4a..a2849211b60be 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -176,7 +176,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) @@ -190,6 +190,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 354a6ee04c963..c2022fa98f502 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -880,13 +880,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 { @@ -898,8 +898,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, @@ -907,7 +907,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 @@ -3715,7 +3725,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 @@ -3749,13 +3759,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 @@ -3775,13 +3786,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 @@ -3801,13 +3813,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 @@ -3833,13 +3846,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 @@ -3865,6 +3879,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 } @@ -3881,7 +3896,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 @@ -3945,6 +3960,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 } @@ -3960,7 +3976,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) { @@ -3982,6 +3998,7 @@ func (q *sqlQuerier) GetTemplateVersionsCreatedAfter(ctx context.Context, create &i.Readme, &i.JobID, &i.CreatedBy, + pq.Array(&i.GitAuthProviders), ); err != nil { return nil, err } @@ -4010,7 +4027,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 { @@ -4048,6 +4065,7 @@ func (q *sqlQuerier) InsertTemplateVersion(ctx context.Context, arg InsertTempla &i.Readme, &i.JobID, &i.CreatedBy, + pq.Array(&i.GitAuthProviders), ) return i, err } @@ -4094,6 +4112,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/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/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/coderd/gitauth/config.go b/coderd/gitauth/config.go index 91fe41ee51db5..69062a1801e1f 100644 --- a/coderd/gitauth/config.go +++ b/coderd/gitauth/config.go @@ -34,14 +34,14 @@ type Config struct { ValidateURL string } -// ConvertConfig converts the YAML configuration entry to the -// parsed and ready-to-consume provider type. +// ConvertConfig converts the SDK configuration entry format +// to the parsed and ready-to-consume in coderd provider type. func ConvertConfig(entries []codersdk.GitAuthConfig, accessURL *url.URL) ([]*Config, error) { ids := map[string]struct{}{} configs := []*Config{} for _, entry := range entries { var typ codersdk.GitProvider - switch entry.Type { + switch codersdk.GitProvider(entry.Type) { case codersdk.GitProviderAzureDevops: typ = codersdk.GitProviderAzureDevops case codersdk.GitProviderBitBucket: 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/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index c1f7045921baa..9ca1da8bf8372 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 } @@ -307,7 +309,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 { @@ -316,7 +318,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 { @@ -325,7 +327,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 { @@ -812,6 +814,27 @@ 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) { + completedError = sql.NullString{ + String: fmt.Sprintf("git auth provider %q is not configured", gitAuthProvider), + Valid: true, + } + break + } + } + + 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(), @@ -819,6 +842,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 83f661563f60d..bcc0d6b0ad93a 100644 --- a/coderd/provisionerdserver/provisionerdserver_test.go +++ b/coderd/provisionerdserver/provisionerdserver_test.go @@ -736,10 +736,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, }) @@ -752,19 +758,31 @@ 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() { + _, 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"}, + }, }, - }, - }) + }) + require.NoError(t, err) + } + 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"} + 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 04d7e25070e1f..160f56e18b30a 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,107 @@ 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.TemplateVersionGitAuth +// @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.TemplateVersionGitAuth, 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 + } + 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, + 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 diff --git a/coderd/templateversions_test.go b/coderd/templateversions_test.go index 9e7a7d46bda9f..7f9aecbf8e3b1 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" @@ -434,6 +436,67 @@ func TestTemplateVersionParameters(t *testing.T) { }) } +func TestTemplateVersionsGitAuth(t *testing.T) { + t.Parallel() + t.Run("Empty", func(t *testing.T) { + t.Parallel() + 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() + + _, err := client.TemplateVersionGitAuth(ctx, version.ID) + require.NoError(t, err) + }) + 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 := coderdtest.RequestGitAuthCallback(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) { t.Parallel() t.Run("ListRunning", func(t *testing.T) { diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index 649fb37f0664d..41d084b2bf865 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -1313,14 +1313,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, @@ -1328,49 +1343,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 @@ -1392,7 +1393,7 @@ func validateGitToken(ctx context.Context, validateURL, token string) (bool, err } if res.StatusCode != http.StatusOK { data, _ := io.ReadAll(res.Body) - return false, xerrors.Errorf("git token validation failed: status %d: body: %s", res.StatusCode, data) + return false, xerrors.Errorf("status %d: body: %s", res.StatusCode, data) } return true, nil } @@ -1459,7 +1460,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(), @@ -1485,8 +1486,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/coderd/workspaceagents_test.go b/coderd/workspaceagents_test.go index b4ac803e08c19..6ccea696b514f 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 @@ -1005,7 +1005,7 @@ func TestWorkspaceAgentsGitAuth(t *testing.T) { var apiError *codersdk.Error require.ErrorAs(t, err, &apiError) require.Equal(t, http.StatusInternalServerError, apiError.StatusCode()) - require.Equal(t, "git token validation failed: status 403: body: Something went wrong!", apiError.Detail) + require.Equal(t, "validate git auth token: status 403: body: Something went wrong!", apiError.Detail) }) t.Run("ExpiredNoRefresh", func(t *testing.T) { @@ -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/templateversions.go b/codersdk/templateversions.go index 18b7a43f37ae4..a915f7e218615 100644 --- a/codersdk/templateversions.go +++ b/codersdk/templateversions.go @@ -24,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 ( @@ -108,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) ([]TemplateVersionGitAuth, 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 []TemplateVersionGitAuth + 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/codersdk/workspaceagents.go b/codersdk/workspaceagents.go index 9364783d29953..6dd1c1d178f17 100644 --- a/codersdk/workspaceagents.go +++ b/codersdk/workspaceagents.go @@ -291,12 +291,26 @@ 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 +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 = "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/admin/audit-logs.md b/docs/admin/audit-logs.md index 762b2f3762ca7..5b9355b391e3d 100644 --- a/docs/admin/audit-logs.md +++ b/docs/admin/audit-logs.md @@ -16,7 +16,7 @@ We track the following resources: | GitSSHKey
create |
FieldTracked
created_atfalse
private_keytrue
public_keytrue
updated_atfalse
user_idtrue
| | License
create, delete |
FieldTracked
exptrue
idfalse
jwtfalse
uploaded_attrue
uuidtrue
| | Template
write, delete |
FieldTracked
active_version_idtrue
allow_user_cancel_workspace_jobstrue
created_atfalse
created_bytrue
default_ttltrue
deletedfalse
descriptiontrue
display_nametrue
group_acltrue
icontrue
idtrue
is_privatetrue
min_autostart_intervaltrue
nametrue
organization_idfalse
provisionertrue
updated_atfalse
user_acltrue
| -| TemplateVersion
create, write |
FieldTracked
created_atfalse
created_bytrue
idtrue
job_idfalse
nametrue
organization_idfalse
readmetrue
template_idtrue
updated_atfalse
| +| TemplateVersion
create, write |
FieldTracked
created_atfalse
created_bytrue
git_auth_providersfalse
idtrue
job_idfalse
nametrue
organization_idfalse
readmetrue
template_idtrue
updated_atfalse
| | User
create, write, delete |
FieldTracked
avatar_urlfalse
created_atfalse
deletedtrue
emailtrue
hashed_passwordtrue
idtrue
last_seen_atfalse
login_typefalse
rbac_rolestrue
statustrue
updated_atfalse
usernametrue
| | Workspace
create, write, delete |
FieldTracked
autostart_scheduletrue
created_atfalse
deletedfalse
idtrue
last_used_atfalse
nametrue
organization_idfalse
owner_idtrue
template_idtrue
ttltrue
updated_atfalse
| | WorkspaceBuild
start, stop |
FieldTracked
build_numberfalse
created_atfalse
daily_costfalse
deadlinefalse
idfalse
initiator_idfalse
job_idfalse
provisioner_statefalse
reasonfalse
template_version_idtrue
transitionfalse
updated_atfalse
workspace_idfalse
| diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 948a0e6304197..cad32370ad2c5 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -2944,6 +2944,23 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `type` | string | false | | | | `validate_url` | string | false | | | +## codersdk.GitProvider + +```json +"azure-devops" +``` + +### Properties + +#### Enumerated Values + +| Value | +| -------------- | +| `azure-devops` | +| `github` | +| `gitlab` | +| `bitbucket` | + ## codersdk.GitSSHKey ```json @@ -4570,6 +4587,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": "azure-devops" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ------------------ | -------------------------------------------- | -------- | ------------ | ----------- | +| `authenticate_url` | string | false | | | +| `authenticated` | boolean | false | | | +| `id` | string | false | | | +| `type` | [codersdk.GitProvider](#codersdkgitprovider) | false | | | + ## codersdk.TemplateVersionParameter ```json diff --git a/docs/api/templates.md b/docs/api/templates.md index b48b805d7c58e..acdd8df3abab2 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -1722,6 +1722,69 @@ 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": "azure-devops" + } +] +``` + +### 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` | [codersdk.GitProvider](schemas.md#codersdkgitprovider) | false | | | + +#### Enumerated Values + +| Property | Value | +| -------- | -------------- | +| `type` | `azure-devops` | +| `type` | `github` | +| `type` | `gitlab` | +| `type` | `bitbucket` | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## Get logs by template version ### Code samples diff --git a/enterprise/audit/table.go b/enterprise/audit/table.go index 239aecfd3017e..ce47a56e40273 100644 --- a/enterprise/audit/table.go +++ b/enterprise/audit/table.go @@ -72,15 +72,16 @@ var AuditableResources = auditMap(map[any]map[string]Action{ "allow_user_cancel_workspace_jobs": ActionTrack, }, &database.TemplateVersion{}: { - "id": ActionTrack, - "template_id": ActionTrack, - "organization_id": ActionIgnore, // Never changes. - "created_at": ActionIgnore, // Never changes, but is implicit and not helpful in a diff. - "updated_at": ActionIgnore, // Changes, but is implicit and not helpful in a diff. - "name": ActionTrack, - "readme": ActionTrack, - "job_id": ActionIgnore, // Not helpful in a diff because jobs aren't tracked in audit logs. - "created_by": ActionTrack, + "id": ActionTrack, + "template_id": ActionTrack, + "organization_id": ActionIgnore, // Never changes. + "created_at": ActionIgnore, // Never changes, but is implicit and not helpful in a diff. + "updated_at": ActionIgnore, // Changes, but is implicit and not helpful in a diff. + "name": ActionTrack, + "readme": ActionTrack, + "job_id": ActionIgnore, // Not helpful in a diff because jobs aren't tracked in audit logs. + "created_by": ActionTrack, + "git_auth_providers": ActionIgnore, // Not helpful because this can only change when new versions are added. }, &database.User{}: { "id": ActionTrack, diff --git a/provisioner/terraform/executor.go b/provisioner/terraform/executor.go index 7d2335beb071f..f273e5245df4f 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) (*State, 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) (*State, 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 + converted := &State{} 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..e370d7371541c 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 State 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) (*State, 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,30 @@ 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 &State{ + 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..e7f0d1924aee9 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/provisionerd.go b/provisionerd/provisionerd.go index 78dcb121da7cd..dd11099e2de0f 100644 --- a/provisionerd/provisionerd.go +++ b/provisionerd/provisionerd.go @@ -172,6 +172,11 @@ func (p *Server) connect(ctx context.Context) { // An exponential back-off occurs when the connection is failing to dial. // This is to prevent server spam in case of a coderd outage. for retrier := retry.New(50*time.Millisecond, 10*time.Second); retrier.Wait(ctx); { + // It's possible for the provisioner daemon to be shut down + // before the wait is complete! + if p.isClosed() { + return + } client, err := p.clientDialer(ctx) if err != nil { if errors.Is(err, context.Canceled) { diff --git a/provisionerd/runner/runner.go b/provisionerd/runner/runner.go index b1914c2247835..0b0f837607994 100644 --- a/provisionerd/runner/runner.go +++ b/provisionerd/runner/runner.go @@ -600,7 +600,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, }) @@ -615,7 +615,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, }) @@ -627,9 +627,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 @@ -680,16 +681,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() @@ -704,7 +711,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() { @@ -733,13 +740,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: @@ -760,11 +767,11 @@ 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 { - return nil, nil, xerrors.Errorf(`rich parameters can't be used together with legacy parameters, set the coder provider flag "feature_use_managed_variables = true" to enable managed variables`) + return nil, xerrors.Errorf(`rich parameters can't be used together with legacy parameters, set the coder provider flag "feature_use_managed_variables = true" to enable managed variables`) } r.logger.Info(context.Background(), "parse dry-run provision successful", @@ -773,9 +780,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()) } } @@ -814,7 +825,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(), @@ -828,7 +839,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 b30dd23392485..51f3a475f6022 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 { @@ -1983,7 +2038,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) } @@ -1996,7 +2051,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 { @@ -2009,7 +2064,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 { @@ -2090,7 +2145,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) } @@ -2103,7 +2158,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 { @@ -2116,7 +2171,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 { @@ -2149,12 +2204,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) } @@ -2167,7 +2223,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 { @@ -2180,7 +2236,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 { @@ -2211,6 +2267,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 @@ -2223,7 +2286,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) } @@ -2236,7 +2299,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 { @@ -2249,7 +2312,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 { @@ -2275,7 +2338,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) } @@ -2288,7 +2351,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 { @@ -2301,7 +2364,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 { @@ -2320,7 +2383,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) } @@ -2333,7 +2396,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 { @@ -2346,7 +2409,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 { @@ -2404,17 +2467,18 @@ 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() { *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) } @@ -2427,7 +2491,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 { @@ -2440,7 +2504,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 { @@ -2471,6 +2535,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 @@ -2493,7 +2564,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) } @@ -2506,7 +2577,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 { @@ -2519,7 +2590,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 { @@ -2698,199 +2769,211 @@ 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, 0x97, 0x0b, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x1a, 0xa1, 0x03, 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, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 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, 0x91, 0x0c, 0x0a, 0x09, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0xa1, 0x03, 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, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x29, 0x0a, 0x10, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 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, 0xbb, 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, - 0x12, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x70, + 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, @@ -2939,7 +3022,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 @@ -2959,25 +3042,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 @@ -2988,39 +3072,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() } @@ -3174,7 +3259,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: @@ -3186,7 +3271,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: @@ -3198,7 +3283,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: @@ -3210,7 +3295,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: @@ -3222,7 +3307,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: @@ -3234,6 +3319,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 @@ -3245,7 +3342,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 @@ -3257,7 +3354,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 @@ -3269,7 +3366,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 @@ -3281,7 +3378,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 @@ -3293,7 +3390,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 @@ -3305,7 +3402,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 @@ -3317,7 +3414,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 @@ -3329,7 +3426,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 @@ -3341,7 +3438,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 @@ -3353,7 +3450,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 @@ -3365,7 +3462,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 @@ -3377,7 +3474,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 @@ -3390,20 +3487,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), } @@ -3413,7 +3510,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 6b935f27db531..826b5ef5f11e8 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,8 +240,9 @@ message Provision { message Plan { Config config = 1; repeated ParameterValue parameter_values = 2; - repeated RichParameterValue rich_parameter_values = 3; - repeated VariableValue variable_values = 4; + repeated RichParameterValue rich_parameter_values = 3; + repeated VariableValue variable_values = 4; + repeated GitAuthProvider git_auth_providers = 5; } message Apply { @@ -257,7 +263,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 { 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/api/api.ts b/site/src/api/api.ts index 303042f2ccf5e..ac99f7b3eb2b5 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -295,6 +295,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 cf36bd98bff3b..8dab0662f63e2 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -754,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 @@ -1138,6 +1146,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..cda012212d14d --- /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({}) +GithubAuthenticated.args = { + type: "github", + authenticated: true, +} + +export const GitlabNotAuthenticated = Template.bind({}) +GitlabNotAuthenticated.args = { + type: "gitlab", + authenticated: false, +} + +export const GitlabAuthenticated = Template.bind({}) +GitlabAuthenticated.args = { + type: "gitlab", + authenticated: true, +} + +export const AzureDevOpsNotAuthenticated = Template.bind({}) +AzureDevOpsNotAuthenticated.args = { + type: "azure-devops", + authenticated: false, +} + +export const AzureDevOpsAuthenticated = Template.bind({}) +AzureDevOpsAuthenticated.args = { + type: "azure-devops", + authenticated: true, +} + +export const BitbucketNotAuthenticated = Template.bind({}) +BitbucketNotAuthenticated.args = { + type: "bitbucket", + authenticated: false, +} + +export const BitbucketAuthenticated = Template.bind({}) +BitbucketAuthenticated.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..7998044677771 --- /dev/null +++ b/site/src/components/GitAuth/GitAuth.tsx @@ -0,0 +1,115 @@ +import Button from "@material-ui/core/Button" +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" +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 { FC } from "react" + +export interface GitAuthProps { + type: TypesGen.GitProvider + authenticated: boolean + authenticateURL: string + error?: string +} + +export const GitAuth: FC = ({ + type, + authenticated, + authenticateURL, + error, +}) => { + const styles = useStyles({ + error: typeof error !== "undefined", + }) + + let prettyName: string + let Icon: (props: SvgIconProps) => JSX.Element + switch (type) { + case "azure-devops": + prettyName = "Azure DevOps" + Icon = AzureDevOpsIcon + break + case "bitbucket": + prettyName = "Bitbucket" + Icon = BitbucketIcon + break + case "github": + prettyName = "GitHub" + Icon = GitHub + break + case "gitlab": + prettyName = "GitLab" + Icon = GitlabIcon + break + default: + throw new Error("invalid git provider: " + type) + } + + return ( + + + + ) +} + +const useStyles = makeStyles< + Theme, + { + error: boolean + } +>((theme) => ({ + link: { + textDecoration: "none", + }, + root: { + padding: 4, + display: "flex", + gap: 12, + alignItems: "center", + textAlign: "left", + }, + button: { + height: "unset", + border: ({ error }) => + error ? `1px solid ${theme.palette.error.main}` : "unset", + }, + icon: { + width: 32, + height: 32, + }, +})) 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.test.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx index 0cabc3e6ed271..c2f8e24fdba75 100644 --- a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx +++ b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx @@ -12,6 +12,7 @@ import { MockTemplateVersionParameter1, MockTemplateVersionParameter2, MockTemplateVersionParameter3, + MockTemplateVersionGitAuth, } from "testHelpers/entities" import { renderWithAuth } from "testHelpers/renderHelpers" import CreateWorkspacePage from "./CreateWorkspacePage" @@ -38,6 +39,17 @@ const renderCreateWorkspacePage = () => { }) } +Object.defineProperty(window, "BroadcastChannel", { + value: class { + addEventListener() { + // noop + } + close() { + // noop + } + }, +}) + describe("CreateWorkspacePage", () => { it("renders", async () => { jest @@ -210,4 +222,24 @@ describe("CreateWorkspacePage", () => { const validationError = await screen.findByText(validationPatternNotMatched) expect(validationError).toBeInTheDocument() }) + + it("gitauth: errors if unauthenticated and submits", async () => { + jest + .spyOn(API, "getTemplateVersionGitAuth") + .mockResolvedValueOnce([MockTemplateVersionGitAuth]) + + await waitFor(() => renderCreateWorkspacePage()) + + const nameField = await screen.findByLabelText(nameLabelText) + + // have to use fireEvent b/c userEvent isn't cleaning up properly between tests + fireEvent.change(nameField, { + target: { value: "test" }, + }) + + const submitButton = screen.getByText(createWorkspaceText) + await userEvent.click(submitButton) + + await screen.findByText("You must authenticate to create a workspace!") + }) }) diff --git a/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx b/site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx index 0dc0f65482548..5a9e63edac6cb 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,14 @@ 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.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 959252a1b9140..e4876bff3e28b 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" @@ -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 @@ -55,6 +58,15 @@ export const CreateWorkspacePageView: FC< props.templateParameters, props.defaultParameterValues, ) + const [gitAuthErrors, setGitAuthErrors] = useState>({}) + useEffect(() => { + // templateGitAuth is refreshed automatically using a BroadcastChannel + // which may change the `authenticated` property. + // + // If the provider becomes authenticated, we want the error message + // to disappear. + setGitAuthErrors({}) + }, [props.templateGitAuth]) const { t } = useTranslation("createWorkspacePage") @@ -75,6 +87,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") } @@ -142,6 +168,20 @@ export const CreateWorkspacePageView: FC< } /> )} + {Boolean( + props.createWorkspaceErrors[ + CreateWorkspaceErrors.GET_TEMPLATE_GITAUTH_ERROR + ], + ) && ( + + )} ) } @@ -220,6 +260,37 @@ export const CreateWorkspacePageView: FC< )} + {/* Template git auth */} + {props.templateGitAuth && props.templateGitAuth.length > 0 && ( +
+
+

+ Git Authentication +

+

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

+
+ + + {props.templateGitAuth.map((auth, index) => ( + + ))} + +
+ )} + {/* Template params */} {props.templateSchema && props.templateSchema.length > 0 && (
diff --git a/site/src/pages/GitAuthPage/GitAuthPage.tsx b/site/src/pages/GitAuthPage/GitAuthPage.tsx index 27eacef1bd257..063f7c6b97ff9 100644 --- a/site/src/pages/GitAuthPage/GitAuthPage.tsx +++ b/site/src/pages/GitAuthPage/GitAuthPage.tsx @@ -2,11 +2,21 @@ 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 { FC, useEffect } from "react" import { Link as RouterLink } from "react-router-dom" +import { REFRESH_GITAUTH_BROADCAST_CHANNEL } from "xServices/createWorkspace/createWorkspaceXService" const GitAuthPage: FC = () => { const styles = useStyles() + useEffect(() => { + // This is used to notify the parent window that the Git auth token has been refreshed. + // It's critical in the create workspace flow! + // eslint-disable-next-line compat/compat -- It actually is supported... not sure why it's complaining. + const bc = new BroadcastChannel(REFRESH_GITAUTH_BROADCAST_CHANNEL) + // The message doesn't matter, any message refreshes the page! + bc.postMessage("noop") + window.close() + }, []) return ( diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 8876bd169a7a2..a541f5cbc3ea5 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -1390,3 +1390,10 @@ export const mockParameterSchema = ( ...partial, } } + +export const MockTemplateVersionGitAuth: TypesGen.TemplateVersionGitAuth = { + id: "github", + type: "github", + authenticate_url: "https://example.com/gitauth/github", + authenticated: false, +} diff --git a/site/src/testHelpers/handlers.ts b/site/src/testHelpers/handlers.ts index 0dd0f2d0fe848..53de504fca3df 100644 --- a/site/src/testHelpers/handlers.ts +++ b/site/src/testHelpers/handlers.ts @@ -89,6 +89,18 @@ export const handlers = [ ) }, ), + rest.get( + "/api/v2/templateversions/:templateVersionId/rich-parameters", + async (req, res, ctx) => { + return res(ctx.status(200), ctx.json([])) + }, + ), + rest.get( + "/api/v2/templateversions/:templateVersionId/gitauth", + async (req, res, ctx) => { + return res(ctx.status(200), ctx.json([])) + }, + ), rest.get( "api/v2/organizations/:organizationId/templates/:templateName/versions/:templateVersionName", async (req, res, ctx) => { diff --git a/site/src/xServices/createWorkspace/createWorkspaceXService.ts b/site/src/xServices/createWorkspace/createWorkspaceXService.ts index 433c341b3c41c..1c8ea33dff7e4 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,12 +10,15 @@ import { CreateWorkspaceRequest, ParameterSchema, Template, + TemplateVersionGitAuth, TemplateVersionParameter, User, Workspace, } from "api/typesGenerated" import { assign, createMachine } from "xstate" +export const REFRESH_GITAUTH_BROADCAST_CHANNEL = "gitauth_refresh" + type CreateWorkspaceContext = { organizationId: string owner: User | null @@ -23,11 +27,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 @@ -44,247 +50,305 @@ 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[] - } - 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: "checkingPermissions", - }, - onError: { - actions: ["assignGetTemplateParametersError"], - target: "error", + gettingTemplateParameters: { + entry: "clearGetTemplateParametersError", + invoke: { + src: "getTemplateParameters", + onDone: { + actions: ["assignTemplateParameters"], + target: "checkingPermissions", + }, + onError: { + actions: ["assignGetTemplateParametersError"], + target: "error", + }, }, }, - }, - checkingPermissions: { - entry: "clearCheckPermissionsError", - invoke: { - src: "checkPermissions", - id: "checkPermissions", - onDone: { - actions: "assignPermissions", - target: "fillingParams", + checkingPermissions: { + entry: "clearCheckPermissionsError", + invoke: { + src: "checkPermissions", + id: "checkPermissions", + onDone: { + actions: "assignPermissions", + target: "gettingTemplateGitAuth", + }, + onError: { + actions: ["assignCheckPermissionsError"], + }, }, - 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) => { + // eslint-disable-next-line compat/compat -- It actually is supported... not sure why eslint is complaining. + 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), - getTemplateParameters: (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 getTemplateVersionRichParameters( - selectedTemplate.active_version_id, - ) - }, - getTemplateSchema: (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 getTemplateVersionSchema(selectedTemplate.active_version_id) - }, - checkPermissions: async (context) => { - if (!context.organizationId) { - throw new Error("No organization ID") - } + return getTemplateVersionRichParameters( + selectedTemplate.active_version_id, + ) + }, + getTemplateSchema: (context) => { + const { selectedTemplate } = context + + if (!selectedTemplate) { + throw new Error("No selected template") + } - // 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: "*", + 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: "*", + }, + 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, - }), + }, + 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, + }), + }, }, - }, -) + )