From df9ddf38c299b17d5bfda3d120efcf28dacc22f7 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 18 Jan 2023 11:42:53 +0100 Subject: [PATCH 01/15] WIP --- cli/create.go | 70 ++++++++++++++++++------- cli/testdata/coder_create_--help.golden | 3 ++ cli/testdata/coder_update_--help.golden | 13 +++-- cli/update.go | 31 +++++++---- 4 files changed, 84 insertions(+), 33 deletions(-) diff --git a/cli/create.go b/cli/create.go index 854ba78565c51..1af0ef05385c4 100644 --- a/cli/create.go +++ b/cli/create.go @@ -1,6 +1,7 @@ package cli import ( + "context" "fmt" "io" "time" @@ -17,11 +18,12 @@ import ( func create() *cobra.Command { var ( - parameterFile string - templateName string - startAt string - stopAfter time.Duration - workspaceName string + parameterFile string + richParameterFile string + templateName string + startAt string + stopAfter time.Duration + workspaceName string ) cmd := &cobra.Command{ Annotations: workspaceCommand, @@ -121,7 +123,7 @@ func create() *cobra.Command { schedSpec = ptr.Ref(sched.String()) } - parameters, err := prepWorkspaceBuild(cmd, client, prepWorkspaceBuildArgs{ + buildParams, err := prepWorkspaceBuild(cmd, client, prepWorkspaceBuildArgs{ Template: template, ExistingParams: []codersdk.Parameter{}, ParameterFile: parameterFile, @@ -140,11 +142,12 @@ func create() *cobra.Command { } workspace, err := client.CreateWorkspace(cmd.Context(), organization.ID, codersdk.Me, codersdk.CreateWorkspaceRequest{ - TemplateID: template.ID, - Name: workspaceName, - AutostartSchedule: schedSpec, - TTLMillis: ptr.Ref(stopAfter.Milliseconds()), - ParameterValues: parameters, + TemplateID: template.ID, + Name: workspaceName, + AutostartSchedule: schedSpec, + TTLMillis: ptr.Ref(stopAfter.Milliseconds()), + ParameterValues: buildParams.parameters, + RichParameterValues: buildParams.richParameters, }) if err != nil { return err @@ -163,26 +166,54 @@ func create() *cobra.Command { cliui.AllowSkipPrompt(cmd) cliflag.StringVarP(cmd.Flags(), &templateName, "template", "t", "CODER_TEMPLATE_NAME", "", "Specify a template name.") cliflag.StringVarP(cmd.Flags(), ¶meterFile, "parameter-file", "", "CODER_PARAMETER_FILE", "", "Specify a file path with parameter values.") + cliflag.StringVarP(cmd.Flags(), &richParameterFile, "rich-parameter-file", "", "CODER_RICH_PARAMETER_FILE", "", "Specify a file path with values for rich parameters defined in the template.") cliflag.StringVarP(cmd.Flags(), &startAt, "start-at", "", "CODER_WORKSPACE_START_AT", "", "Specify the workspace autostart schedule. Check `coder schedule start --help` for the syntax.") cliflag.DurationVarP(cmd.Flags(), &stopAfter, "stop-after", "", "CODER_WORKSPACE_STOP_AFTER", 8*time.Hour, "Specify a duration after which the workspace should shut down (e.g. 8h).") return cmd } type prepWorkspaceBuildArgs struct { - Template codersdk.Template - ExistingParams []codersdk.Parameter - ParameterFile string - NewWorkspaceName string + Template codersdk.Template + ExistingParams []codersdk.Parameter + ParameterFile string + ExistingRichParams []codersdk.WorkspaceBuildParameter + RichParameterFile string + NewWorkspaceName string +} + +type buildParameters struct { + // Parameters contains legacy parameters stored in /parameters. + parameters []codersdk.CreateParameterRequest + // Rich parameters stores values for build parameters annotated with description, icon, type, etc. + richParameters []codersdk.WorkspaceBuildParameter } // prepWorkspaceBuild will ensure a workspace build will succeed on the latest template version. -// Any missing params will be prompted to the user. -func prepWorkspaceBuild(cmd *cobra.Command, client *codersdk.Client, args prepWorkspaceBuildArgs) ([]codersdk.CreateParameterRequest, error) { +// Any missing params will be prompted to the user. It supports legacy and rich parameters. +func prepWorkspaceBuild(cmd *cobra.Command, client *codersdk.Client, args prepWorkspaceBuildArgs) (*buildParameters, error) { ctx := cmd.Context() templateVersion, err := client.TemplateVersion(ctx, args.Template.ActiveVersionID) if err != nil { return nil, err } + + legacyParameters, err := prepLegacyParameters(ctx, cmd, client, templateVersion, args) + if err != nil { + return nil, err + } + + richParameters, err := prepRichParameters() + if err != nil { + return nil, err + } + + return &buildParameters{ + parameters: legacyParameters, + richParameters: richParameters, + }, nil +} + +func prepLegacyParameters(ctx context.Context, cmd *cobra.Command, client *codersdk.Client, templateVersion codersdk.TemplateVersion, args prepWorkspaceBuildArgs) ([]codersdk.CreateParameterRequest, error) { parameterSchemas, err := client.TemplateVersionSchema(ctx, templateVersion.ID) if err != nil { return nil, err @@ -278,6 +309,9 @@ PromptParamLoop: if err != nil { return nil, err } - return parameters, nil } + +func prepRichParameters() ([]codersdk.WorkspaceBuildParameter, error) { + return nil, nil +} diff --git a/cli/testdata/coder_create_--help.golden b/cli/testdata/coder_create_--help.golden index 1d4d1ea2bf25a..52aaff8d1a053 100644 --- a/cli/testdata/coder_create_--help.golden +++ b/cli/testdata/coder_create_--help.golden @@ -7,6 +7,9 @@ Flags: -h, --help help for create --parameter-file string Specify a file path with parameter values. Consumes $CODER_PARAMETER_FILE + --rich-parameter-file string Specify a file path with values for rich + parameters defined in the template. + Consumes $CODER_RICH_PARAMETER_FILE --start-at coder schedule start --help Specify the workspace autostart schedule. Check coder schedule start --help for the syntax. Consumes $CODER_WORKSPACE_START_AT diff --git a/cli/testdata/coder_update_--help.golden b/cli/testdata/coder_update_--help.golden index b6c46a328b24e..3f037ec8348e8 100644 --- a/cli/testdata/coder_update_--help.golden +++ b/cli/testdata/coder_update_--help.golden @@ -4,11 +4,14 @@ Usage: coder update [flags] Flags: - --always-prompt Always prompt all parameters. Does not pull parameter values - from existing workspace - -h, --help help for update - --parameter-file string Specify a file path with parameter values. - Consumes $CODER_PARAMETER_FILE + --always-prompt Always prompt all parameters. Does not pull parameter + values from existing workspace + -h, --help help for update + --parameter-file string Specify a file path with parameter values. + Consumes $CODER_PARAMETER_FILE + --rich-parameter-file string Specify a file path with values for rich parameters + defined in the template. + Consumes $CODER_RICH_PARAMETER_FILE Global Flags: --global-config coder Path to the global coder config directory. diff --git a/cli/update.go b/cli/update.go index d419cac9389c4..7ecdbd1bc850f 100644 --- a/cli/update.go +++ b/cli/update.go @@ -11,8 +11,9 @@ import ( func update() *cobra.Command { var ( - parameterFile string - alwaysPrompt bool + parameterFile string + richParameterFile string + alwaysPrompt bool ) cmd := &cobra.Command{ @@ -39,27 +40,36 @@ func update() *cobra.Command { } var existingParams []codersdk.Parameter + var existingRichParams []codersdk.WorkspaceBuildParameter if !alwaysPrompt { existingParams, err = client.Parameters(cmd.Context(), codersdk.ParameterWorkspace, workspace.ID) if err != nil { return nil } + + existingRichParams, err = client.WorkspaceBuildParameters(cmd.Context(), workspace.LatestBuild.ID) + if err != nil { + return nil + } } - parameters, err := prepWorkspaceBuild(cmd, client, prepWorkspaceBuildArgs{ - Template: template, - ExistingParams: existingParams, - ParameterFile: parameterFile, - NewWorkspaceName: workspace.Name, + buildParams, err := prepWorkspaceBuild(cmd, client, prepWorkspaceBuildArgs{ + Template: template, + ExistingParams: existingParams, + ParameterFile: parameterFile, + ExistingRichParams: existingRichParams, + RichParameterFile: richParameterFile, + NewWorkspaceName: workspace.Name, }) if err != nil { return nil } build, err := client.CreateWorkspaceBuild(cmd.Context(), workspace.ID, codersdk.CreateWorkspaceBuildRequest{ - TemplateVersionID: template.ActiveVersionID, - Transition: workspace.LatestBuild.Transition, - ParameterValues: parameters, + TemplateVersionID: template.ActiveVersionID, + Transition: workspace.LatestBuild.Transition, + ParameterValues: buildParams.parameters, + RichParameterValues: buildParams.richParameters, }) if err != nil { return err @@ -82,5 +92,6 @@ func update() *cobra.Command { cmd.Flags().BoolVar(&alwaysPrompt, "always-prompt", false, "Always prompt all parameters. Does not pull parameter values from existing workspace") cliflag.StringVarP(cmd.Flags(), ¶meterFile, "parameter-file", "", "CODER_PARAMETER_FILE", "", "Specify a file path with parameter values.") + cliflag.StringVarP(cmd.Flags(), &richParameterFile, "rich-parameter-file", "", "CODER_RICH_PARAMETER_FILE", "", "Specify a file path with values for rich parameters defined in the template.") return cmd } From e8e2e5328bbed8c38538190de6af150543f703fa Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 18 Jan 2023 13:24:51 +0100 Subject: [PATCH 02/15] WIP --- cli/cliui/parameter.go | 53 ++++++++++++++++++++++++++++ cli/create.go | 78 ++++++++++++++++++++++++++++-------------- cli/parameter.go | 27 +++++++++++++-- 3 files changed, 131 insertions(+), 27 deletions(-) diff --git a/cli/cliui/parameter.go b/cli/cliui/parameter.go index 28e0c08e785ce..2519b7cc48e50 100644 --- a/cli/cliui/parameter.go +++ b/cli/cliui/parameter.go @@ -60,3 +60,56 @@ func ParameterSchema(cmd *cobra.Command, parameterSchema codersdk.ParameterSchem return value, nil } + +func RichParameter(cmd *cobra.Command, templateVersionParameter codersdk.TemplateVersionParameter) (string, error) { + _, _ = fmt.Fprintln(cmd.OutOrStdout(), Styles.Bold.Render(templateVersionParameter.Name)) + if templateVersionParameter.Description != "" { + _, _ = fmt.Fprintln(cmd.OutOrStdout(), " "+strings.TrimSpace(strings.Join(strings.Split(templateVersionParameter.Description, "\n"), "\n "))+"\n") + } + + // TODO Implement full validation and show descriptions. + var err error + var value string + if len(templateVersionParameter.Options) > 0 { + // Move the cursor up a single line for nicer display! + _, _ = fmt.Fprint(cmd.OutOrStdout(), "\033[1A") + value, err = Select(cmd, SelectOptions{ + Options: templateVersionParameterOptionValues(templateVersionParameter), + Default: templateVersionParameter.DefaultValue, + HideSearch: true, + }) + if err == nil { + _, _ = fmt.Fprintln(cmd.OutOrStdout()) + _, _ = fmt.Fprintln(cmd.OutOrStdout(), " "+Styles.Prompt.String()+Styles.Field.Render(value)) + } + } else { + text := "Enter a value" + if templateVersionParameter.DefaultValue != "" { + text += fmt.Sprintf(" (default: %q)", templateVersionParameter.DefaultValue) + } + text += ":" + + value, err = Prompt(cmd, PromptOptions{ + Text: Styles.Bold.Render(text), + }) + value = strings.TrimSpace(value) + } + if err != nil { + return "", err + } + + // If they didn't specify anything, use the default value if set. + if len(templateVersionParameter.Options) == 0 && value == "" { + value = templateVersionParameter.DefaultValue + } + + return value, nil +} + +func templateVersionParameterOptionValues(parameter codersdk.TemplateVersionParameter) []string { + var options []string + for _, opt := range parameter.Options { + options = append(options, opt.Value) + } + return options +} diff --git a/cli/create.go b/cli/create.go index 1af0ef05385c4..b1fec6eb19446 100644 --- a/cli/create.go +++ b/cli/create.go @@ -1,7 +1,6 @@ package cli import ( - "context" "fmt" "io" "time" @@ -197,23 +196,7 @@ func prepWorkspaceBuild(cmd *cobra.Command, client *codersdk.Client, args prepWo return nil, err } - legacyParameters, err := prepLegacyParameters(ctx, cmd, client, templateVersion, args) - if err != nil { - return nil, err - } - - richParameters, err := prepRichParameters() - if err != nil { - return nil, err - } - - return &buildParameters{ - parameters: legacyParameters, - richParameters: richParameters, - }, nil -} - -func prepLegacyParameters(ctx context.Context, cmd *cobra.Command, client *codersdk.Client, templateVersion codersdk.TemplateVersion, args prepWorkspaceBuildArgs) ([]codersdk.CreateParameterRequest, error) { + // Legacy parameters parameterSchemas, err := client.TemplateVersionSchema(ctx, templateVersion.ID) if err != nil { return nil, err @@ -231,7 +214,7 @@ func prepLegacyParameters(ctx context.Context, cmd *cobra.Command, client *coder } } disclaimerPrinted := false - parameters := make([]codersdk.CreateParameterRequest, 0) + legacyParameters := make([]codersdk.CreateParameterRequest, 0) PromptParamLoop: for _, parameterSchema := range parameterSchemas { if !parameterSchema.AllowOverrideSource { @@ -258,7 +241,7 @@ PromptParamLoop: return nil, err } - parameters = append(parameters, codersdk.CreateParameterRequest{ + legacyParameters = append(legacyParameters, codersdk.CreateParameterRequest{ Name: parameterSchema.Name, SourceValue: parameterValue, SourceScheme: codersdk.ParameterSourceSchemeData, @@ -267,10 +250,55 @@ PromptParamLoop: } _, _ = fmt.Fprintln(cmd.OutOrStdout()) + // Rich parameters + templateVersionParameters, err := client.TemplateVersionRichParameters(cmd.Context(), templateVersion.ID) + if err != nil { + return nil, xerrors.Errorf("get template version rich parameters", err) + } + + parameterMapFromFile = map[string]string{} + useParamFile = false + if args.ParameterFile != "" { + useParamFile = true + _, _ = fmt.Fprintln(cmd.OutOrStdout(), cliui.Styles.Paragraph.Render("Attempting to read the variables from the rich parameter file.")+"\r\n") + parameterMapFromFile, err = createParameterMapFromFile(args.ParameterFile) + if err != nil { + return nil, err + } + } + disclaimerPrinted = false + richParameters := make([]codersdk.WorkspaceBuildParameter, 0) +PromptRichParamLoop: + for _, templateVersionParameter := range templateVersionParameters { + if !disclaimerPrinted { + _, _ = fmt.Fprintln(cmd.OutOrStdout(), cliui.Styles.Paragraph.Render("This template has customizable parameters. Values can be changed after create, but may have unintended side effects (like data loss).")+"\r\n") + disclaimerPrinted = true + } + + // Param file is all or nothing + if !useParamFile { + for _, e := range args.ExistingParams { + if e.Name == templateVersionParameter.Name { + // If the param already exists, we do not need to prompt it again. + // The workspace scope will reuse params for each build. + continue PromptRichParamLoop + } + } + } + + parameterValue, err := getWorkspaceBuildParameterValueFromMapOrInput(cmd, parameterMapFromFile, templateVersionParameter) + if err != nil { + return nil, err + } + + richParameters = append(richParameters, *parameterValue) + } + // Run a dry-run with the given parameters to check correctness dryRun, err := client.CreateTemplateVersionDryRun(cmd.Context(), templateVersion.ID, codersdk.CreateTemplateVersionDryRunRequest{ WorkspaceName: args.NewWorkspaceName, - ParameterValues: parameters, + ParameterValues: legacyParameters, + // TODO RichParameterValues }) if err != nil { return nil, xerrors.Errorf("begin workspace dry-run: %w", err) @@ -309,9 +337,9 @@ PromptParamLoop: if err != nil { return nil, err } - return parameters, nil -} -func prepRichParameters() ([]codersdk.WorkspaceBuildParameter, error) { - return nil, nil + return &buildParameters{ + parameters: legacyParameters, + richParameters: richParameters, + }, nil } diff --git a/cli/parameter.go b/cli/parameter.go index 3fe065b057e9a..127ac58cf5cd5 100644 --- a/cli/parameter.go +++ b/cli/parameter.go @@ -3,11 +3,10 @@ package cli import ( "os" + "github.com/spf13/cobra" "golang.org/x/xerrors" "gopkg.in/yaml.v3" - "github.com/spf13/cobra" - "github.com/coder/coder/cli/cliui" "github.com/coder/coder/codersdk" ) @@ -58,3 +57,27 @@ func getParameterValueFromMapOrInput(cmd *cobra.Command, parameterMap map[string } return parameterValue, nil } + +func getWorkspaceBuildParameterValueFromMapOrInput(cmd *cobra.Command, parameterMap map[string]string, templateVersionParameter codersdk.TemplateVersionParameter) (*codersdk.WorkspaceBuildParameter, error) { + var parameterValue string + var err error + if parameterMap != nil { + var ok bool + parameterValue, ok = parameterMap[templateVersionParameter.Name] + if !ok { + parameterValue, err = cliui.RichParameter(cmd, templateVersionParameter) + if err != nil { + return nil, err + } + } + } else { + parameterValue, err = cliui.RichParameter(cmd, templateVersionParameter) + if err != nil { + return nil, err + } + } + return &codersdk.WorkspaceBuildParameter{ + Name: templateVersionParameter.Name, + Value: parameterValue, + }, nil +} From cfe5521603bfbbee3dda4991cecce80957dbffa3 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 18 Jan 2023 17:05:59 +0100 Subject: [PATCH 03/15] CLI: handle workspace build parameters --- cli/cliui/parameter.go | 1 + coderd/workspacebuilds.go | 2 +- provisioner/terraform/provision.go | 8 +++-- provisioner/terraform/provision_test.go | 43 +++++++++++++++++++++++++ provisionerd/runner/runner.go | 5 +-- 5 files changed, 54 insertions(+), 5 deletions(-) diff --git a/cli/cliui/parameter.go b/cli/cliui/parameter.go index 2519b7cc48e50..5ba37f6609620 100644 --- a/cli/cliui/parameter.go +++ b/cli/cliui/parameter.go @@ -68,6 +68,7 @@ func RichParameter(cmd *cobra.Command, templateVersionParameter codersdk.Templat } // TODO Implement full validation and show descriptions. + // TODO Is mutable? var err error var value string if len(templateVersionParameter.Options) > 0 { diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index f62b82a07b63a..25dbf5dae0870 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -567,7 +567,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { Value: values, }) if err != nil { - return xerrors.Errorf("insert workspace build parameter: %w", err) + return xerrors.Errorf("insert workspace build parameters: %w", err) } return nil diff --git a/provisioner/terraform/provision.go b/provisioner/terraform/provision.go index a267085126f76..5df40e6807d3f 100644 --- a/provisioner/terraform/provision.go +++ b/provisioner/terraform/provision.go @@ -12,6 +12,7 @@ import ( "github.com/coder/coder/provisionersdk" "github.com/coder/coder/provisionersdk/proto" + "github.com/coder/terraform-provider-coder/provider" ) // Provision executes `terraform apply` or `terraform plan` for dry runs. @@ -138,7 +139,7 @@ func (s *server) Provision(stream proto.DRPCProvisioner_ProvisionStream) error { } s.logger.Debug(ctx, "ran initialization") - env, err := provisionEnv(config, request.GetPlan().GetParameterValues()) + env, err := provisionEnv(config, request.GetPlan().GetParameterValues(), request.GetPlan().GetRichParameterValues()) if err != nil { return err } @@ -204,7 +205,7 @@ func planVars(plan *proto.Provision_Plan) ([]string, error) { return vars, nil } -func provisionEnv(config *proto.Provision_Config, params []*proto.ParameterValue) ([]string, error) { +func provisionEnv(config *proto.Provision_Config, params []*proto.ParameterValue, richParams []*proto.RichParameterValue) ([]string, error) { env := safeEnviron() env = append(env, "CODER_AGENT_URL="+config.Metadata.CoderUrl, @@ -228,6 +229,9 @@ func provisionEnv(config *proto.Provision_Config, params []*proto.ParameterValue return nil, xerrors.Errorf("unsupported parameter type %q for %q", param.DestinationScheme, param.Name) } } + for _, param := range richParams { + env = append(env, provider.ParameterEnvironmentVariable(param.Name)+"="+param.Value) + } return env, nil } diff --git a/provisioner/terraform/provision_test.go b/provisioner/terraform/provision_test.go index a90bbacdffd3b..9eebb422db5da 100644 --- a/provisioner/terraform/provision_test.go +++ b/provisioner/terraform/provision_test.go @@ -320,6 +320,49 @@ func TestProvision(t *testing.T) { }, ErrorContains: "unsupported parameter type", }, + { + Name: "rich-parameter-with-value", + Files: map[string]string{ + "main.tf": `terraform { + required_providers { + coder = { + source = "coder/coder" + version = "0.6.6" + } + } + } + + data "coder_parameter" "example" { + name = "Example" + type = "string" + default = "foobar" + } + + resource "null_resource" "example" { + triggers = { + misc = "${data.coder_parameter.example.value}" + } + }`, + }, + Request: &proto.Provision_Plan{ + RichParameterValues: []*proto.RichParameterValue{ + { + Name: "Example", + Value: "foobaz", + }, + }, + }, + Response: &proto.Provision_Response{ + Type: &proto.Provision_Response_Complete{ + Complete: &proto.Provision_Complete{ + Resources: []*proto.Resource{{ + Name: "example", + Type: "null_resource", + }}, + }, + }, + }, + }, } for _, testCase := range testCases { diff --git a/provisionerd/runner/runner.go b/provisionerd/runner/runner.go index 060ea9273ffe5..f5b6df17f82a4 100644 --- a/provisionerd/runner/runner.go +++ b/provisionerd/runner/runner.go @@ -941,8 +941,9 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p completedPlan, failed := r.buildWorkspace(ctx, "Planning infrastructure", &sdkproto.Provision_Request{ Type: &sdkproto.Provision_Request_Plan{ Plan: &sdkproto.Provision_Plan{ - Config: config, - ParameterValues: r.job.GetWorkspaceBuild().ParameterValues, + Config: config, + ParameterValues: r.job.GetWorkspaceBuild().ParameterValues, + RichParameterValues: r.job.GetWorkspaceBuild().RichParameterValues, }, }, }) From 74315b22b9f7b5e550956889fa978517fdf38e30 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 18 Jan 2023 17:33:41 +0100 Subject: [PATCH 04/15] fix: golintci --- cli/cliui/parameter.go | 4 ++-- cli/create.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/cliui/parameter.go b/cli/cliui/parameter.go index 5ba37f6609620..6f0923a6c0984 100644 --- a/cli/cliui/parameter.go +++ b/cli/cliui/parameter.go @@ -107,9 +107,9 @@ func RichParameter(cmd *cobra.Command, templateVersionParameter codersdk.Templat return value, nil } -func templateVersionParameterOptionValues(parameter codersdk.TemplateVersionParameter) []string { +func templateVersionParameterOptionValues(param codersdk.TemplateVersionParameter) []string { var options []string - for _, opt := range parameter.Options { + for _, opt := range param.Options { options = append(options, opt.Value) } return options diff --git a/cli/create.go b/cli/create.go index b1fec6eb19446..631fd5b77fda3 100644 --- a/cli/create.go +++ b/cli/create.go @@ -253,7 +253,7 @@ PromptParamLoop: // Rich parameters templateVersionParameters, err := client.TemplateVersionRichParameters(cmd.Context(), templateVersion.ID) if err != nil { - return nil, xerrors.Errorf("get template version rich parameters", err) + return nil, xerrors.Errorf("get template version rich parameters: %w", err) } parameterMapFromFile = map[string]string{} From 90b5e357a5762be324c94c3d99610a793671b6a5 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 19 Jan 2023 11:15:12 +0100 Subject: [PATCH 05/15] Fix: dry run --- cli/create.go | 6 +- coderd/apidoc/docs.go | 6 + coderd/apidoc/swagger.json | 6 + .../provisionerdserver/provisionerdserver.go | 10 +- coderd/templateversions.go | 16 +- codersdk/templateversions.go | 5 +- docs/api/schemas.md | 15 +- docs/api/templates.md | 12 + provisionerd/proto/provisionerd.pb.go | 359 +++++++++--------- provisionerd/proto/provisionerd.proto | 3 +- provisionerd/proto/provisionerd_drpc.pb.go | 2 +- provisionerd/runner/runner.go | 17 +- site/src/api/typesGenerated.ts | 1 + 13 files changed, 262 insertions(+), 196 deletions(-) diff --git a/cli/create.go b/cli/create.go index 631fd5b77fda3..a82c02096b9cd 100644 --- a/cli/create.go +++ b/cli/create.go @@ -296,9 +296,9 @@ PromptRichParamLoop: // Run a dry-run with the given parameters to check correctness dryRun, err := client.CreateTemplateVersionDryRun(cmd.Context(), templateVersion.ID, codersdk.CreateTemplateVersionDryRunRequest{ - WorkspaceName: args.NewWorkspaceName, - ParameterValues: legacyParameters, - // TODO RichParameterValues + WorkspaceName: args.NewWorkspaceName, + ParameterValues: legacyParameters, + RichParameterValues: richParameters, }) if err != nil { return nil, xerrors.Errorf("begin workspace dry-run: %w", err) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 177c4230c94a1..8584b89fc803f 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -5491,6 +5491,12 @@ const docTemplate = `{ "$ref": "#/definitions/codersdk.CreateParameterRequest" } }, + "rich_parameter_values": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.WorkspaceBuildParameter" + } + }, "workspace_name": { "type": "string" } diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index b9a34ede0b0f3..97b951341f36f 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -4865,6 +4865,12 @@ "$ref": "#/definitions/codersdk.CreateParameterRequest" } }, + "rich_parameter_values": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.WorkspaceBuildParameter" + } + }, "workspace_name": { "type": "string" } diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index ebed06dd1b5bc..9f5ffa68f0e0a 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -235,7 +235,8 @@ func (server *Server) AcquireJob(ctx context.Context, _ *proto.Empty) (*proto.Ac protoJob.Type = &proto.AcquiredJob_TemplateDryRun_{ TemplateDryRun: &proto.AcquiredJob_TemplateDryRun{ - ParameterValues: protoParameters, + ParameterValues: protoParameters, + RichParameterValues: convertRichParameterValues(input.RichParameterValues), Metadata: &sdkproto.Provision_Metadata{ CoderUrl: server.AccessURL.String(), WorkspaceName: input.WorkspaceName, @@ -1175,9 +1176,10 @@ type WorkspaceProvisionJob struct { // TemplateVersionDryRunJob is the payload for the "template_version_dry_run" job type. type TemplateVersionDryRunJob struct { - TemplateVersionID uuid.UUID `json:"template_version_id"` - WorkspaceName string `json:"workspace_name"` - ParameterValues []database.ParameterValue `json:"parameter_values"` + TemplateVersionID uuid.UUID `json:"template_version_id"` + WorkspaceName string `json:"workspace_name"` + ParameterValues []database.ParameterValue `json:"parameter_values"` + RichParameterValues []database.WorkspaceBuildParameter `json:"rich_parameter_values"` } // ProvisionerJobLogsNotifyMessage is the payload published on diff --git a/coderd/templateversions.go b/coderd/templateversions.go index dc4fd16b401b6..b4fcddaf6ac54 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -362,12 +362,22 @@ func (api *API) postTemplateVersionDryRun(rw http.ResponseWriter, r *http.Reques } } + richParameterValues := make([]database.WorkspaceBuildParameter, len(req.RichParameterValues)) + for i, v := range req.RichParameterValues { + richParameterValues[i] = database.WorkspaceBuildParameter{ + WorkspaceBuildID: uuid.Nil, + Name: v.Name, + Value: v.Value, + } + } + // Marshal template version dry-run job with the parameters from the // request. input, err := json.Marshal(provisionerdserver.TemplateVersionDryRunJob{ - TemplateVersionID: templateVersion.ID, - WorkspaceName: req.WorkspaceName, - ParameterValues: parameterValues, + TemplateVersionID: templateVersion.ID, + WorkspaceName: req.WorkspaceName, + ParameterValues: parameterValues, + RichParameterValues: richParameterValues, }) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ diff --git a/codersdk/templateversions.go b/codersdk/templateversions.go index c3198934db094..442e85b4e7d25 100644 --- a/codersdk/templateversions.go +++ b/codersdk/templateversions.go @@ -143,8 +143,9 @@ func (c *Client) TemplateVersionLogsAfter(ctx context.Context, version uuid.UUID // CreateTemplateVersionDryRunRequest defines the request parameters for // CreateTemplateVersionDryRun. type CreateTemplateVersionDryRunRequest struct { - WorkspaceName string `json:"workspace_name"` - ParameterValues []CreateParameterRequest `json:"parameter_values"` + WorkspaceName string `json:"workspace_name"` + ParameterValues []CreateParameterRequest `json:"parameter_values"` + RichParameterValues []WorkspaceBuildParameter `json:"rich_parameter_values"` } // CreateTemplateVersionDryRun begins a dry-run provisioner job against the diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 7993145e3295b..2764683a0a90a 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -762,16 +762,23 @@ CreateParameterRequest is a structure used to create a new parameter value for a "source_value": "string" } ], + "rich_parameter_values": [ + { + "name": "string", + "value": "string" + } + ], "workspace_name": "string" } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -| ------------------ | --------------------------------------------------------------------------- | -------- | ------------ | ---------------------------------------------------------------------------------- | -| `parameter_values` | array of [codersdk.CreateParameterRequest](#codersdkcreateparameterrequest) | false | | Parameter values is a structure used to create a new parameter value for a scope.] | -| `workspace_name` | string | false | | | +| Name | Type | Required | Restrictions | Description | +| ----------------------- | ----------------------------------------------------------------------------- | -------- | ------------ | ---------------------------------------------------------------------------------- | +| `parameter_values` | array of [codersdk.CreateParameterRequest](#codersdkcreateparameterrequest) | false | | Parameter values is a structure used to create a new parameter value for a scope.] | +| `rich_parameter_values` | array of [codersdk.WorkspaceBuildParameter](#codersdkworkspacebuildparameter) | false | | | +| `workspace_name` | string | false | | | ## codersdk.CreateTestAuditLogRequest diff --git a/docs/api/templates.md b/docs/api/templates.md index b271184929b48..074cec89d84d6 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -408,6 +408,12 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa "source_value": "string" } ], + "rich_parameter_values": [ + { + "name": "string", + "value": "string" + } + ], "workspace_name": "string" } ``` @@ -1293,6 +1299,12 @@ curl -X POST http://coder-server:8080/api/v2/templateversions/{templateversion}/ "source_value": "string" } ], + "rich_parameter_values": [ + { + "name": "string", + "value": "string" + } + ], "workspace_name": "string" } ``` diff --git a/provisionerd/proto/provisionerd.pb.go b/provisionerd/proto/provisionerd.pb.go index 4ce8743ab4070..8af6507b3986d 100644 --- a/provisionerd/proto/provisionerd.pb.go +++ b/provisionerd/proto/provisionerd.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.26.0 -// protoc v3.21.5 +// protoc-gen-go v1.28.1 +// protoc v3.21.6 // source: provisionerd/proto/provisionerd.proto package proto @@ -119,7 +119,6 @@ type AcquiredJob struct { UserName string `protobuf:"bytes,4,opt,name=user_name,json=userName,proto3" json:"user_name,omitempty"` TemplateSourceArchive []byte `protobuf:"bytes,5,opt,name=template_source_archive,json=templateSourceArchive,proto3" json:"template_source_archive,omitempty"` // Types that are assignable to Type: - // // *AcquiredJob_WorkspaceBuild_ // *AcquiredJob_TemplateImport_ // *AcquiredJob_TemplateDryRun_ @@ -251,7 +250,6 @@ type FailedJob struct { JobId string `protobuf:"bytes,1,opt,name=job_id,json=jobId,proto3" json:"job_id,omitempty"` Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` // Types that are assignable to Type: - // // *FailedJob_WorkspaceBuild_ // *FailedJob_TemplateImport_ // *FailedJob_TemplateDryRun_ @@ -362,7 +360,6 @@ type CompletedJob struct { JobId string `protobuf:"bytes,1,opt,name=job_id,json=jobId,proto3" json:"job_id,omitempty"` // Types that are assignable to Type: - // // *CompletedJob_WorkspaceBuild_ // *CompletedJob_TemplateImport_ // *CompletedJob_TemplateDryRun_ @@ -514,7 +511,7 @@ func (x *Log) GetLevel() proto.LogLevel { if x != nil { return x.Level } - return proto.LogLevel_TRACE + return proto.LogLevel(0) } func (x *Log) GetCreatedAt() int64 { @@ -924,8 +921,9 @@ type AcquiredJob_TemplateDryRun struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ParameterValues []*proto.ParameterValue `protobuf:"bytes,1,rep,name=parameter_values,json=parameterValues,proto3" json:"parameter_values,omitempty"` - Metadata *proto.Provision_Metadata `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata,omitempty"` + ParameterValues []*proto.ParameterValue `protobuf:"bytes,1,rep,name=parameter_values,json=parameterValues,proto3" json:"parameter_values,omitempty"` + RichParameterValues []*proto.RichParameterValue `protobuf:"bytes,2,rep,name=rich_parameter_values,json=richParameterValues,proto3" json:"rich_parameter_values,omitempty"` + Metadata *proto.Provision_Metadata `protobuf:"bytes,3,opt,name=metadata,proto3" json:"metadata,omitempty"` } func (x *AcquiredJob_TemplateDryRun) Reset() { @@ -967,6 +965,13 @@ func (x *AcquiredJob_TemplateDryRun) GetParameterValues() []*proto.ParameterValu return nil } +func (x *AcquiredJob_TemplateDryRun) GetRichParameterValues() []*proto.RichParameterValue { + if x != nil { + return x.RichParameterValues + } + return nil +} + func (x *AcquiredJob_TemplateDryRun) GetMetadata() *proto.Provision_Metadata { if x != nil { return x.Metadata @@ -1271,7 +1276,7 @@ var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{ 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x1a, 0x26, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x07, 0x0a, - 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x81, 0x08, 0x0a, 0x0b, 0x41, 0x63, 0x71, 0x75, 0x69, + 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0xd6, 0x08, 0x0a, 0x0b, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, @@ -1325,156 +1330,161 @@ var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{ 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 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, 0x95, 0x01, 0x0a, 0x0e, 0x54, + 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0xea, 0x01, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x46, 0x0a, 0x10, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x86, 0x03, 0x0a, 0x09, 0x46, - 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, - 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x51, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, - 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x46, 0x61, - 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x48, 0x00, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x51, 0x0a, 0x0f, 0x74, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, - 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x74, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x52, 0x0a, 0x10, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x54, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x48, 0x00, 0x52, - 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x1a, - 0x26, 0x0a, 0x0e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, - 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x05, 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, 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, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, - 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x48, - 0x00, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, - 0x64, 0x12, 0x54, 0x0a, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x6d, - 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, - 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, - 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x55, 0x0a, 0x10, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x5f, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, - 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x54, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x48, 0x00, 0x52, 0x0e, - 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x1a, 0x5b, - 0x0a, 0x0e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, - 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x33, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x1a, 0xd3, 0x01, 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, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0e, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3c, - 0x0a, 0x0e, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0d, 0x73, - 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, - 0x72, 0x69, 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, 0xb3, 0x01, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, - 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, - 0x25, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x4c, 0x6f, 0x67, - 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x49, 0x0a, 0x11, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, - 0x74, 0x65, 0x72, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, - 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, - 0x10, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, - 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x22, 0x77, 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, + 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, 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, 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, + 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, 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, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, + 0x86, 0x03, 0x0a, 0x09, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x12, 0x15, 0x0a, + 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, + 0x6f, 0x62, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x51, 0x0a, 0x0f, 0x77, 0x6f, + 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x72, 0x64, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x57, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x48, 0x00, 0x52, 0x0e, 0x77, + 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x51, 0x0a, + 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, + 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x00, + 0x52, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, + 0x12, 0x52, 0x0a, 0x10, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x72, 0x79, + 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, + 0x4a, 0x6f, 0x62, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, + 0x75, 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72, + 0x79, 0x52, 0x75, 0x6e, 0x1a, 0x26, 0x0a, 0x0e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 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, 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, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, + 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, + 0x75, 0x69, 0x6c, 0x64, 0x48, 0x00, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x54, 0x0a, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x29, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, + 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x54, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x55, 0x0a, 0x10, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, + 0x6f, 0x62, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, + 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72, 0x79, + 0x52, 0x75, 0x6e, 0x1a, 0x5b, 0x0a, 0x0e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x33, 0x0a, 0x09, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x1a, 0xd3, 0x01, 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, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x52, 0x0e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x0e, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x52, 0x0d, 0x73, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x72, 0x69, 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, 0xb3, 0x01, 0x0a, 0x10, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, + 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, + 0x6f, 0x62, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, + 0x64, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x49, 0x0a, 0x11, 0x70, + 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x52, 0x10, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x22, 0x77, + 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, 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, } var ( @@ -1539,27 +1549,28 @@ var file_provisionerd_proto_provisionerd_proto_depIdxs = []int32{ 23, // 16: provisionerd.AcquiredJob.WorkspaceBuild.metadata:type_name -> provisioner.Provision.Metadata 23, // 17: provisionerd.AcquiredJob.TemplateImport.metadata:type_name -> provisioner.Provision.Metadata 21, // 18: provisionerd.AcquiredJob.TemplateDryRun.parameter_values:type_name -> provisioner.ParameterValue - 23, // 19: provisionerd.AcquiredJob.TemplateDryRun.metadata:type_name -> provisioner.Provision.Metadata - 24, // 20: provisionerd.CompletedJob.WorkspaceBuild.resources:type_name -> provisioner.Resource - 24, // 21: provisionerd.CompletedJob.TemplateImport.start_resources:type_name -> provisioner.Resource - 24, // 22: provisionerd.CompletedJob.TemplateImport.stop_resources:type_name -> provisioner.Resource - 25, // 23: provisionerd.CompletedJob.TemplateImport.rich_parameters:type_name -> provisioner.RichParameter - 24, // 24: provisionerd.CompletedJob.TemplateDryRun.resources:type_name -> provisioner.Resource - 1, // 25: provisionerd.ProvisionerDaemon.AcquireJob:input_type -> provisionerd.Empty - 8, // 26: provisionerd.ProvisionerDaemon.CommitQuota:input_type -> provisionerd.CommitQuotaRequest - 6, // 27: provisionerd.ProvisionerDaemon.UpdateJob:input_type -> provisionerd.UpdateJobRequest - 3, // 28: provisionerd.ProvisionerDaemon.FailJob:input_type -> provisionerd.FailedJob - 4, // 29: provisionerd.ProvisionerDaemon.CompleteJob:input_type -> provisionerd.CompletedJob - 2, // 30: provisionerd.ProvisionerDaemon.AcquireJob:output_type -> provisionerd.AcquiredJob - 9, // 31: provisionerd.ProvisionerDaemon.CommitQuota:output_type -> provisionerd.CommitQuotaResponse - 7, // 32: provisionerd.ProvisionerDaemon.UpdateJob:output_type -> provisionerd.UpdateJobResponse - 1, // 33: provisionerd.ProvisionerDaemon.FailJob:output_type -> provisionerd.Empty - 1, // 34: provisionerd.ProvisionerDaemon.CompleteJob:output_type -> provisionerd.Empty - 30, // [30:35] is the sub-list for method output_type - 25, // [25:30] is the sub-list for method input_type - 25, // [25:25] is the sub-list for extension type_name - 25, // [25:25] is the sub-list for extension extendee - 0, // [0:25] is the sub-list for field type_name + 22, // 19: provisionerd.AcquiredJob.TemplateDryRun.rich_parameter_values:type_name -> provisioner.RichParameterValue + 23, // 20: provisionerd.AcquiredJob.TemplateDryRun.metadata:type_name -> provisioner.Provision.Metadata + 24, // 21: provisionerd.CompletedJob.WorkspaceBuild.resources:type_name -> provisioner.Resource + 24, // 22: provisionerd.CompletedJob.TemplateImport.start_resources:type_name -> provisioner.Resource + 24, // 23: provisionerd.CompletedJob.TemplateImport.stop_resources:type_name -> provisioner.Resource + 25, // 24: provisionerd.CompletedJob.TemplateImport.rich_parameters:type_name -> provisioner.RichParameter + 24, // 25: provisionerd.CompletedJob.TemplateDryRun.resources:type_name -> provisioner.Resource + 1, // 26: provisionerd.ProvisionerDaemon.AcquireJob:input_type -> provisionerd.Empty + 8, // 27: provisionerd.ProvisionerDaemon.CommitQuota:input_type -> provisionerd.CommitQuotaRequest + 6, // 28: provisionerd.ProvisionerDaemon.UpdateJob:input_type -> provisionerd.UpdateJobRequest + 3, // 29: provisionerd.ProvisionerDaemon.FailJob:input_type -> provisionerd.FailedJob + 4, // 30: provisionerd.ProvisionerDaemon.CompleteJob:input_type -> provisionerd.CompletedJob + 2, // 31: provisionerd.ProvisionerDaemon.AcquireJob:output_type -> provisionerd.AcquiredJob + 9, // 32: provisionerd.ProvisionerDaemon.CommitQuota:output_type -> provisionerd.CommitQuotaResponse + 7, // 33: provisionerd.ProvisionerDaemon.UpdateJob:output_type -> provisionerd.UpdateJobResponse + 1, // 34: provisionerd.ProvisionerDaemon.FailJob:output_type -> provisionerd.Empty + 1, // 35: provisionerd.ProvisionerDaemon.CompleteJob:output_type -> provisionerd.Empty + 31, // [31:36] is the sub-list for method output_type + 26, // [26:31] is the sub-list for method input_type + 26, // [26:26] is the sub-list for extension type_name + 26, // [26:26] is the sub-list for extension extendee + 0, // [0:26] is the sub-list for field type_name } func init() { file_provisionerd_proto_provisionerd_proto_init() } diff --git a/provisionerd/proto/provisionerd.proto b/provisionerd/proto/provisionerd.proto index 66555ef53e09a..8b9ec5f8ab31c 100644 --- a/provisionerd/proto/provisionerd.proto +++ b/provisionerd/proto/provisionerd.proto @@ -24,7 +24,8 @@ message AcquiredJob { } message TemplateDryRun { repeated provisioner.ParameterValue parameter_values = 1; - provisioner.Provision.Metadata metadata = 2; + repeated provisioner.RichParameterValue rich_parameter_values = 2; + provisioner.Provision.Metadata metadata = 3; } string job_id = 1; diff --git a/provisionerd/proto/provisionerd_drpc.pb.go b/provisionerd/proto/provisionerd_drpc.pb.go index 6d73176475490..058af595809b8 100644 --- a/provisionerd/proto/provisionerd_drpc.pb.go +++ b/provisionerd/proto/provisionerd_drpc.pb.go @@ -1,5 +1,5 @@ // Code generated by protoc-gen-go-drpc. DO NOT EDIT. -// protoc-gen-go-drpc version: v0.0.26 +// protoc-gen-go-drpc version: (devel) // source: provisionerd/proto/provisionerd.proto package proto diff --git a/provisionerd/runner/runner.go b/provisionerd/runner/runner.go index f5b6df17f82a4..2ccf63c71c4cf 100644 --- a/provisionerd/runner/runner.go +++ b/provisionerd/runner/runner.go @@ -427,6 +427,7 @@ func (r *Runner) do(ctx context.Context) (*proto.CompletedJob, *proto.FailedJob) r.logger.Debug(context.Background(), "acquired job is template dry-run", slog.F("workspace_name", jobType.TemplateDryRun.Metadata.WorkspaceName), slog.F("parameters", jobType.TemplateDryRun.ParameterValues), + slog.F("rich_parameter_values", jobType.TemplateDryRun.RichParameterValues), ) return r.runTemplateDryRun(ctx) case *proto.AcquiredJob_WorkspaceBuild_: @@ -646,9 +647,15 @@ func (r *Runner) runTemplateImportParse(ctx context.Context) ([]*sdkproto.Parame } // 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. +// 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, metadata *sdkproto.Provision_Metadata) ([]*sdkproto.Resource, []*sdkproto.RichParameter, error) { + return r.runTemplateImportProvisionWithRichParameters(ctx, values, 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, richParameterValues []*sdkproto.RichParameterValue, metadata *sdkproto.Provision_Metadata) ([]*sdkproto.Resource, []*sdkproto.RichParameter, error) { ctx, span := r.startTrace(ctx, tracing.FuncName()) defer span.End() @@ -685,7 +692,8 @@ func (r *Runner) runTemplateImportProvision(ctx context.Context, values []*sdkpr Directory: r.workDirectory, Metadata: metadata, }, - ParameterValues: values, + ParameterValues: values, + RichParameterValues: richParameterValues, }, }, }) @@ -767,8 +775,9 @@ 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.runTemplateImportProvision(ctx, + resources, _, err := r.runTemplateImportProvisionWithRichParameters(ctx, r.job.GetTemplateDryRun().GetParameterValues(), + r.job.GetTemplateDryRun().GetRichParameterValues(), metadata, ) if err != nil { diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index f5d1728a9e9d6..61bbe2ad1c766 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -188,6 +188,7 @@ export interface CreateTemplateRequest { export interface CreateTemplateVersionDryRunRequest { readonly workspace_name: string readonly parameter_values: CreateParameterRequest[] + readonly rich_parameter_values: WorkspaceBuildParameter[] } // From codersdk/organizations.go From 34db76e58422158e18dac5e274519b9f7bd6c3a4 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 19 Jan 2023 11:39:45 +0100 Subject: [PATCH 06/15] fix --- provisionerd/proto/provisionerd.pb.go | 9 ++++++--- provisionerd/proto/provisionerd_drpc.pb.go | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/provisionerd/proto/provisionerd.pb.go b/provisionerd/proto/provisionerd.pb.go index 8af6507b3986d..a8b741d89987e 100644 --- a/provisionerd/proto/provisionerd.pb.go +++ b/provisionerd/proto/provisionerd.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.1 -// protoc v3.21.6 +// protoc-gen-go v1.26.0 +// protoc v3.21.5 // source: provisionerd/proto/provisionerd.proto package proto @@ -119,6 +119,7 @@ type AcquiredJob struct { UserName string `protobuf:"bytes,4,opt,name=user_name,json=userName,proto3" json:"user_name,omitempty"` TemplateSourceArchive []byte `protobuf:"bytes,5,opt,name=template_source_archive,json=templateSourceArchive,proto3" json:"template_source_archive,omitempty"` // Types that are assignable to Type: + // // *AcquiredJob_WorkspaceBuild_ // *AcquiredJob_TemplateImport_ // *AcquiredJob_TemplateDryRun_ @@ -250,6 +251,7 @@ type FailedJob struct { JobId string `protobuf:"bytes,1,opt,name=job_id,json=jobId,proto3" json:"job_id,omitempty"` Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` // Types that are assignable to Type: + // // *FailedJob_WorkspaceBuild_ // *FailedJob_TemplateImport_ // *FailedJob_TemplateDryRun_ @@ -360,6 +362,7 @@ type CompletedJob struct { JobId string `protobuf:"bytes,1,opt,name=job_id,json=jobId,proto3" json:"job_id,omitempty"` // Types that are assignable to Type: + // // *CompletedJob_WorkspaceBuild_ // *CompletedJob_TemplateImport_ // *CompletedJob_TemplateDryRun_ @@ -511,7 +514,7 @@ func (x *Log) GetLevel() proto.LogLevel { if x != nil { return x.Level } - return proto.LogLevel(0) + return proto.LogLevel_TRACE } func (x *Log) GetCreatedAt() int64 { diff --git a/provisionerd/proto/provisionerd_drpc.pb.go b/provisionerd/proto/provisionerd_drpc.pb.go index 058af595809b8..6d73176475490 100644 --- a/provisionerd/proto/provisionerd_drpc.pb.go +++ b/provisionerd/proto/provisionerd_drpc.pb.go @@ -1,5 +1,5 @@ // Code generated by protoc-gen-go-drpc. DO NOT EDIT. -// protoc-gen-go-drpc version: (devel) +// protoc-gen-go-drpc version: v0.0.26 // source: provisionerd/proto/provisionerd.proto package proto From a78f798b9c542ea2176a8076f7b60192612ae411 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 19 Jan 2023 12:48:47 +0100 Subject: [PATCH 07/15] CLI: is mutable --- cli/cliui/parameter.go | 1 - cli/create.go | 11 +++++++++-- cli/update.go | 1 + coderd/workspacebuilds.go | 1 + 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/cli/cliui/parameter.go b/cli/cliui/parameter.go index 6f0923a6c0984..e19e09671dada 100644 --- a/cli/cliui/parameter.go +++ b/cli/cliui/parameter.go @@ -68,7 +68,6 @@ func RichParameter(cmd *cobra.Command, templateVersionParameter codersdk.Templat } // TODO Implement full validation and show descriptions. - // TODO Is mutable? var err error var value string if len(templateVersionParameter.Options) > 0 { diff --git a/cli/create.go b/cli/create.go index a82c02096b9cd..918698f8c1cf4 100644 --- a/cli/create.go +++ b/cli/create.go @@ -178,6 +178,8 @@ type prepWorkspaceBuildArgs struct { ExistingRichParams []codersdk.WorkspaceBuildParameter RichParameterFile string NewWorkspaceName string + + UpdateWorkspace bool } type buildParameters struct { @@ -261,7 +263,7 @@ PromptParamLoop: if args.ParameterFile != "" { useParamFile = true _, _ = fmt.Fprintln(cmd.OutOrStdout(), cliui.Styles.Paragraph.Render("Attempting to read the variables from the rich parameter file.")+"\r\n") - parameterMapFromFile, err = createParameterMapFromFile(args.ParameterFile) + parameterMapFromFile, err = createParameterMapFromFile(args.RichParameterFile) if err != nil { return nil, err } @@ -277,7 +279,7 @@ PromptRichParamLoop: // Param file is all or nothing if !useParamFile { - for _, e := range args.ExistingParams { + for _, e := range args.ExistingRichParams { if e.Name == templateVersionParameter.Name { // If the param already exists, we do not need to prompt it again. // The workspace scope will reuse params for each build. @@ -286,6 +288,11 @@ PromptRichParamLoop: } } + if args.UpdateWorkspace && !templateVersionParameter.Mutable { + _, _ = fmt.Fprintln(cmd.OutOrStdout(), cliui.Styles.Warn.Render(fmt.Sprintf(`Parameter %q is not mutable, so can't be customized after workspace creation.`, templateVersionParameter.Name))) + continue + } + parameterValue, err := getWorkspaceBuildParameterValueFromMapOrInput(cmd, parameterMapFromFile, templateVersionParameter) if err != nil { return nil, err diff --git a/cli/update.go b/cli/update.go index 7ecdbd1bc850f..9e23ee73569dd 100644 --- a/cli/update.go +++ b/cli/update.go @@ -60,6 +60,7 @@ func update() *cobra.Command { ExistingRichParams: existingRichParams, RichParameterFile: richParameterFile, NewWorkspaceName: workspace.Name, + UpdateWorkspace: true, }) if err != nil { return nil diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index 25dbf5dae0870..8864e484cb171 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -455,6 +455,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { } if parameters == nil { + // TODO Is mutable? buildParameters, err := api.Database.GetWorkspaceBuildParameters(ctx, priorHistory.ID) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ From 7e6ddca14bc169533cde9386ac2bb712df22ec5e Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 19 Jan 2023 15:17:34 +0100 Subject: [PATCH 08/15] coderd: mutable --- coderd/workspacebuilds.go | 65 +++++++++++++++++++++++----------- coderd/workspacebuilds_test.go | 47 ++++++++++++++++++++++-- 2 files changed, 89 insertions(+), 23 deletions(-) diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index 8864e484cb171..86b5961e905ed 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -377,11 +377,6 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { state = createBuild.ProvisionerState } - var parameters []codersdk.WorkspaceBuildParameter - if createBuild.RichParameterValues != nil { - parameters = createBuild.RichParameterValues - } - if createBuild.Orphan { if createBuild.Transition != codersdk.WorkspaceTransitionDelete { httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ @@ -454,22 +449,43 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { state = priorHistory.ProvisionerState } - if parameters == nil { - // TODO Is mutable? - buildParameters, err := api.Database.GetWorkspaceBuildParameters(ctx, priorHistory.ID) - if err != nil { - httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Internal error fetching prior workspace build parameters.", - Detail: err.Error(), - }) - return + templateVersionParameters, err := api.Database.GetTemplateVersionParameters(ctx, createBuild.TemplateVersionID) + if err != nil { + httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ + Message: "Internal error fetching template version parameters.", + Detail: err.Error(), + }) + return + + } + + lastBuildParameters, err := api.Database.GetWorkspaceBuildParameters(ctx, priorHistory.ID) + if err != nil { + httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ + Message: "Internal error fetching prior workspace build parameters.", + Detail: err.Error(), + }) + return + } + apiLastBuildParameters := convertWorkspaceBuildParameters(lastBuildParameters) + + var parameters []codersdk.WorkspaceBuildParameter + for _, templateVersionParameter := range templateVersionParameters { + // Check if parameter value is in request + if buildParameter, found := findWorkspaceBuildParameter(createBuild.RichParameterValues, templateVersionParameter.Name); found { + if !templateVersionParameter.Mutable { + httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ + Message: fmt.Sprintf("Parameter %q is mutable, so it can't be updated after creating workspace.", templateVersionParameter.Name), + }) + return + } + parameters = append(parameters, *buildParameter) + continue } - parameters = make([]codersdk.WorkspaceBuildParameter, 0, len(buildParameters)) - for _, param := range buildParameters { - parameters = append(parameters, codersdk.WorkspaceBuildParameter{ - Name: param.Name, - Value: param.Value, - }) + + // Check if parameter is defined in previous build + if buildParameter, found := findWorkspaceBuildParameter(apiLastBuildParameters, templateVersionParameter.Name); found { + parameters = append(parameters, *buildParameter) } } @@ -1172,3 +1188,12 @@ func convertWorkspaceBuildParameters(parameters []database.WorkspaceBuildParamet } return apiParameters } + +func findWorkspaceBuildParameter(params []codersdk.WorkspaceBuildParameter, parameterName string) (*codersdk.WorkspaceBuildParameter, bool) { + for _, p := range params { + if p.Name == parameterName { + return &p, true + } + } + return nil, false +} diff --git a/coderd/workspacebuilds_test.go b/coderd/workspacebuilds_test.go index b3b446e55f36b..c362d21765564 100644 --- a/coderd/workspacebuilds_test.go +++ b/coderd/workspacebuilds_test.go @@ -644,11 +644,16 @@ func TestWorkspaceBuildWithRichParameters(t *testing.T) { secondParameterName = "second_parameter" secondParameterDescription = "This is second parameter" secondParameterValue = "2" + + immutableParameterName = "immutable_parameter" + immutableParameterDescription = "This is mutable parameter" + immutableParameterValue = "3" ) initialBuildParameters := []codersdk.WorkspaceBuildParameter{ {Name: firstParameterName, Value: firstParameterValue}, {Name: secondParameterName, Value: secondParameterValue}, + {Name: immutableParameterName, Value: immutableParameterValue}, } echoResponses := &echo.Responses{ @@ -658,8 +663,9 @@ func TestWorkspaceBuildWithRichParameters(t *testing.T) { Type: &proto.Provision_Response_Complete{ Complete: &proto.Provision_Complete{ Parameters: []*proto.RichParameter{ - {Name: firstParameterName, Description: firstParameterDescription}, - {Name: secondParameterName, Description: secondParameterDescription}, + {Name: firstParameterName, Description: firstParameterDescription, Mutable: true}, + {Name: secondParameterName, Description: secondParameterDescription, Mutable: true}, + {Name: immutableParameterName, Description: immutableParameterDescription, Mutable: false}, }, }, }, @@ -705,7 +711,12 @@ func TestWorkspaceBuildWithRichParameters(t *testing.T) { workspaceBuildParameters, err := client.WorkspaceBuildParameters(ctx, nextWorkspaceBuild.ID) require.NoError(t, err) - require.ElementsMatch(t, nextBuildParameters, workspaceBuildParameters) + + expected := append(nextBuildParameters, codersdk.WorkspaceBuildParameter{ + Name: immutableParameterName, + Value: immutableParameterValue, + }) + require.ElementsMatch(t, expected, workspaceBuildParameters) }) t.Run("UsePreviousParameterValues", func(t *testing.T) { t.Parallel() @@ -738,4 +749,34 @@ func TestWorkspaceBuildWithRichParameters(t *testing.T) { require.NoError(t, err) require.ElementsMatch(t, initialBuildParameters, workspaceBuildParameters) }) + + t.Run("DoNotModifyImmutables", 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, echoResponses) + coderdtest.AwaitTemplateVersionJob(t, client, version.ID) + + template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) + workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID, func(cwr *codersdk.CreateWorkspaceRequest) { + cwr.RichParameterValues = initialBuildParameters + }) + + workspaceBuild := coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID) + require.Equal(t, codersdk.WorkspaceStatusRunning, workspaceBuild.Status) + + // Update build parameters + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + nextBuildParameters := []codersdk.WorkspaceBuildParameter{ + {Name: immutableParameterName, Value: "BAD"}, + } + _, err := client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{ + Transition: codersdk.WorkspaceTransitionStart, + RichParameterValues: nextBuildParameters, + }) + require.Error(t, err) + }) } From b55e40281ed38e5b1e122f4fdfd5b94e03c987f3 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 19 Jan 2023 15:19:59 +0100 Subject: [PATCH 09/15] fix: golanci --- coderd/workspacebuilds.go | 1 - 1 file changed, 1 deletion(-) diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index 86b5961e905ed..b663ec729042f 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -456,7 +456,6 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { Detail: err.Error(), }) return - } lastBuildParameters, err := api.Database.GetWorkspaceBuildParameters(ctx, priorHistory.ID) From f6dee95a8c0cff8cc4b8fe1e8bcb97796d03f93e Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 19 Jan 2023 15:49:19 +0100 Subject: [PATCH 10/15] fix: richParameterFile --- cli/create.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/create.go b/cli/create.go index 918698f8c1cf4..84dce3a0d17fc 100644 --- a/cli/create.go +++ b/cli/create.go @@ -260,7 +260,7 @@ PromptParamLoop: parameterMapFromFile = map[string]string{} useParamFile = false - if args.ParameterFile != "" { + if args.RichParameterFile != "" { useParamFile = true _, _ = fmt.Fprintln(cmd.OutOrStdout(), cliui.Styles.Paragraph.Render("Attempting to read the variables from the rich parameter file.")+"\r\n") parameterMapFromFile, err = createParameterMapFromFile(args.RichParameterFile) From f46cf48f9635e65a49ea4426cc74e3b38d143066 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 19 Jan 2023 17:10:04 +0100 Subject: [PATCH 11/15] CLI: create unit tests --- cli/create.go | 9 +-- cli/create_test.go | 118 +++++++++++++++++++++++++++++++++ coderd/workspacebuilds_test.go | 2 +- 3 files changed, 124 insertions(+), 5 deletions(-) diff --git a/cli/create.go b/cli/create.go index 84dce3a0d17fc..a5cf49048cf19 100644 --- a/cli/create.go +++ b/cli/create.go @@ -123,10 +123,11 @@ func create() *cobra.Command { } buildParams, err := prepWorkspaceBuild(cmd, client, prepWorkspaceBuildArgs{ - Template: template, - ExistingParams: []codersdk.Parameter{}, - ParameterFile: parameterFile, - NewWorkspaceName: workspaceName, + Template: template, + ExistingParams: []codersdk.Parameter{}, + ParameterFile: parameterFile, + RichParameterFile: richParameterFile, + NewWorkspaceName: workspaceName, }) if err != nil { return err diff --git a/cli/create_test.go b/cli/create_test.go index 616ec3775d854..3465ea1414fb5 100644 --- a/cli/create_test.go +++ b/cli/create_test.go @@ -321,6 +321,124 @@ func TestCreate(t *testing.T) { }) } +func TestCreateWithRichParameters(t *testing.T) { + t.Parallel() + + const ( + firstParameterName = "first_parameter" + firstParameterDescription = "This is first parameter" + firstParameterValue = "1" + + secondParameterName = "second_parameter" + secondParameterDescription = "This is second parameter" + secondParameterValue = "2" + + immutableParameterName = "immutable_parameter" + immutableParameterDescription = "This is mutable parameter" + immutableParameterValue = "3" + ) + + echoResponses := &echo.Responses{ + Parse: echo.ParseComplete, + ProvisionPlan: []*proto.Provision_Response{ + { + Type: &proto.Provision_Response_Complete{ + Complete: &proto.Provision_Complete{ + Parameters: []*proto.RichParameter{ + {Name: firstParameterName, Description: firstParameterDescription, Mutable: true}, + {Name: secondParameterName, Description: secondParameterDescription, Mutable: true}, + {Name: immutableParameterName, Description: immutableParameterDescription, Mutable: false}, + }, + }, + }, + }}, + ProvisionApply: []*proto.Provision_Response{{ + Type: &proto.Provision_Response_Complete{ + Complete: &proto.Provision_Complete{}, + }, + }}, + } + + t.Run("InputParameters", 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, 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) + }() + + matches := []string{ + firstParameterDescription, firstParameterValue, + secondParameterDescription, secondParameterValue, + immutableParameterDescription, immutableParameterValue, + "Confirm create?", "yes", + } + for i := 0; i < len(matches); i += 2 { + match := matches[i] + value := matches[i+1] + pty.ExpectMatch(match) + pty.WriteLine(value) + } + <-doneChan + }) + + t.Run("RichParametersFile", 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, echoResponses) + coderdtest.AwaitTemplateVersionJob(t, client, version.ID) + + template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) + + tempDir := t.TempDir() + removeTmpDirUntilSuccessAfterTest(t, tempDir) + parameterFile, _ := os.CreateTemp(tempDir, "testParameterFile*.yaml") + _, _ = parameterFile.WriteString( + firstParameterName + ": " + firstParameterValue + "\n" + + secondParameterName + ": " + secondParameterValue + "\n" + + immutableParameterName + ": " + immutableParameterValue) + cmd, root := clitest.New(t, "create", "my-workspace", "--template", template.Name, "--rich-parameter-file", parameterFile.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) + }() + + matches := []string{ + "Confirm create?", "yes", + } + for i := 0; i < len(matches); i += 2 { + match := matches[i] + value := matches[i+1] + pty.ExpectMatch(match) + pty.WriteLine(value) + } + <-doneChan + }) +} + func createTestParseResponseWithDefault(defaultValue string) []*proto.Parse_Response { return []*proto.Parse_Response{{ Type: &proto.Parse_Response_Complete{ diff --git a/coderd/workspacebuilds_test.go b/coderd/workspacebuilds_test.go index c362d21765564..bbdec82b4a30d 100644 --- a/coderd/workspacebuilds_test.go +++ b/coderd/workspacebuilds_test.go @@ -646,7 +646,7 @@ func TestWorkspaceBuildWithRichParameters(t *testing.T) { secondParameterValue = "2" immutableParameterName = "immutable_parameter" - immutableParameterDescription = "This is mutable parameter" + immutableParameterDescription = "This is immutable parameter" immutableParameterValue = "3" ) From 40a4aa03eb584d7eeb94b5f74a0c36bf87b7f10a Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 19 Jan 2023 18:52:50 +0100 Subject: [PATCH 12/15] CLI: update test --- cli/update_test.go | 91 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/cli/update_test.go b/cli/update_test.go index 73a30544d5ef6..ed933c3d914d3 100644 --- a/cli/update_test.go +++ b/cli/update_test.go @@ -3,6 +3,7 @@ package cli_test import ( "context" "fmt" + "os" "testing" "github.com/stretchr/testify/assert" @@ -12,6 +13,7 @@ import ( "github.com/coder/coder/coderd/coderdtest" "github.com/coder/coder/codersdk" "github.com/coder/coder/provisioner/echo" + "github.com/coder/coder/provisionersdk/proto" "github.com/coder/coder/pty/ptytest" ) @@ -139,3 +141,92 @@ func TestUpdate(t *testing.T) { <-doneChan }) } + +func TestUpdateWithRichParameters(t *testing.T) { + t.Parallel() + + const ( + firstParameterName = "first_parameter" + firstParameterDescription = "This is first parameter" + firstParameterValue = "1" + + secondParameterName = "second_parameter" + secondParameterDescription = "This is second parameter" + secondParameterValue = "2" + + immutableParameterName = "immutable_parameter" + immutableParameterDescription = "This is mutable parameter" + immutableParameterValue = "3" + ) + + echoResponses := &echo.Responses{ + Parse: echo.ParseComplete, + ProvisionPlan: []*proto.Provision_Response{ + { + Type: &proto.Provision_Response_Complete{ + Complete: &proto.Provision_Complete{ + Parameters: []*proto.RichParameter{ + {Name: firstParameterName, Description: firstParameterDescription, Mutable: true}, + {Name: secondParameterName, Description: secondParameterDescription, Mutable: true}, + {Name: immutableParameterName, Description: immutableParameterDescription, Mutable: false}, + }, + }, + }, + }}, + ProvisionApply: []*proto.Provision_Response{{ + Type: &proto.Provision_Response_Complete{ + Complete: &proto.Provision_Complete{}, + }, + }}, + } + + t.Run("ImmutableCannotBeCustomized", 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, echoResponses) + coderdtest.AwaitTemplateVersionJob(t, client, version.ID) + + template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) + + tempDir := t.TempDir() + removeTmpDirUntilSuccessAfterTest(t, tempDir) + parameterFile, _ := os.CreateTemp(tempDir, "testParameterFile*.yaml") + _, _ = parameterFile.WriteString( + firstParameterName + ": " + firstParameterValue + "\n" + + secondParameterName + ": " + secondParameterValue + "\n" + + immutableParameterName + ": " + immutableParameterValue) + + cmd, root := clitest.New(t, "create", "my-workspace", "--template", template.Name, "--rich-parameter-file", parameterFile.Name(), "-y") + clitest.SetupConfig(t, client, root) + err := cmd.Execute() + assert.NoError(t, err) + + cmd, root = clitest.New(t, "update", "my-workspace", "--always-prompt") + 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) + }() + + matches := []string{ + firstParameterDescription, firstParameterValue, + secondParameterDescription, secondParameterValue, + fmt.Sprintf("Parameter %q is not mutable, so can't be customized after workspace creation.", immutableParameterName), "yes", + } + for i := 0; i < len(matches); i += 2 { + match := matches[i] + value := matches[i+1] + pty.ExpectMatch(match) + pty.WriteLine(value) + } + <-doneChan + }) +} From 1af7b9a78faaa6959f64f35dee8212a003192263 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Thu, 19 Jan 2023 19:10:14 +0100 Subject: [PATCH 13/15] Fix --- cli/create.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cli/create.go b/cli/create.go index a5cf49048cf19..2c4d20e3a74e2 100644 --- a/cli/create.go +++ b/cli/create.go @@ -251,7 +251,10 @@ PromptParamLoop: DestinationScheme: parameterSchema.DefaultDestinationScheme, }) } - _, _ = fmt.Fprintln(cmd.OutOrStdout()) + + if disclaimerPrinted { + _, _ = fmt.Fprintln(cmd.OutOrStdout()) + } // Rich parameters templateVersionParameters, err := client.TemplateVersionRichParameters(cmd.Context(), templateVersion.ID) @@ -302,6 +305,10 @@ PromptRichParamLoop: richParameters = append(richParameters, *parameterValue) } + if disclaimerPrinted { + _, _ = fmt.Fprintln(cmd.OutOrStdout()) + } + // Run a dry-run with the given parameters to check correctness dryRun, err := client.CreateTemplateVersionDryRun(cmd.Context(), templateVersion.ID, codersdk.CreateTemplateVersionDryRunRequest{ WorkspaceName: args.NewWorkspaceName, From 8c989ee3adac6578bb28a910805f4ce0d0a51d93 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 20 Jan 2023 08:42:07 +0100 Subject: [PATCH 14/15] fix: order --- cli/update_test.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/cli/update_test.go b/cli/update_test.go index ed933c3d914d3..90e32b92c0262 100644 --- a/cli/update_test.go +++ b/cli/update_test.go @@ -167,8 +167,8 @@ func TestUpdateWithRichParameters(t *testing.T) { Complete: &proto.Provision_Complete{ Parameters: []*proto.RichParameter{ {Name: firstParameterName, Description: firstParameterDescription, Mutable: true}, - {Name: secondParameterName, Description: secondParameterDescription, Mutable: true}, {Name: immutableParameterName, Description: immutableParameterDescription, Mutable: false}, + {Name: secondParameterName, Description: secondParameterDescription, Mutable: true}, }, }, }, @@ -195,8 +195,8 @@ func TestUpdateWithRichParameters(t *testing.T) { parameterFile, _ := os.CreateTemp(tempDir, "testParameterFile*.yaml") _, _ = parameterFile.WriteString( firstParameterName + ": " + firstParameterValue + "\n" + - secondParameterName + ": " + secondParameterValue + "\n" + - immutableParameterName + ": " + immutableParameterValue) + immutableParameterName + ": " + immutableParameterValue + "\n" + + secondParameterName + ": " + secondParameterValue) cmd, root := clitest.New(t, "create", "my-workspace", "--template", template.Name, "--rich-parameter-file", parameterFile.Name(), "-y") clitest.SetupConfig(t, client, root) @@ -218,14 +218,16 @@ func TestUpdateWithRichParameters(t *testing.T) { matches := []string{ firstParameterDescription, firstParameterValue, + fmt.Sprintf("Parameter %q is not mutable, so can't be customized after workspace creation.", immutableParameterName), "", secondParameterDescription, secondParameterValue, - fmt.Sprintf("Parameter %q is not mutable, so can't be customized after workspace creation.", immutableParameterName), "yes", } for i := 0; i < len(matches); i += 2 { match := matches[i] value := matches[i+1] pty.ExpectMatch(match) - pty.WriteLine(value) + if value != "" { + pty.WriteLine(value) + } } <-doneChan }) From 6dbd044f5a7c628c50d5b4b9773eff4db918d631 Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Fri, 20 Jan 2023 09:06:43 +0100 Subject: [PATCH 15/15] fix --- cli/create_test.go | 4 ++-- cli/update_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/create_test.go b/cli/create_test.go index 3465ea1414fb5..03552dbd9bd43 100644 --- a/cli/create_test.go +++ b/cli/create_test.go @@ -333,8 +333,8 @@ func TestCreateWithRichParameters(t *testing.T) { secondParameterDescription = "This is second parameter" secondParameterValue = "2" - immutableParameterName = "immutable_parameter" - immutableParameterDescription = "This is mutable parameter" + immutableParameterName = "third_parameter" + immutableParameterDescription = "This is not mutable parameter" immutableParameterValue = "3" ) diff --git a/cli/update_test.go b/cli/update_test.go index 90e32b92c0262..250e768d48580 100644 --- a/cli/update_test.go +++ b/cli/update_test.go @@ -155,7 +155,7 @@ func TestUpdateWithRichParameters(t *testing.T) { secondParameterValue = "2" immutableParameterName = "immutable_parameter" - immutableParameterDescription = "This is mutable parameter" + immutableParameterDescription = "This is not mutable parameter" immutableParameterValue = "3" )