diff --git a/provisioner/echo/serve.go b/provisioner/echo/serve.go index 26d1fcbe3ad06..1ecdd69179753 100644 --- a/provisioner/echo/serve.go +++ b/provisioner/echo/serve.go @@ -12,6 +12,7 @@ import ( "text/template" "github.com/google/uuid" + "github.com/spf13/afero" "golang.org/x/xerrors" protobuf "google.golang.org/protobuf/proto" @@ -21,12 +22,12 @@ import ( "github.com/coder/coder/v2/provisionersdk/proto" ) -// ProvisionApplyWithAgent returns provision responses that will mock a fake +// ProvisionGraphWithAgentAndAPIKeyScope returns provision responses that will mock a fake // "aws_instance" resource with an agent that has the given auth token. -func ProvisionApplyWithAgentAndAPIKeyScope(authToken string, apiKeyScope string) []*proto.Response { +func ProvisionGraphWithAgentAndAPIKeyScope(authToken string, apiKeyScope string) []*proto.Response { return []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example_with_scope", Type: "aws_instance", @@ -44,24 +45,29 @@ func ProvisionApplyWithAgentAndAPIKeyScope(authToken string, apiKeyScope string) }} } -// ProvisionApplyWithAgent returns provision responses that will mock a fake +// ProvisionGraphWithAgent returns provision responses that will mock a fake // "aws_instance" resource with an agent that has the given auth token. -func ProvisionApplyWithAgent(authToken string) []*proto.Response { +func ProvisionGraphWithAgent(authToken string, muts ...func(g *proto.GraphComplete)) []*proto.Response { + gc := &proto.GraphComplete{ + Resources: []*proto.Resource{{ + Name: "example", + Type: "aws_instance", + Agents: []*proto.Agent{{ + Id: uuid.NewString(), + Name: "example", + Auth: &proto.Agent_Token{ + Token: authToken, + }, + }}, + }}, + } + for _, mut := range muts { + mut(gc) + } + return []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ - Resources: []*proto.Resource{{ - Name: "example", - Type: "aws_instance", - Agents: []*proto.Agent{{ - Id: uuid.NewString(), - Name: "example", - Auth: &proto.Agent_Token{ - Token: authToken, - }, - }}, - }}, - }, + Type: &proto.Response_Graph{ + Graph: gc, }, }} } @@ -73,12 +79,19 @@ var ( Parse: &proto.ParseComplete{}, }, }} + // InitComplete is a helper to indicate an empty init completion. + InitComplete = []*proto.Response{{ + Type: &proto.Response_Init{ + Init: &proto.InitComplete{ + ModuleFiles: []byte{}, + }, + }, + }} // PlanComplete is a helper to indicate an empty provision completion. PlanComplete = []*proto.Response{{ Type: &proto.Response_Plan{ Plan: &proto.PlanComplete{ - Plan: []byte("{}"), - ModuleFiles: []byte{}, + Plan: []byte("{}"), }, }, }} @@ -88,7 +101,19 @@ var ( Apply: &proto.ApplyComplete{}, }, }} + GraphComplete = []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{}, + }, + }} + InitFailed = []*proto.Response{{ + Type: &proto.Response_Init{ + Init: &proto.InitComplete{ + Error: "failed!", + }, + }, + }} // PlanFailed is a helper to convey a failed plan operation PlanFailed = []*proto.Response{{ Type: &proto.Response_Plan{ @@ -105,6 +130,13 @@ var ( }, }, }} + GraphFailed = []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ + Error: "failed!", + }, + }, + }} ) // Serve starts the echo provisioner. @@ -174,6 +206,59 @@ func (*echo) Parse(sess *provisionersdk.Session, _ *proto.ParseRequest, _ <-chan return provisionersdk.ParseErrorf("complete response missing") } +func (*echo) Init(sess *provisionersdk.Session, req *proto.InitRequest, canceledOrComplete <-chan struct{}) *proto.InitComplete { + err := sess.Files.ExtractArchive(sess.Context(), sess.Logger, afero.NewOsFs(), req.TemplateSourceArchive) + if err != nil { + return provisionersdk.InitErrorf("extract archive: %s", err.Error()) + } + + responses, err := readResponses( + sess, + "", // transition not supported for init graph responses + "init.protobuf") + if err != nil { + return &proto.InitComplete{Error: err.Error()} + } + for _, response := range responses { + if log := response.GetLog(); log != nil { + sess.ProvisionLog(log.Level, log.Output) + } + if complete := response.GetInit(); complete != nil { + return complete + } + } + + // some tests use Echo without a complete response to test cancel + <-canceledOrComplete + return provisionersdk.InitErrorf("canceled") +} + +func (*echo) Graph(sess *provisionersdk.Session, req *proto.GraphRequest, canceledOrComplete <-chan struct{}) *proto.GraphComplete { + responses, err := readResponses( + sess, + strings.ToLower(req.GetMetadata().GetWorkspaceTransition().String()), + "graph.protobuf") + if err != nil { + return &proto.GraphComplete{Error: err.Error()} + } + for _, response := range responses { + if log := response.GetLog(); log != nil { + sess.ProvisionLog(log.Level, log.Output) + } + if complete := response.GetGraph(); complete != nil { + if len(complete.AiTasks) > 0 { + // These two fields are linked; if there are AI tasks, indicate that. + complete.HasAiTasks = true + } + return complete + } + } + + // some tests use Echo without a complete response to test cancel + <-canceledOrComplete + return provisionersdk.GraphError("canceled") +} + // Plan reads requests from the provided directory to stream responses. func (*echo) Plan(sess *provisionersdk.Session, req *proto.PlanRequest, canceledOrComplete <-chan struct{}) *proto.PlanComplete { responses, err := readResponses( @@ -228,19 +313,73 @@ func (*echo) Shutdown(_ context.Context, _ *proto.Empty) (*proto.Empty, error) { type Responses struct { Parse []*proto.Response - // ProvisionApply and ProvisionPlan are used to mock ALL responses of - // Apply and Plan, regardless of transition. - ProvisionApply []*proto.Response + // Used to mock ALL responses regardless of transition. + ProvisionInit []*proto.Response ProvisionPlan []*proto.Response + ProvisionApply []*proto.Response + ProvisionGraph []*proto.Response - // ProvisionApplyMap and ProvisionPlanMap are used to mock specific - // transition responses. They are prioritized over the generic responses. - ProvisionApplyMap map[proto.WorkspaceTransition][]*proto.Response + // Used to mock specific transition responses. They are prioritized over the generic responses. ProvisionPlanMap map[proto.WorkspaceTransition][]*proto.Response + ProvisionApplyMap map[proto.WorkspaceTransition][]*proto.Response + ProvisionGraphMap map[proto.WorkspaceTransition][]*proto.Response ExtraFiles map[string][]byte } +func isType[T any](x any) bool { + _, ok := x.(T) + return ok +} + +func (r *Responses) Valid() error { + isLog := isType[*proto.Response_Log] + isParse := isType[*proto.Response_Parse] + isInit := isType[*proto.Response_Init] + isDataUpload := isType[*proto.Response_DataUpload] + isChunkPiece := isType[*proto.Response_ChunkPiece] + isPlan := isType[*proto.Response_Plan] + isApply := isType[*proto.Response_Apply] + isGraph := isType[*proto.Response_Graph] + + for _, parse := range r.Parse { + ty := parse.Type + if !(isParse(ty) || isLog(ty)) { + return xerrors.Errorf("invalid parse response type: %T", ty) + } + } + + for _, init := range r.ProvisionInit { + ty := init.Type + if !(isInit(ty) || isLog(ty) || isChunkPiece(ty) || isDataUpload(ty)) { + return xerrors.Errorf("invalid init response type: %T", ty) + } + } + + for _, plan := range r.ProvisionPlan { + ty := plan.Type + if !(isPlan(ty) || isLog(ty)) { + return xerrors.Errorf("invalid plan response type: %T", ty) + } + } + + for _, apply := range r.ProvisionApply { + ty := apply.Type + if !(isApply(ty) || isLog(ty)) { + return xerrors.Errorf("invalid apply response type: %T", ty) + } + } + + for _, graph := range r.ProvisionGraph { + ty := graph.Type + if !(isGraph(ty) || isLog(ty)) { + return xerrors.Errorf("invalid graph response type: %T", ty) + } + } + + return nil +} + // Tar returns a tar archive of responses to provisioner operations. func Tar(responses *Responses) ([]byte, error) { logger := slog.Make() @@ -255,27 +394,74 @@ func TarWithOptions(ctx context.Context, logger slog.Logger, responses *Response if responses == nil { responses = &Responses{ Parse: ParseComplete, - ProvisionApply: ApplyComplete, + ProvisionInit: InitComplete, ProvisionPlan: PlanComplete, + ProvisionApply: ApplyComplete, + ProvisionGraph: GraphComplete, ProvisionApplyMap: nil, ProvisionPlanMap: nil, ExtraFiles: nil, } } - if responses.ProvisionPlan == nil { + // source apply from the graph if graph exists + if responses.ProvisionApply == nil && len(responses.ProvisionGraph) > 0 { + for _, resp := range responses.ProvisionGraph { + if resp.GetLog() != nil { + responses.ProvisionApply = append(responses.ProvisionApply, resp) + continue + } + responses.ProvisionApply = append(responses.ProvisionApply, &proto.Response{ + Type: &proto.Response_Apply{Apply: &proto.ApplyComplete{ + Error: resp.GetGraph().GetError(), + }}, + }) + } + } + if responses.ProvisionGraph == nil { for _, resp := range responses.ProvisionApply { + if resp.GetLog() != nil { + responses.ProvisionGraph = append(responses.ProvisionGraph, resp) + continue + } + responses.ProvisionGraph = append(responses.ProvisionGraph, &proto.Response{ + Type: &proto.Response_Graph{Graph: &proto.GraphComplete{ + Error: resp.GetApply().GetError(), + }}, + }) + } + } + if responses.ProvisionInit == nil { + for _, resp := range responses.ProvisionGraph { + if resp.GetLog() != nil { + responses.ProvisionInit = append(responses.ProvisionInit, resp) + continue + } + responses.ProvisionInit = append(responses.ProvisionInit, &proto.Response{ + Type: &proto.Response_Init{ + Init: &proto.InitComplete{ + Error: resp.GetGraph().GetError(), + Timings: nil, + Modules: nil, + ModuleFiles: nil, + ModuleFilesHash: nil, + }, + }, + }, + ) + } + } + if responses.ProvisionPlan == nil { + for _, resp := range responses.ProvisionGraph { if resp.GetLog() != nil { responses.ProvisionPlan = append(responses.ProvisionPlan, resp) continue } responses.ProvisionPlan = append(responses.ProvisionPlan, &proto.Response{ Type: &proto.Response_Plan{Plan: &proto.PlanComplete{ - Error: resp.GetApply().GetError(), - Resources: resp.GetApply().GetResources(), - Parameters: resp.GetApply().GetParameters(), - ExternalAuthProviders: resp.GetApply().GetExternalAuthProviders(), - Plan: []byte("{}"), - ModuleFiles: []byte{}, + Error: resp.GetGraph().GetError(), + Plan: []byte("{}"), + //nolint:gosec // the number of resources will not exceed int32 + AiTaskCount: int32(len(resp.GetGraph().GetAiTasks())), }}, }) } @@ -315,6 +501,13 @@ func TarWithOptions(ctx context.Context, logger slog.Logger, responses *Response if err != nil { return err } + + response := new(proto.Response) + err = protobuf.Unmarshal(data, response) + if err != nil { + return xerrors.Errorf("you must have saved the wrong type, the proto cannot unmarshal: %w", err) + } + logger.Debug(context.Background(), "proto written", slog.F("name", name), slog.F("bytes_written", n)) return nil @@ -325,6 +518,12 @@ func TarWithOptions(ctx context.Context, logger slog.Logger, responses *Response return nil, err } } + for index, response := range responses.ProvisionInit { + err := writeProto(fmt.Sprintf("%d.init.protobuf", index), response) + if err != nil { + return nil, err + } + } for index, response := range responses.ProvisionApply { err := writeProto(fmt.Sprintf("%d.apply.protobuf", index), response) if err != nil { @@ -337,6 +536,12 @@ func TarWithOptions(ctx context.Context, logger slog.Logger, responses *Response return nil, err } } + for index, response := range responses.ProvisionGraph { + err := writeProto(fmt.Sprintf("%d.graph.protobuf", index), response) + if err != nil { + return nil, err + } + } for trans, m := range responses.ProvisionApplyMap { for i, rs := range m { err := writeProto(fmt.Sprintf("%d.%s.apply.protobuf", i, strings.ToLower(trans.String())), rs) @@ -360,6 +565,14 @@ func TarWithOptions(ctx context.Context, logger slog.Logger, responses *Response } } } + for trans, m := range responses.ProvisionGraphMap { + for i, resp := range m { + err := writeProto(fmt.Sprintf("%d.%s.graph.protobuf", i, strings.ToLower(trans.String())), resp) + if err != nil { + return nil, err + } + } + } dirs := []string{} for name, content := range responses.ExtraFiles { logger.Debug(ctx, "extra file", slog.F("name", name)) @@ -401,8 +614,8 @@ func TarWithOptions(ctx context.Context, logger slog.Logger, responses *Response // that matches the parameters defined in the responses. Dynamic parameters // parsed these, even in the echo provisioner. var mainTF bytes.Buffer - for _, respPlan := range responses.ProvisionPlan { - plan := respPlan.GetPlan() + for _, respPlan := range responses.ProvisionGraph { + plan := respPlan.GetGraph() if plan == nil { continue } @@ -440,6 +653,11 @@ terraform { if err != nil { return nil, err } + + if err := responses.Valid(); err != nil { + return nil, xerrors.Errorf("responses invalid: %w", err) + } + return buffer.Bytes(), nil } @@ -508,13 +726,14 @@ data "coder_parameter" "{{ .Name }}" { func WithResources(resources []*proto.Resource) *Responses { return &Responses{ - Parse: ParseComplete, - ProvisionApply: []*proto.Response{{Type: &proto.Response_Apply{Apply: &proto.ApplyComplete{ + Parse: ParseComplete, + ProvisionInit: InitComplete, + ProvisionApply: []*proto.Response{{Type: &proto.Response_Apply{Apply: &proto.ApplyComplete{}}}}, + ProvisionGraph: []*proto.Response{{Type: &proto.Response_Graph{Graph: &proto.GraphComplete{ Resources: resources, }}}}, ProvisionPlan: []*proto.Response{{Type: &proto.Response_Plan{Plan: &proto.PlanComplete{ - Resources: resources, - Plan: []byte("{}"), + Plan: []byte("{}"), }}}}, } } @@ -522,8 +741,10 @@ func WithResources(resources []*proto.Resource) *Responses { func WithExtraFiles(extraFiles map[string][]byte) *Responses { return &Responses{ Parse: ParseComplete, + ProvisionInit: InitComplete, ProvisionApply: ApplyComplete, ProvisionPlan: PlanComplete, + ProvisionGraph: GraphComplete, ExtraFiles: extraFiles, } } diff --git a/provisioner/terraform/executor.go b/provisioner/terraform/executor.go index b132b78982bea..a8d093aa9ad46 100644 --- a/provisioner/terraform/executor.go +++ b/provisioner/terraform/executor.go @@ -23,7 +23,6 @@ import ( "cdr.dev/slog" "github.com/coder/coder/v2/provisionersdk/tfpath" - "github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/tracing" "github.com/coder/coder/v2/provisionersdk/proto" ) @@ -283,7 +282,7 @@ func (e *executor) init(ctx, killCtx context.Context, logr logSink) error { func checksumFileCRC32(ctx context.Context, logger slog.Logger, path string) uint32 { content, err := os.ReadFile(path) if err != nil { - logger.Debug(ctx, "file %s does not exist or can't be read, skip checksum calculation") + logger.Debug(ctx, "file does not exist or can't be read, skip checksum calculation", slog.F("path", path)) return 0 } return crc32.ChecksumIEEE(content) @@ -330,34 +329,16 @@ func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr l return nil, xerrors.Errorf("terraform plan: %w", err) } - // Capture the duration of the call to `terraform graph`. - graphTimings := newTimingAggregator(database.ProvisionerJobTimingStageGraph) - graphTimings.ingest(createGraphTimingsEvent(timingGraphStart)) - - state, plan, err := e.planResources(ctx, killCtx, planfilePath) + plan, err := e.parsePlan(ctx, killCtx, planfilePath) if err != nil { - graphTimings.ingest(createGraphTimingsEvent(timingGraphErrored)) - return nil, xerrors.Errorf("plan resources: %w", err) + return nil, xerrors.Errorf("show terraform plan file: %w", err) } + planJSON, err := json.Marshal(plan) if err != nil { return nil, xerrors.Errorf("marshal plan: %w", err) } - graphTimings.ingest(createGraphTimingsEvent(timingGraphComplete)) - - var moduleFiles []byte - // Skipping modules archiving is useful if the caller does not need it, eg during - // a workspace build. This removes some added costs of sending the modules - // payload back to coderd if coderd is just going to ignore it. - if !req.OmitModuleFiles { - moduleFiles, err = GetModulesArchive(os.DirFS(e.files.WorkDirectory())) - if err != nil { - // TODO: we probably want to persist this error or make it louder eventually - e.logger.Warn(ctx, "failed to archive terraform modules", slog.Error(err)) - } - } - // When a prebuild claim attempt is made, log a warning if a resource is due to be replaced, since this will obviate // the point of prebuilding if the expensive resource is replaced once claimed! var ( @@ -384,18 +365,16 @@ func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr l } } + state, err := ConvertPlanState(plan) + if err != nil { + return nil, xerrors.Errorf("convert plan state: %w", err) + } + msg := &proto.PlanComplete{ - Parameters: state.Parameters, - Resources: state.Resources, - ExternalAuthProviders: state.ExternalAuthProviders, - Timings: graphTimings.aggregate(), - Presets: state.Presets, - Plan: planJSON, - ResourceReplacements: resReps, - ModuleFiles: moduleFiles, - HasAiTasks: state.HasAITasks, - AiTasks: state.AITasks, - HasExternalAgents: state.HasExternalAgents, + Plan: planJSON, + DailyCost: state.DailyCost, + ResourceReplacements: resReps, + AiTaskCount: state.AITaskCount, } return msg, nil @@ -418,42 +397,6 @@ func onlyDataResources(sm tfjson.StateModule) tfjson.StateModule { return filtered } -// planResources must only be called while the lock is held. -func (e *executor) planResources(ctx, killCtx context.Context, planfilePath string) (*State, *tfjson.Plan, error) { - ctx, span := e.server.startTrace(ctx, tracing.FuncName()) - defer span.End() - - plan, err := e.parsePlan(ctx, killCtx, planfilePath) - if err != nil { - return nil, nil, xerrors.Errorf("show terraform plan file: %w", err) - } - - rawGraph, err := e.graph(ctx, killCtx) - if err != nil { - return nil, nil, xerrors.Errorf("graph: %w", err) - } - modules := []*tfjson.StateModule{} - if plan.PriorState != nil { - // We need the data resources for rich parameters. For some reason, they - // only show up in the PriorState. - // - // We don't want all prior resources, because Quotas (and - // future features) would never know which resources are getting - // deleted by a stop. - - filtered := onlyDataResources(*plan.PriorState.Values.RootModule) - modules = append(modules, &filtered) - } - modules = append(modules, plan.PlannedValues.RootModule) - - state, err := ConvertState(ctx, modules, rawGraph, e.server.logger) - if err != nil { - return nil, nil, err - } - - return state, plan, nil -} - // parsePlan must only be called while the lock is held. func (e *executor) parsePlan(ctx, killCtx context.Context, planfilePath string) (*tfjson.Plan, error) { ctx, span := e.server.startTrace(ctx, tracing.FuncName()) @@ -541,9 +484,11 @@ func (e *executor) graph(ctx, killCtx context.Context) (string, error) { // TODO: When the plan is present, we should probably use it? // "-plan=" + e.files.PlanFilePath(), } + if ver.GreaterThanOrEqual(version170) { args = append(args, "-type=plan") } + var out strings.Builder cmd := exec.CommandContext(killCtx, e.binaryPath, args...) // #nosec cmd.Stdout = &out @@ -602,11 +547,6 @@ func (e *executor) apply( return nil, xerrors.Errorf("terraform apply: %w", err) } - // `terraform show` & `terraform graph` - state, err := e.stateResources(ctx, killCtx) - if err != nil { - return nil, err - } statefilePath := e.files.StateFilePath() stateContent, err := os.ReadFile(statefilePath) if err != nil { @@ -614,41 +554,10 @@ func (e *executor) apply( } return &proto.ApplyComplete{ - Parameters: state.Parameters, - Resources: state.Resources, - ExternalAuthProviders: state.ExternalAuthProviders, - State: stateContent, - AiTasks: state.AITasks, + State: stateContent, }, nil } -// stateResources must only be called while the lock is held. -func (e *executor) stateResources(ctx, killCtx context.Context) (*State, error) { - ctx, span := e.server.startTrace(ctx, tracing.FuncName()) - defer span.End() - - state, err := e.state(ctx, killCtx) - if err != nil { - return nil, err - } - rawGraph, err := e.graph(ctx, killCtx) - if err != nil { - return nil, xerrors.Errorf("get terraform graph: %w", err) - } - converted := &State{} - if state.Values == nil { - return converted, nil - } - - converted, err = ConvertState(ctx, []*tfjson.StateModule{ - state.Values.RootModule, - }, rawGraph, e.server.logger) - if err != nil { - return nil, err - } - return converted, nil -} - // state must only be called while the lock is held. func (e *executor) state(ctx, killCtx context.Context) (*tfjson.State, error) { ctx, span := e.server.startTrace(ctx, tracing.FuncName()) diff --git a/provisioner/terraform/planresources.go b/provisioner/terraform/planresources.go new file mode 100644 index 0000000000000..3c3758df1d373 --- /dev/null +++ b/provisioner/terraform/planresources.go @@ -0,0 +1,80 @@ +package terraform + +import ( + tfjson "github.com/hashicorp/terraform-json" + "github.com/mitchellh/mapstructure" + "golang.org/x/xerrors" +) + +type PlanState struct { + DailyCost int32 + AITaskCount int32 +} + +func planModules(plan *tfjson.Plan) []*tfjson.StateModule { + modules := []*tfjson.StateModule{} + if plan.PriorState != nil { + // We need the data resources for rich parameters. For some reason, they + // only show up in the PriorState. + // + // We don't want all prior resources, because Quotas (and + // future features) would never know which resources are getting + // deleted by a stop. + + filtered := onlyDataResources(*plan.PriorState.Values.RootModule) + modules = append(modules, &filtered) + } + modules = append(modules, plan.PlannedValues.RootModule) + return modules +} + +// ConvertPlanState consumes a terraform plan json output and produces a thinner +// version of `State` to be used before `terraform apply`. `ConvertState` +// requires `terraform graph`, this does not. +func ConvertPlanState(plan *tfjson.Plan) (*PlanState, error) { + modules := planModules(plan) + + var dailyCost int32 + var aiTaskCount int32 + for _, mod := range modules { + err := forEachResource(mod, func(res *tfjson.StateResource) error { + switch res.Type { + case "coder_metadata": + var attrs resourceMetadataAttributes + err := mapstructure.Decode(res.AttributeValues, &attrs) + if err != nil { + return xerrors.Errorf("decode metadata attributes: %w", err) + } + dailyCost += attrs.DailyCost + case "coder_ai_task": + aiTaskCount++ + } + return nil + }) + if err != nil { + return nil, xerrors.Errorf("parse plan: %w", err) + } + } + + return &PlanState{ + DailyCost: dailyCost, + AITaskCount: aiTaskCount, + }, nil +} + +func forEachResource(input *tfjson.StateModule, do func(res *tfjson.StateResource) error) error { + for _, res := range input.Resources { + err := do(res) + if err != nil { + return xerrors.Errorf("in module %s: %w", input.Address, err) + } + } + + for _, mod := range input.ChildModules { + err := forEachResource(mod, do) + if err != nil { + return xerrors.Errorf("in module %s: %w", mod.Address, err) + } + } + return nil +} diff --git a/provisioner/terraform/provision.go b/provisioner/terraform/provision.go index 5fedf16c21eb1..f977d14573bbf 100644 --- a/provisioner/terraform/provision.go +++ b/provisioner/terraform/provision.go @@ -12,6 +12,7 @@ import ( "strings" "time" + tfjson "github.com/hashicorp/terraform-json" "github.com/spf13/afero" "golang.org/x/xerrors" @@ -67,51 +68,34 @@ func (s *server) setupContexts(parent context.Context, canceledOrComplete <-chan return ctx, cancel, killCtx, kill } -func (s *server) Plan( - sess *provisionersdk.Session, request *proto.PlanRequest, canceledOrComplete <-chan struct{}, -) *proto.PlanComplete { +func (s *server) Init( + sess *provisionersdk.Session, request *proto.InitRequest, canceledOrComplete <-chan struct{}, +) *proto.InitComplete { ctx, span := s.startTrace(sess.Context(), tracing.FuncName()) defer span.End() ctx, cancel, killCtx, kill := s.setupContexts(ctx, canceledOrComplete) defer cancel() defer kill() - e := s.executor(sess.Files, database.ProvisionerJobTimingStagePlan) + e := s.executor(sess.Files, database.ProvisionerJobTimingStageInit) if err := e.checkMinVersion(ctx); err != nil { - return provisionersdk.PlanErrorf("%s", err.Error()) + return provisionersdk.InitErrorf("%s", err.Error()) } logTerraformEnvVars(sess) - // If we're destroying, exit early if there's no state. This is necessary to - // avoid any cases where a workspace is "locked out" of terraform due to - // e.g. bad template param values and cannot be deleted. This is just for - // contingency, in the future we will try harder to prevent workspaces being - // broken this hard. - if request.Metadata.GetWorkspaceTransition() == proto.WorkspaceTransition_DESTROY && len(sess.Config.State) == 0 { - sess.ProvisionLog(proto.LogLevel_INFO, "The terraform state does not exist, there is nothing to do") - return &proto.PlanComplete{} - } - - statefilePath := sess.Files.StateFilePath() - if len(sess.Config.State) > 0 { - err := os.WriteFile(statefilePath, sess.Config.State, 0o600) - if err != nil { - return provisionersdk.PlanErrorf("write statefile %q: %s", statefilePath, err) - } + // TODO: These logs should probably be streamed back to the provisioner runner. + err := sess.Files.ExtractArchive(ctx, s.logger, afero.NewOsFs(), request.GetTemplateSourceArchive()) + if err != nil { + return provisionersdk.InitErrorf("extract template archive: %s", err) } - err := CleanStaleTerraformPlugins(sess.Context(), s.cachePath, afero.NewOsFs(), time.Now(), s.logger) + err = CleanStaleTerraformPlugins(sess.Context(), s.cachePath, afero.NewOsFs(), time.Now(), s.logger) if err != nil { - return provisionersdk.PlanErrorf("unable to clean stale Terraform plugins: %s", err) + return provisionersdk.InitErrorf("unable to clean stale Terraform plugins: %s", err) } - s.logger.Debug(ctx, "running initialization") - - // The JSON output of `terraform init` doesn't include discrete fields for capturing timings of each plugin, - // so we capture the whole init process. - initTimings := newTimingAggregator(database.ProvisionerJobTimingStageInit) - endStage := initTimings.startStage(database.ProvisionerJobTimingStageInit) - + s.logger.Debug(ctx, "running terraform initialization") + endStage := e.timings.startStage(database.ProvisionerJobTimingStageInit) err = e.init(ctx, killCtx, sess) endStage(err) if err != nil { @@ -137,7 +121,7 @@ func (s *server) Plan( slog.F("provider_coder_stacktrace", stacktrace), ) } - return provisionersdk.PlanErrorf("initialize terraform: %s", err) + return provisionersdk.InitErrorf("initialize terraform: %s", err) } modules, err := getModules(sess.Files) @@ -147,8 +131,61 @@ func (s *server) Plan( s.logger.Error(ctx, "failed to get modules from disk", slog.Error(err)) } + var moduleFiles []byte + // Skipping modules archiving is useful if the caller does not need it, eg during + // a workspace build. This removes some added costs of sending the modules + // payload back to coderd if coderd is just going to ignore it. + if !request.OmitModuleFiles { + moduleFiles, err = GetModulesArchive(os.DirFS(e.files.WorkDirectory())) + if err != nil { + // TODO: we probably want to persist this error or make it louder eventually + e.logger.Warn(ctx, "failed to archive terraform modules", slog.Error(err)) + } + } + s.logger.Debug(ctx, "ran initialization") + return &proto.InitComplete{ + Timings: e.timings.aggregate(), + Modules: modules, + ModuleFiles: moduleFiles, + ModuleFilesHash: nil, + } +} + +func (s *server) Plan( + sess *provisionersdk.Session, request *proto.PlanRequest, canceledOrComplete <-chan struct{}, +) *proto.PlanComplete { + ctx, span := s.startTrace(sess.Context(), tracing.FuncName()) + defer span.End() + ctx, cancel, killCtx, kill := s.setupContexts(ctx, canceledOrComplete) + defer cancel() + defer kill() + + e := s.executor(sess.Files, database.ProvisionerJobTimingStagePlan) + if err := e.checkMinVersion(ctx); err != nil { + return provisionersdk.PlanErrorf("%s", err.Error()) + } + logTerraformEnvVars(sess) + + // If we're destroying, exit early if there's no state. This is necessary to + // avoid any cases where a workspace is "locked out" of terraform due to + // e.g. bad template param values and cannot be deleted. This is just for + // contingency, in the future we will try harder to prevent workspaces being + // broken this hard. + if request.Metadata.GetWorkspaceTransition() == proto.WorkspaceTransition_DESTROY && len(request.GetState()) == 0 { + sess.ProvisionLog(proto.LogLevel_INFO, "The terraform state does not exist, there is nothing to do") + return &proto.PlanComplete{} + } + + statefilePath := sess.Files.StateFilePath() + if len(request.GetState()) > 0 { + err := os.WriteFile(statefilePath, request.GetState(), 0o600) + if err != nil { + return provisionersdk.PlanErrorf("write statefile %q: %s", statefilePath, err) + } + } + env, err := provisionEnv(sess.Config, request.Metadata, request.PreviousParameterValues, request.RichParameterValues, request.ExternalAuthProviders) if err != nil { return provisionersdk.PlanErrorf("setup env: %s", err) @@ -160,20 +197,78 @@ func (s *server) Plan( return provisionersdk.PlanErrorf("plan vars: %s", err) } - endPlanStage := e.timings.startStage(database.ProvisionerJobTimingStagePlan) + endStage := e.timings.startStage(database.ProvisionerJobTimingStagePlan) resp, err := e.plan(ctx, killCtx, env, vars, sess, request) - endPlanStage(err) + endStage(err) if err != nil { return provisionersdk.PlanErrorf("%s", err.Error()) } - // Prepend init timings since they occur prior to plan timings. - // Order is irrelevant; this is merely indicative. - resp.Timings = append(resp.Timings, append(initTimings.aggregate(), e.timings.aggregate()...)...) - resp.Modules = modules + resp.Timings = e.timings.aggregate() return resp } +func (s *server) Graph( + sess *provisionersdk.Session, request *proto.GraphRequest, canceledOrComplete <-chan struct{}, +) *proto.GraphComplete { + ctx, span := s.startTrace(sess.Context(), tracing.FuncName()) + defer span.End() + ctx, cancel, killCtx, kill := s.setupContexts(ctx, canceledOrComplete) + defer cancel() + defer kill() + + e := s.executor(sess.Files, database.ProvisionerJobTimingStageGraph) + if err := e.checkMinVersion(ctx); err != nil { + return provisionersdk.GraphError("%s", err.Error()) + } + logTerraformEnvVars(sess) + + modules := []*tfjson.StateModule{} + switch request.Source { + case proto.GraphSource_SOURCE_PLAN: + plan, err := e.parsePlan(ctx, killCtx, e.files.PlanFilePath()) + if err != nil { + return provisionersdk.GraphError("parse plan for graph: %s", err) + } + + modules = planModules(plan) + case proto.GraphSource_SOURCE_STATE: + tfState, err := e.state(ctx, killCtx) + if err != nil { + return provisionersdk.GraphError("load tfstate for graph: %s", err) + } + if tfState.Values != nil { + modules = []*tfjson.StateModule{tfState.Values.RootModule} + } + default: + return provisionersdk.GraphError("unknown graph source: %q", request.Source.String()) + } + + endStage := e.timings.startStage(database.ProvisionerJobTimingStageGraph) + rawGraph, err := e.graph(ctx, killCtx) + endStage(err) + if err != nil { + return provisionersdk.GraphError("generate graph: %s", err) + } + + state, err := ConvertState(ctx, modules, rawGraph, e.server.logger) + if err != nil { + return provisionersdk.GraphError("convert state for graph: %s", err) + } + + return &proto.GraphComplete{ + Error: "", + Timings: e.timings.aggregate(), + Resources: state.Resources, + Parameters: state.Parameters, + ExternalAuthProviders: state.ExternalAuthProviders, + Presets: state.Presets, + HasAiTasks: state.HasAITasks, + AiTasks: state.AITasks, + HasExternalAgents: state.HasExternalAgents, + } +} + func (s *server) Apply( sess *provisionersdk.Session, request *proto.ApplyRequest, canceledOrComplete <-chan struct{}, ) *proto.ApplyComplete { @@ -194,7 +289,7 @@ func (s *server) Apply( // e.g. bad template param values and cannot be deleted. This is just for // contingency, in the future we will try harder to prevent workspaces being // broken this hard. - if request.Metadata.GetWorkspaceTransition() == proto.WorkspaceTransition_DESTROY && len(sess.Config.State) == 0 { + if request.Metadata.GetWorkspaceTransition() == proto.WorkspaceTransition_DESTROY && len(request.GetState()) == 0 { sess.ProvisionLog(proto.LogLevel_INFO, "The terraform plan does not exist, there is nothing to do") return &proto.ApplyComplete{} } @@ -217,8 +312,9 @@ func (s *server) Apply( // In this case, we return Complete with an explicit error message. stateData, _ := os.ReadFile(statefilePath) return &proto.ApplyComplete{ - State: stateData, - Error: errorMessage, + State: stateData, + Error: errorMessage, + Timings: e.timings.aggregate(), } } resp.Timings = e.timings.aggregate() diff --git a/provisioner/terraform/timings.go b/provisioner/terraform/timings.go index 0b150d2eafd4d..05b4e0cf8bbc7 100644 --- a/provisioner/terraform/timings.go +++ b/provisioner/terraform/timings.go @@ -281,12 +281,3 @@ func (e *timingSpan) toProto() *proto.Timing { State: e.state, } } - -func createGraphTimingsEvent(event timingKind) (time.Time, *timingSpan) { - return dbtime.Now(), &timingSpan{ - kind: event, - action: "building terraform dependency graph", - provider: "terraform", - resource: "state file", - } -} diff --git a/provisionerd/runner/apply.go b/provisionerd/runner/apply.go new file mode 100644 index 0000000000000..752d98024db8a --- /dev/null +++ b/provisionerd/runner/apply.go @@ -0,0 +1,64 @@ +package runner + +import ( + "context" + "time" + + "cdr.dev/slog" + "github.com/coder/coder/v2/provisionerd/proto" + sdkproto "github.com/coder/coder/v2/provisionersdk/proto" +) + +func (r *Runner) apply(ctx context.Context, stage string, req *sdkproto.ApplyRequest) ( + *sdkproto.ApplyComplete, *proto.FailedJob, +) { + // use the notStopped so that if we attempt to gracefully cancel, the stream + // will still be available for us to send the cancel to the provisioner + err := r.session.Send(&sdkproto.Request{Type: &sdkproto.Request_Apply{Apply: req}}) + if err != nil { + return nil, r.failedWorkspaceBuildf("start provision: %s", err) + } + nevermind := make(chan struct{}) + defer close(nevermind) + go func() { + select { + case <-nevermind: + return + case <-r.notStopped.Done(): + return + case <-r.notCanceled.Done(): + _ = r.session.Send(&sdkproto.Request{ + Type: &sdkproto.Request_Cancel{ + Cancel: &sdkproto.CancelRequest{}, + }, + }) + } + }() + + for { + msg, err := r.session.Recv() + if err != nil { + return nil, r.failedWorkspaceBuildf("recv workspace provision: %s", err) + } + switch msgType := msg.Type.(type) { + case *sdkproto.Response_Log: + r.logProvisionerJobLog(context.Background(), msgType.Log.Level, "workspace provisioner job logged", + slog.F("level", msgType.Log.Level), + slog.F("output", msgType.Log.Output), + slog.F("workspace_build_id", r.job.GetWorkspaceBuild().WorkspaceBuildId), + ) + + r.queueLog(ctx, &proto.Log{ + Source: proto.LogSource_PROVISIONER, + Level: msgType.Log.Level, + CreatedAt: time.Now().UnixMilli(), + Output: msgType.Log.Output, + Stage: stage, + }) + case *sdkproto.Response_Apply: + return msgType.Apply, nil + default: + return nil, r.failedJobf("unexpected plan response type %T", msg.Type) + } + } +} diff --git a/provisionerd/runner/graph.go b/provisionerd/runner/graph.go new file mode 100644 index 0000000000000..53461f35f72c7 --- /dev/null +++ b/provisionerd/runner/graph.go @@ -0,0 +1,64 @@ +package runner + +import ( + "context" + "time" + + "cdr.dev/slog" + "github.com/coder/coder/v2/coderd/tracing" + "github.com/coder/coder/v2/provisionerd/proto" + sdkproto "github.com/coder/coder/v2/provisionersdk/proto" +) + +func (r *Runner) graph(ctx context.Context, req *sdkproto.GraphRequest) (*sdkproto.GraphComplete, *proto.FailedJob) { + ctx, span := r.startTrace(ctx, tracing.FuncName()) + defer span.End() + + err := r.session.Send(&sdkproto.Request{Type: &sdkproto.Request_Graph{Graph: req}}) + if err != nil { + return nil, r.failedJobf("send graph request: %v", err) + } + + nevermind := make(chan struct{}) + defer close(nevermind) + go func() { + select { + case <-nevermind: + return + case <-r.notStopped.Done(): + return + case <-r.notCanceled.Done(): + _ = r.session.Send(&sdkproto.Request{ + Type: &sdkproto.Request_Cancel{ + Cancel: &sdkproto.CancelRequest{}, + }, + }) + } + }() + + for { + msg, err := r.session.Recv() + if err != nil { + return nil, r.failedJobf("receive graph response: %v", err) + } + switch msgType := msg.Type.(type) { + case *sdkproto.Response_Log: + r.logProvisionerJobLog(context.Background(), msgType.Log.Level, "terraform graphing", + slog.F("level", msgType.Log.Level), + slog.F("output", msgType.Log.Output), + ) + + r.queueLog(ctx, &proto.Log{ + Source: proto.LogSource_PROVISIONER, + Level: msgType.Log.Level, + CreatedAt: time.Now().UnixMilli(), + Output: msgType.Log.Output, + Stage: "Graphing Infrastructure", + }) + case *sdkproto.Response_Graph: + return msgType.Graph, nil + default: + return nil, r.failedJobf("unexpected graph response type %T", msg.Type) + } + } +} diff --git a/provisionerd/runner/init.go b/provisionerd/runner/init.go new file mode 100644 index 0000000000000..975d37708e23e --- /dev/null +++ b/provisionerd/runner/init.go @@ -0,0 +1,113 @@ +package runner + +import ( + "bytes" + "context" + "time" + + "cdr.dev/slog" + "github.com/coder/coder/v2/coderd/tracing" + "github.com/coder/coder/v2/provisionerd/proto" + sdkproto "github.com/coder/coder/v2/provisionersdk/proto" +) + +//nolint:revive +func (r *Runner) init(ctx context.Context, omitModules bool, templateArchive []byte) (*sdkproto.InitComplete, *proto.FailedJob) { + ctx, span := r.startTrace(ctx, tracing.FuncName()) + defer span.End() + + err := r.session.Send(&sdkproto.Request{Type: &sdkproto.Request_Init{Init: &sdkproto.InitRequest{ + TemplateSourceArchive: templateArchive, + OmitModuleFiles: omitModules, + }}}) + if err != nil { + return nil, r.failedJobf("send init request: %v", err) + } + + nevermind := make(chan struct{}) + defer close(nevermind) + go func() { + select { + case <-nevermind: + return + case <-r.notStopped.Done(): + return + case <-r.notCanceled.Done(): + _ = r.session.Send(&sdkproto.Request{ + Type: &sdkproto.Request_Cancel{ + Cancel: &sdkproto.CancelRequest{}, + }, + }) + } + }() + + var moduleFilesUpload *sdkproto.DataBuilder + for { + msg, err := r.session.Recv() + if err != nil { + return nil, r.failedJobf("receive init response: %v", err) + } + switch msgType := msg.Type.(type) { + case *sdkproto.Response_Log: + r.logProvisionerJobLog(context.Background(), msgType.Log.Level, "terraform initialization", + slog.F("level", msgType.Log.Level), + slog.F("output", msgType.Log.Output), + ) + + r.queueLog(ctx, &proto.Log{ + Source: proto.LogSource_PROVISIONER, + Level: msgType.Log.Level, + CreatedAt: time.Now().UnixMilli(), + Output: msgType.Log.Output, + Stage: "Initializing Terraform Directory", + }) + case *sdkproto.Response_DataUpload: + if omitModules { + return nil, r.failedJobf("received unexpected module files data upload when omitModules is true") + } + c := msgType.DataUpload + if c.UploadType != sdkproto.DataUploadType_UPLOAD_TYPE_MODULE_FILES { + return nil, r.failedJobf("invalid data upload type: %q", c.UploadType) + } + + if moduleFilesUpload != nil { + return nil, r.failedJobf("multiple module data uploads received, only expect 1") + } + + moduleFilesUpload, err = sdkproto.NewDataBuilder(c) + if err != nil { + return nil, r.failedJobf("create data builder: %s", err.Error()) + } + case *sdkproto.Response_ChunkPiece: + if omitModules { + return nil, r.failedJobf("received unexpected module files data upload when omitModules is true") + } + c := msgType.ChunkPiece + if moduleFilesUpload == nil { + return nil, r.failedJobf("received chunk piece before module files data upload") + } + + _, err := moduleFilesUpload.Add(c) + if err != nil { + return nil, r.failedJobf("module files, add chunk piece: %s", err.Error()) + } + case *sdkproto.Response_Init: + if moduleFilesUpload != nil { + // If files were uploaded in multiple chunks, put them back together. + moduleFilesData, err := moduleFilesUpload.Complete() + if err != nil { + return nil, r.failedJobf("complete module files data upload: %s", err.Error()) + } + + if !bytes.Equal(msgType.Init.ModuleFilesHash, moduleFilesUpload.Hash) { + return nil, r.failedJobf("module files hash mismatch, uploaded: %x, expected: %x", moduleFilesUpload.Hash, msgType.Init.ModuleFilesHash) + } + msgType.Init.ModuleFiles = moduleFilesData + } + + return msgType.Init, nil + default: + return nil, r.failedJobf("unexpected init response type %T", msg.Type) + } + } +} diff --git a/provisionerd/runner/plan.go b/provisionerd/runner/plan.go new file mode 100644 index 0000000000000..b623cc5209573 --- /dev/null +++ b/provisionerd/runner/plan.go @@ -0,0 +1,64 @@ +package runner + +import ( + "context" + "time" + + "cdr.dev/slog" + "github.com/coder/coder/v2/coderd/tracing" + "github.com/coder/coder/v2/provisionerd/proto" + sdkproto "github.com/coder/coder/v2/provisionersdk/proto" +) + +func (r *Runner) plan(ctx context.Context, stage string, req *sdkproto.PlanRequest) (*sdkproto.PlanComplete, *proto.FailedJob) { + ctx, span := r.startTrace(ctx, tracing.FuncName()) + defer span.End() + + err := r.session.Send(&sdkproto.Request{Type: &sdkproto.Request_Plan{Plan: req}}) + if err != nil { + return nil, r.failedJobf("send plan request: %v", err) + } + + nevermind := make(chan struct{}) + defer close(nevermind) + go func() { + select { + case <-nevermind: + return + case <-r.notStopped.Done(): + return + case <-r.notCanceled.Done(): + _ = r.session.Send(&sdkproto.Request{ + Type: &sdkproto.Request_Cancel{ + Cancel: &sdkproto.CancelRequest{}, + }, + }) + } + }() + + for { + msg, err := r.session.Recv() + if err != nil { + return nil, r.failedJobf("receive plan response: %v", err) + } + switch msgType := msg.Type.(type) { + case *sdkproto.Response_Log: + r.logProvisionerJobLog(context.Background(), msgType.Log.Level, "terraform planning", + slog.F("level", msgType.Log.Level), + slog.F("output", msgType.Log.Output), + ) + + r.queueLog(ctx, &proto.Log{ + Source: proto.LogSource_PROVISIONER, + Level: msgType.Log.Level, + CreatedAt: time.Now().UnixMilli(), + Output: msgType.Log.Output, + Stage: stage, + }) + case *sdkproto.Response_Plan: + return msgType.Plan, nil + default: + return nil, r.failedJobf("unexpected plan response type %T", msg.Type) + } + } +} diff --git a/provisionerd/runner/quota.go b/provisionerd/runner/quota.go deleted file mode 100644 index 26c7e0478ec2c..0000000000000 --- a/provisionerd/runner/quota.go +++ /dev/null @@ -1,11 +0,0 @@ -package runner - -import "github.com/coder/coder/v2/provisionersdk/proto" - -func sumDailyCost(resources []*proto.Resource) int { - var sum int - for _, r := range resources { - sum += int(r.DailyCost) - } - return sum -} diff --git a/provisionerd/runner/runner.go b/provisionerd/runner/runner.go index 22b6403fe729d..ac40407354a69 100644 --- a/provisionerd/runner/runner.go +++ b/provisionerd/runner/runner.go @@ -1,7 +1,6 @@ package runner import ( - "bytes" "context" "encoding/json" "errors" @@ -515,7 +514,6 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p defer span.End() failedJob := r.configure(&sdkproto.Config{ - TemplateSourceArchive: r.job.GetTemplateSourceArchive(), TemplateId: strings2.EmptyToNil(r.job.GetTemplateImport().Metadata.TemplateId), TemplateVersionId: strings2.EmptyToNil(r.job.GetTemplateImport().Metadata.TemplateVersionId), ExpReuseTerraformWorkspace: ptr.Ref(false), @@ -524,6 +522,18 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p return nil, failedJob } + // Initialize the Terraform working directory + initResp, failedInit := r.init(ctx, false, r.job.GetTemplateSourceArchive()) + if failedInit != nil { + return nil, failedInit + } + if initResp == nil { + return nil, r.failedJobf("template import init returned nil response") + } + if initResp.Error != "" { + return nil, r.failedJobf("template import init error: %s", initResp.Error) + } + // Parse parameters and update the job with the parameter specs r.queueLog(ctx, &proto.Log{ Source: proto.LogSource_PROVISIONER_DAEMON, @@ -560,7 +570,7 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p CoderUrl: r.job.GetTemplateImport().Metadata.CoderUrl, WorkspaceOwnerGroups: r.job.GetTemplateImport().Metadata.WorkspaceOwnerGroups, WorkspaceTransition: sdkproto.WorkspaceTransition_START, - }, false) + }) if err != nil { return nil, r.failedJobf("template import provision for start: %s", err) } @@ -576,8 +586,7 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p CoderUrl: r.job.GetTemplateImport().Metadata.CoderUrl, WorkspaceOwnerGroups: r.job.GetTemplateImport().Metadata.WorkspaceOwnerGroups, WorkspaceTransition: sdkproto.WorkspaceTransition_STOP, - }, true, // Modules downloaded on the start provision - ) + }) if err != nil { return nil, r.failedJobf("template import provision for stop: %s", err) } @@ -597,12 +606,13 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p RichParameters: startProvision.Parameters, ExternalAuthProvidersNames: externalAuthProviderNames, ExternalAuthProviders: startProvision.ExternalAuthProviders, - StartModules: startProvision.Modules, - StopModules: stopProvision.Modules, - Presets: startProvision.Presets, - Plan: startProvision.Plan, - // ModuleFiles are not on the stopProvision. So grab from the startProvision. - ModuleFiles: startProvision.ModuleFiles, + // TODO: These are defined as different, but can they be? + // Terraform downloads modules regardless of `count`, so this should be the same + StartModules: initResp.Modules, + StopModules: initResp.Modules, + Presets: startProvision.Presets, + Plan: startProvision.Plan, + ModuleFiles: initResp.ModuleFiles, // ModuleFileHash will be populated if the file is uploaded async ModuleFilesHash: []byte{}, HasAiTasks: startProvision.HasAITasks, @@ -666,10 +676,8 @@ type templateImportProvision struct { Resources []*sdkproto.Resource Parameters []*sdkproto.RichParameter ExternalAuthProviders []*sdkproto.ExternalAuthProviderResource - Modules []*sdkproto.Module Presets []*sdkproto.Preset Plan json.RawMessage - ModuleFiles []byte HasAITasks bool HasExternalAgents bool } @@ -677,8 +685,8 @@ type templateImportProvision struct { // Performs a dry-run provision when importing a template. // This is used to detect resources that would be provisioned for a workspace in various states. // It doesn't define values for rich parameters as they're unknown during template import. -func (r *Runner) runTemplateImportProvision(ctx context.Context, variableValues []*sdkproto.VariableValue, metadata *sdkproto.Metadata, omitModules bool) (*templateImportProvision, error) { - return r.runTemplateImportProvisionWithRichParameters(ctx, variableValues, nil, metadata, omitModules) +func (r *Runner) runTemplateImportProvision(ctx context.Context, variableValues []*sdkproto.VariableValue, metadata *sdkproto.Metadata) (*templateImportProvision, error) { + return r.runTemplateImportProvisionWithRichParameters(ctx, variableValues, nil, metadata) } // Performs a dry-run provision with provided rich parameters. @@ -688,7 +696,6 @@ func (r *Runner) runTemplateImportProvisionWithRichParameters( variableValues []*sdkproto.VariableValue, richParameterValues []*sdkproto.RichParameterValue, metadata *sdkproto.Metadata, - omitModules bool, ) (*templateImportProvision, error) { ctx, span := r.startTrace(ctx, tracing.FuncName()) defer span.End() @@ -700,126 +707,48 @@ func (r *Runner) runTemplateImportProvisionWithRichParameters( case sdkproto.WorkspaceTransition_STOP: stage = "Detecting ephemeral resources" } - // use the notStopped so that if we attempt to gracefully cancel, the stream will still be available for us - // to send the cancel to the provisioner - err := r.session.Send(&sdkproto.Request{Type: &sdkproto.Request_Plan{Plan: &sdkproto.PlanRequest{ - Metadata: metadata, - RichParameterValues: richParameterValues, - // Template import has no previous values - PreviousParameterValues: make([]*sdkproto.RichParameterValue, 0), + + planComplete, failed := r.plan(ctx, stage, &sdkproto.PlanRequest{ + Metadata: metadata, + RichParameterValues: richParameterValues, VariableValues: variableValues, - OmitModuleFiles: omitModules, - }}}) - if err != nil { - return nil, xerrors.Errorf("start provision: %w", err) + ExternalAuthProviders: nil, + PreviousParameterValues: nil, + State: nil, + }) + if failed != nil { + return nil, xerrors.Errorf("plan during template import provision: %w", failed) + } + if planComplete == nil { + return nil, xerrors.New("plan during template import provision returned nil response") + } + if planComplete.Error != "" { + return nil, xerrors.Errorf("plan during template import provision error: %s", planComplete.Error) } - nevermind := make(chan struct{}) - defer close(nevermind) - go func() { - select { - case <-nevermind: - return - case <-r.notStopped.Done(): - return - case <-r.notCanceled.Done(): - _ = r.session.Send(&sdkproto.Request{ - Type: &sdkproto.Request_Cancel{ - Cancel: &sdkproto.CancelRequest{}, - }, - }) - } - }() - - var moduleFilesUpload *sdkproto.DataBuilder - for { - msg, err := r.session.Recv() - if err != nil { - return nil, xerrors.Errorf("recv import provision: %w", err) - } - - switch msgType := msg.Type.(type) { - case *sdkproto.Response_Log: - r.logProvisionerJobLog(context.Background(), msgType.Log.Level, "template import provision job logged", - slog.F("level", msgType.Log.Level), - slog.F("output", msgType.Log.Output), - ) - r.queueLog(ctx, &proto.Log{ - Source: proto.LogSource_PROVISIONER, - Level: msgType.Log.Level, - CreatedAt: time.Now().UnixMilli(), - Output: msgType.Log.Output, - Stage: stage, - }) - case *sdkproto.Response_DataUpload: - c := msgType.DataUpload - if c.UploadType != sdkproto.DataUploadType_UPLOAD_TYPE_MODULE_FILES { - return nil, xerrors.Errorf("invalid data upload type: %q", c.UploadType) - } - - if moduleFilesUpload != nil { - return nil, xerrors.New("multiple module data uploads received, only expect 1") - } - - moduleFilesUpload, err = sdkproto.NewDataBuilder(c) - if err != nil { - return nil, xerrors.Errorf("create data builder: %w", err) - } - case *sdkproto.Response_ChunkPiece: - c := msgType.ChunkPiece - if moduleFilesUpload == nil { - return nil, xerrors.New("received chunk piece before module files data upload") - } - - _, err := moduleFilesUpload.Add(c) - if err != nil { - return nil, xerrors.Errorf("module files, add chunk piece: %w", err) - } - case *sdkproto.Response_Plan: - c := msgType.Plan - if c.Error != "" { - r.logger.Info(context.Background(), "dry-run provision failure", - slog.F("error", c.Error), - ) - - return nil, xerrors.New(c.Error) - } - - if moduleFilesUpload != nil && len(c.ModuleFiles) > 0 { - return nil, xerrors.New("module files were uploaded and module files were returned in the plan response. Only one of these should be set") - } - - r.logger.Info(context.Background(), "parse dry-run provision successful", - slog.F("resource_count", len(c.Resources)), - slog.F("resources", resourceNames(c.Resources)), - ) - moduleFilesData := c.ModuleFiles - if moduleFilesUpload != nil { - uploadData, err := moduleFilesUpload.Complete() - if err != nil { - return nil, xerrors.Errorf("module files, complete upload: %w", err) - } - moduleFilesData = uploadData - if !bytes.Equal(c.ModuleFilesHash, moduleFilesUpload.Hash) { - return nil, xerrors.Errorf("module files hash mismatch, uploaded: %x, expected: %x", moduleFilesUpload.Hash, c.ModuleFilesHash) - } - } - return &templateImportProvision{ - Resources: c.Resources, - Parameters: c.Parameters, - ExternalAuthProviders: c.ExternalAuthProviders, - Modules: c.Modules, - Presets: c.Presets, - Plan: c.Plan, - ModuleFiles: moduleFilesData, - HasAITasks: c.HasAiTasks, - HasExternalAgents: c.HasExternalAgents, - }, nil - default: - return nil, xerrors.Errorf("invalid message type %q received from provisioner", - reflect.TypeOf(msg.Type).String()) - } + graphComplete, failed := r.graph(ctx, &sdkproto.GraphRequest{ + Metadata: metadata, + Source: sdkproto.GraphSource_SOURCE_PLAN, + }) + if failed != nil { + return nil, xerrors.Errorf("graph during template import provision: %w", failed) } + if graphComplete == nil { + return nil, xerrors.New("graph during template import provision returned nil response") + } + if graphComplete.Error != "" { + return nil, xerrors.Errorf("graph during template import provision error: %s", graphComplete.Error) + } + + return &templateImportProvision{ + Resources: graphComplete.Resources, + Parameters: graphComplete.Parameters, + ExternalAuthProviders: graphComplete.ExternalAuthProviders, + Presets: graphComplete.Presets, + Plan: planComplete.Plan, + HasAITasks: graphComplete.HasAiTasks, + HasExternalAgents: graphComplete.HasExternalAgents, + }, nil } func (r *Runner) runTemplateDryRun(ctx context.Context) (*proto.CompletedJob, *proto.FailedJob) { @@ -854,19 +783,28 @@ func (r *Runner) runTemplateDryRun(ctx context.Context) (*proto.CompletedJob, *p metadata.WorkspaceOwnerId = id.String() } - failedJob := r.configure(&sdkproto.Config{ - TemplateSourceArchive: r.job.GetTemplateSourceArchive(), - }) + failedJob := r.configure(&sdkproto.Config{}) if failedJob != nil { return nil, failedJob } + // Initialize the Terraform working directory + initResp, failedJob := r.init(ctx, false, r.job.GetTemplateSourceArchive()) + if failedJob != nil { + return nil, failedJob + } + if initResp == nil { + return nil, r.failedJobf("template dry-run init returned nil response") + } + if initResp.Error != "" { + return nil, r.failedJobf("template dry-run init error: %s", initResp.Error) + } + // Run the template import provision task since it's already a dry run. provision, err := r.runTemplateImportProvisionWithRichParameters(ctx, r.job.GetTemplateDryRun().GetVariableValues(), r.job.GetTemplateDryRun().GetRichParameterValues(), metadata, - false, ) if err != nil { return nil, r.failedJobf("run dry-run provision job: %s", err) @@ -877,73 +815,14 @@ func (r *Runner) runTemplateDryRun(ctx context.Context) (*proto.CompletedJob, *p Type: &proto.CompletedJob_TemplateDryRun_{ TemplateDryRun: &proto.CompletedJob_TemplateDryRun{ Resources: provision.Resources, - Modules: provision.Modules, + Modules: initResp.Modules, }, }, }, nil } -func (r *Runner) buildWorkspace(ctx context.Context, stage string, req *sdkproto.Request) ( - *sdkproto.Response, *proto.FailedJob, -) { - // use the notStopped so that if we attempt to gracefully cancel, the stream - // will still be available for us to send the cancel to the provisioner - err := r.session.Send(req) - if err != nil { - return nil, r.failedWorkspaceBuildf("start provision: %s", err) - } - nevermind := make(chan struct{}) - defer close(nevermind) - go func() { - select { - case <-nevermind: - return - case <-r.notStopped.Done(): - return - case <-r.notCanceled.Done(): - _ = r.session.Send(&sdkproto.Request{ - Type: &sdkproto.Request_Cancel{ - Cancel: &sdkproto.CancelRequest{}, - }, - }) - } - }() - - for { - msg, err := r.session.Recv() - if err != nil { - return nil, r.failedWorkspaceBuildf("recv workspace provision: %s", err) - } - switch msgType := msg.Type.(type) { - case *sdkproto.Response_Log: - r.logProvisionerJobLog(context.Background(), msgType.Log.Level, "workspace provisioner job logged", - slog.F("level", msgType.Log.Level), - slog.F("output", msgType.Log.Output), - slog.F("workspace_build_id", r.job.GetWorkspaceBuild().WorkspaceBuildId), - ) - - r.queueLog(ctx, &proto.Log{ - Source: proto.LogSource_PROVISIONER, - Level: msgType.Log.Level, - CreatedAt: time.Now().UnixMilli(), - Output: msgType.Log.Output, - Stage: stage, - }) - case *sdkproto.Response_DataUpload: - continue // Only for template imports - case *sdkproto.Response_ChunkPiece: - continue // Only for template imports - default: - // Stop looping! - return msg, nil - } - } -} - -func (r *Runner) commitQuota(ctx context.Context, resources []*sdkproto.Resource) *proto.FailedJob { - cost := sumDailyCost(resources) +func (r *Runner) commitQuota(ctx context.Context, cost int32) *proto.FailedJob { r.logger.Debug(ctx, "committing quota", - slog.F("resources", resourceNames(resources)), slog.F("cost", cost), ) if cost == 0 { @@ -953,9 +832,8 @@ func (r *Runner) commitQuota(ctx context.Context, resources []*sdkproto.Resource const stage = "Commit quota" resp, err := r.quotaCommitter.CommitQuota(ctx, &proto.CommitQuotaRequest{ - JobId: r.job.JobId, - // #nosec G115 - Safe conversion as cost is expected to be within int32 range for provisioning costs - DailyCost: int32(cost), + JobId: r.job.JobId, + DailyCost: cost, }) if err != nil { r.queueLog(ctx, &proto.Log{ @@ -1014,8 +892,6 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p } failedJob := r.configure(&sdkproto.Config{ - TemplateSourceArchive: r.job.GetTemplateSourceArchive(), - State: r.job.GetWorkspaceBuild().State, ProvisionerLogLevel: r.job.GetWorkspaceBuild().LogLevel, TemplateId: strings2.EmptyToNil(r.job.GetWorkspaceBuild().Metadata.TemplateId), TemplateVersionId: strings2.EmptyToNil(r.job.GetWorkspaceBuild().Metadata.TemplateVersionId), @@ -1025,25 +901,53 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p return nil, failedJob } - resp, failed := r.buildWorkspace(ctx, "Planning infrastructure", &sdkproto.Request{ - Type: &sdkproto.Request_Plan{ - Plan: &sdkproto.PlanRequest{ - OmitModuleFiles: true, // Only useful for template imports - Metadata: r.job.GetWorkspaceBuild().Metadata, - RichParameterValues: r.job.GetWorkspaceBuild().RichParameterValues, - PreviousParameterValues: r.job.GetWorkspaceBuild().PreviousParameterValues, - VariableValues: r.job.GetWorkspaceBuild().VariableValues, - ExternalAuthProviders: r.job.GetWorkspaceBuild().ExternalAuthProviders, + // timings collects all timings from each phase of the build + timings := make([]*sdkproto.Timing, 0) + + // Initialize the Terraform working directory + initComplete, failedJob := r.init(ctx, true, r.job.GetTemplateSourceArchive()) + if failedJob != nil { + return nil, failedJob + } + if initComplete == nil { + return nil, r.failedWorkspaceBuildf("invalid message type received from provisioner during init") + } + // Collect init timings + timings = append(timings, initComplete.Timings...) + if initComplete.Error != "" { + r.logger.Warn(context.Background(), "init request failed", + slog.F("error", initComplete.Error), + ) + + return nil, &proto.FailedJob{ + JobId: r.job.JobId, + Error: initComplete.Error, + Type: &proto.FailedJob_WorkspaceBuild_{ + WorkspaceBuild: &proto.FailedJob_WorkspaceBuild{ + State: r.job.GetWorkspaceBuild().State, + Timings: timings, + }, }, - }, + } + } + + // Run `terraform plan` + planComplete, failed := r.plan(ctx, "Planning Infrastructure", &sdkproto.PlanRequest{ + Metadata: r.job.GetWorkspaceBuild().Metadata, + RichParameterValues: r.job.GetWorkspaceBuild().RichParameterValues, + VariableValues: r.job.GetWorkspaceBuild().VariableValues, + ExternalAuthProviders: r.job.GetWorkspaceBuild().ExternalAuthProviders, + PreviousParameterValues: r.job.GetWorkspaceBuild().PreviousParameterValues, + State: r.job.GetWorkspaceBuild().State, }) if failed != nil { return nil, failed } - planComplete := resp.GetPlan() if planComplete == nil { - return nil, r.failedWorkspaceBuildf("invalid message type %T received from provisioner", resp.Type) + return nil, r.failedWorkspaceBuildf("invalid message type received from provisioner during plan") } + // Collect plan timings + timings = append(timings, planComplete.Timings...) if planComplete.Error != "" { r.logger.Warn(context.Background(), "plan request failed", slog.F("error", planComplete.Error), @@ -1053,27 +957,28 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p JobId: r.job.JobId, Error: planComplete.Error, Type: &proto.FailedJob_WorkspaceBuild_{ - WorkspaceBuild: &proto.FailedJob_WorkspaceBuild{}, + WorkspaceBuild: &proto.FailedJob_WorkspaceBuild{ + Timings: timings, + }, }, } } - if len(planComplete.AiTasks) > 1 { - return nil, r.failedWorkspaceBuildf("only one 'coder_ai_task' resource can be provisioned per template") + + if planComplete.AiTaskCount > 1 { + return nil, r.failedWorkspaceBuildf("only one 'coder_ai_task' resource can be provisioned per template, found %d", planComplete.AiTaskCount) } - r.logger.Info(context.Background(), "plan request successful", - slog.F("resource_count", len(planComplete.Resources)), - slog.F("resources", resourceNames(planComplete.Resources)), - ) + r.logger.Info(context.Background(), "plan request successful") r.flushQueuedLogs(ctx) if commitQuota { - failed = r.commitQuota(ctx, planComplete.Resources) + failed = r.commitQuota(ctx, planComplete.GetDailyCost()) r.flushQueuedLogs(ctx) if failed != nil { return nil, failed } } + // Run Terraform Apply r.queueLog(ctx, &proto.Log{ Source: proto.LogSource_PROVISIONER_DAEMON, Level: sdkproto.LogLevel_INFO, @@ -1081,24 +986,17 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p CreatedAt: time.Now().UnixMilli(), }) - resp, failed = r.buildWorkspace(ctx, applyStage, &sdkproto.Request{ - Type: &sdkproto.Request_Apply{ - Apply: &sdkproto.ApplyRequest{ - Metadata: r.job.GetWorkspaceBuild().Metadata, - }, - }, + applyComplete, failed := r.apply(ctx, applyStage, &sdkproto.ApplyRequest{ + Metadata: r.job.GetWorkspaceBuild().Metadata, }) if failed != nil { return nil, failed } - applyComplete := resp.GetApply() if applyComplete == nil { - return nil, r.failedWorkspaceBuildf("invalid message type %T received from provisioner", resp.Type) + return nil, r.failedWorkspaceBuildf("invalid message type received from provisioner during apply") } - - // Prepend the plan timings (since they occurred first). - applyComplete.Timings = append(planComplete.Timings, applyComplete.Timings...) - + // Collect apply timings + timings = append(timings, applyComplete.Timings...) if applyComplete.Error != "" { r.logger.Warn(context.Background(), "apply failed; updating state", slog.F("error", applyComplete.Error), @@ -1111,15 +1009,46 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p Type: &proto.FailedJob_WorkspaceBuild_{ WorkspaceBuild: &proto.FailedJob_WorkspaceBuild{ State: applyComplete.State, - Timings: applyComplete.Timings, + Timings: timings, + }, + }, + } + } + + // Run Terraform Graph + graphComplete, failed := r.graph(ctx, &sdkproto.GraphRequest{ + Metadata: r.job.GetWorkspaceBuild().Metadata, + Source: sdkproto.GraphSource_SOURCE_STATE, + }) + if failed != nil { + return nil, failed + } + if graphComplete == nil { + return nil, r.failedWorkspaceBuildf("invalid message type received from provisioner during graph") + } + // Collect graph timings + timings = append(timings, graphComplete.Timings...) + if graphComplete.Error != "" { + r.logger.Warn(context.Background(), "graph request failed", + slog.F("error", planComplete.Error), + ) + + return nil, &proto.FailedJob{ + JobId: r.job.JobId, + Error: graphComplete.Error, + Type: &proto.FailedJob_WorkspaceBuild_{ + WorkspaceBuild: &proto.FailedJob_WorkspaceBuild{ + // Graph does not change the state, so return the state returned from apply. + State: applyComplete.State, + Timings: timings, }, }, } } r.logger.Info(context.Background(), "apply successful", - slog.F("resource_count", len(applyComplete.Resources)), - slog.F("resources", resourceNames(applyComplete.Resources)), + slog.F("resource_count", len(graphComplete.Resources)), + slog.F("resources", resourceNames(graphComplete.Resources)), slog.F("state_len", len(applyComplete.State)), ) r.flushQueuedLogs(ctx) @@ -1129,15 +1058,14 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p Type: &proto.CompletedJob_WorkspaceBuild_{ WorkspaceBuild: &proto.CompletedJob_WorkspaceBuild{ State: applyComplete.State, - Resources: applyComplete.Resources, - Timings: applyComplete.Timings, - // Modules are created on disk by `terraform init`, and that is only - // called by `plan`. `apply` does not modify them, so we can use the - // modules from the plan response. - Modules: planComplete.Modules, + Resources: graphComplete.Resources, + Timings: timings, + // Modules files are omitted for workspace builds, but the modules.json metadata + // is available from init to return. + Modules: initComplete.Modules, // Resource replacements are discovered at plan time, only. ResourceReplacements: planComplete.ResourceReplacements, - AiTasks: applyComplete.AiTasks, + AiTasks: graphComplete.AiTasks, }, }, }, nil diff --git a/provisionersdk/errors.go b/provisionersdk/errors.go index 0dc66e6e6b301..f7820592d1dd3 100644 --- a/provisionersdk/errors.go +++ b/provisionersdk/errors.go @@ -10,6 +10,10 @@ func ParseErrorf(format string, args ...any) *proto.ParseComplete { return &proto.ParseComplete{Error: fmt.Sprintf(format, args...)} } +func InitErrorf(format string, args ...any) *proto.InitComplete { + return &proto.InitComplete{Error: fmt.Sprintf(format, args...)} +} + func PlanErrorf(format string, args ...any) *proto.PlanComplete { return &proto.PlanComplete{Error: fmt.Sprintf(format, args...)} } @@ -17,3 +21,7 @@ func PlanErrorf(format string, args ...any) *proto.PlanComplete { func ApplyErrorf(format string, args ...any) *proto.ApplyComplete { return &proto.ApplyComplete{Error: fmt.Sprintf(format, args...)} } + +func GraphError(format string, args ...any) *proto.GraphComplete { + return &proto.GraphComplete{Error: fmt.Sprintf(format, args...)} +} diff --git a/provisionersdk/proto/provisioner.pb.go b/provisionersdk/proto/provisioner.pb.go index 72741a1036b41..69f73eb8fa0f7 100644 --- a/provisionersdk/proto/provisioner.pb.go +++ b/provisionersdk/proto/provisioner.pb.go @@ -348,6 +348,55 @@ func (PrebuiltWorkspaceBuildStage) EnumDescriptor() ([]byte, []int) { return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{5} } +type GraphSource int32 + +const ( + GraphSource_SOURCE_UNKNOWN GraphSource = 0 + GraphSource_SOURCE_PLAN GraphSource = 1 + GraphSource_SOURCE_STATE GraphSource = 2 +) + +// Enum value maps for GraphSource. +var ( + GraphSource_name = map[int32]string{ + 0: "SOURCE_UNKNOWN", + 1: "SOURCE_PLAN", + 2: "SOURCE_STATE", + } + GraphSource_value = map[string]int32{ + "SOURCE_UNKNOWN": 0, + "SOURCE_PLAN": 1, + "SOURCE_STATE": 2, + } +) + +func (x GraphSource) Enum() *GraphSource { + p := new(GraphSource) + *p = x + return p +} + +func (x GraphSource) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (GraphSource) Descriptor() protoreflect.EnumDescriptor { + return file_provisionersdk_proto_provisioner_proto_enumTypes[6].Descriptor() +} + +func (GraphSource) Type() protoreflect.EnumType { + return &file_provisionersdk_proto_provisioner_proto_enumTypes[6] +} + +func (x GraphSource) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use GraphSource.Descriptor instead. +func (GraphSource) EnumDescriptor() ([]byte, []int) { + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{6} +} + type TimingState int32 const ( @@ -381,11 +430,11 @@ func (x TimingState) String() string { } func (TimingState) Descriptor() protoreflect.EnumDescriptor { - return file_provisionersdk_proto_provisioner_proto_enumTypes[6].Descriptor() + return file_provisionersdk_proto_provisioner_proto_enumTypes[7].Descriptor() } func (TimingState) Type() protoreflect.EnumType { - return &file_provisionersdk_proto_provisioner_proto_enumTypes[6] + return &file_provisionersdk_proto_provisioner_proto_enumTypes[7] } func (x TimingState) Number() protoreflect.EnumNumber { @@ -394,7 +443,7 @@ func (x TimingState) Number() protoreflect.EnumNumber { // Deprecated: Use TimingState.Descriptor instead. func (TimingState) EnumDescriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{6} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{7} } type DataUploadType int32 @@ -430,11 +479,11 @@ func (x DataUploadType) String() string { } func (DataUploadType) Descriptor() protoreflect.EnumDescriptor { - return file_provisionersdk_proto_provisioner_proto_enumTypes[7].Descriptor() + return file_provisionersdk_proto_provisioner_proto_enumTypes[8].Descriptor() } func (DataUploadType) Type() protoreflect.EnumType { - return &file_provisionersdk_proto_provisioner_proto_enumTypes[7] + return &file_provisionersdk_proto_provisioner_proto_enumTypes[8] } func (x DataUploadType) Number() protoreflect.EnumNumber { @@ -443,7 +492,7 @@ func (x DataUploadType) Number() protoreflect.EnumNumber { // Deprecated: Use DataUploadType.Descriptor instead. func (DataUploadType) EnumDescriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{7} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{8} } // Empty indicates a successful request/response. @@ -3155,16 +3204,12 @@ type Config struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // template_source_archive is a tar of the template source files - TemplateSourceArchive []byte `protobuf:"bytes,1,opt,name=template_source_archive,json=templateSourceArchive,proto3" json:"template_source_archive,omitempty"` - // state is the provisioner state (if any) - State []byte `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` - ProvisionerLogLevel string `protobuf:"bytes,3,opt,name=provisioner_log_level,json=provisionerLogLevel,proto3" json:"provisioner_log_level,omitempty"` + ProvisionerLogLevel string `protobuf:"bytes,1,opt,name=provisioner_log_level,json=provisionerLogLevel,proto3" json:"provisioner_log_level,omitempty"` // Template imports can omit template id - TemplateId *string `protobuf:"bytes,4,opt,name=template_id,json=templateId,proto3,oneof" json:"template_id,omitempty"` + TemplateId *string `protobuf:"bytes,2,opt,name=template_id,json=templateId,proto3,oneof" json:"template_id,omitempty"` // Dry runs omit version id - TemplateVersionId *string `protobuf:"bytes,5,opt,name=template_version_id,json=templateVersionId,proto3,oneof" json:"template_version_id,omitempty"` - ExpReuseTerraformWorkspace *bool `protobuf:"varint,6,opt,name=exp_reuse_terraform_workspace,json=expReuseTerraformWorkspace,proto3,oneof" json:"exp_reuse_terraform_workspace,omitempty"` // Whether to reuse existing terraform workspaces if they exist. + TemplateVersionId *string `protobuf:"bytes,3,opt,name=template_version_id,json=templateVersionId,proto3,oneof" json:"template_version_id,omitempty"` + ExpReuseTerraformWorkspace *bool `protobuf:"varint,4,opt,name=exp_reuse_terraform_workspace,json=expReuseTerraformWorkspace,proto3,oneof" json:"exp_reuse_terraform_workspace,omitempty"` // Whether to reuse existing terraform workspaces if they exist. } func (x *Config) Reset() { @@ -3199,20 +3244,6 @@ func (*Config) Descriptor() ([]byte, []int) { return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{34} } -func (x *Config) GetTemplateSourceArchive() []byte { - if x != nil { - return x.TemplateSourceArchive - } - return nil -} - -func (x *Config) GetState() []byte { - if x != nil { - return x.State - } - return nil -} - func (x *Config) GetProvisionerLogLevel() string { if x != nil { return x.ProvisionerLogLevel @@ -3352,6 +3383,145 @@ func (x *ParseComplete) GetWorkspaceTags() map[string]string { return nil } +type InitRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // template_source_archive is a tar of the template source files + TemplateSourceArchive []byte `protobuf:"bytes,1,opt,name=template_source_archive,json=templateSourceArchive,proto3" json:"template_source_archive,omitempty"` + // If true, the provisioner can safely assume the caller does not need the + // module files downloaded by the `terraform init` command. + // Ideally this boolean would be flipped in its truthy value, however since + // this is costly, the zero value omitting the module files is preferred. + OmitModuleFiles bool `protobuf:"varint,3,opt,name=omit_module_files,json=omitModuleFiles,proto3" json:"omit_module_files,omitempty"` +} + +func (x *InitRequest) Reset() { + *x = InitRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InitRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InitRequest) ProtoMessage() {} + +func (x *InitRequest) ProtoReflect() protoreflect.Message { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[37] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InitRequest.ProtoReflect.Descriptor instead. +func (*InitRequest) Descriptor() ([]byte, []int) { + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{37} +} + +func (x *InitRequest) GetTemplateSourceArchive() []byte { + if x != nil { + return x.TemplateSourceArchive + } + return nil +} + +func (x *InitRequest) GetOmitModuleFiles() bool { + if x != nil { + return x.OmitModuleFiles + } + return false +} + +type InitComplete struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` + Timings []*Timing `protobuf:"bytes,2,rep,name=timings,proto3" json:"timings,omitempty"` + Modules []*Module `protobuf:"bytes,3,rep,name=modules,proto3" json:"modules,omitempty"` + ModuleFiles []byte `protobuf:"bytes,4,opt,name=module_files,json=moduleFiles,proto3" json:"module_files,omitempty"` + ModuleFilesHash []byte `protobuf:"bytes,5,opt,name=module_files_hash,json=moduleFilesHash,proto3" json:"module_files_hash,omitempty"` +} + +func (x *InitComplete) Reset() { + *x = InitComplete{} + if protoimpl.UnsafeEnabled { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[38] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InitComplete) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InitComplete) ProtoMessage() {} + +func (x *InitComplete) ProtoReflect() protoreflect.Message { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[38] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InitComplete.ProtoReflect.Descriptor instead. +func (*InitComplete) Descriptor() ([]byte, []int) { + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{38} +} + +func (x *InitComplete) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +func (x *InitComplete) GetTimings() []*Timing { + if x != nil { + return x.Timings + } + return nil +} + +func (x *InitComplete) GetModules() []*Module { + if x != nil { + return x.Modules + } + return nil +} + +func (x *InitComplete) GetModuleFiles() []byte { + if x != nil { + return x.ModuleFiles + } + return nil +} + +func (x *InitComplete) GetModuleFilesHash() []byte { + if x != nil { + return x.ModuleFilesHash + } + return nil +} + // PlanRequest asks the provisioner to plan what resources & parameters it will create type PlanRequest struct { state protoimpl.MessageState @@ -3363,18 +3533,14 @@ type PlanRequest struct { VariableValues []*VariableValue `protobuf:"bytes,3,rep,name=variable_values,json=variableValues,proto3" json:"variable_values,omitempty"` ExternalAuthProviders []*ExternalAuthProvider `protobuf:"bytes,4,rep,name=external_auth_providers,json=externalAuthProviders,proto3" json:"external_auth_providers,omitempty"` PreviousParameterValues []*RichParameterValue `protobuf:"bytes,5,rep,name=previous_parameter_values,json=previousParameterValues,proto3" json:"previous_parameter_values,omitempty"` - // If true, the provisioner can safely assume the caller does not need the - // module files downloaded by the `terraform init` command. - // Ideally this boolean would be flipped in its truthy value, however for - // backwards compatibility reasons, the zero value should be the previous - // behavior of downloading the module files. - OmitModuleFiles bool `protobuf:"varint,6,opt,name=omit_module_files,json=omitModuleFiles,proto3" json:"omit_module_files,omitempty"` + // state is the provisioner state (if any) + State []byte `protobuf:"bytes,6,opt,name=state,proto3" json:"state,omitempty"` } func (x *PlanRequest) Reset() { *x = PlanRequest{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[37] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3387,7 +3553,7 @@ func (x *PlanRequest) String() string { func (*PlanRequest) ProtoMessage() {} func (x *PlanRequest) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[37] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3400,7 +3566,7 @@ func (x *PlanRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PlanRequest.ProtoReflect.Descriptor instead. func (*PlanRequest) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{37} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{39} } func (x *PlanRequest) GetMetadata() *Metadata { @@ -3438,11 +3604,11 @@ func (x *PlanRequest) GetPreviousParameterValues() []*RichParameterValue { return nil } -func (x *PlanRequest) GetOmitModuleFiles() bool { +func (x *PlanRequest) GetState() []byte { if x != nil { - return x.OmitModuleFiles + return x.State } - return false + return nil } // PlanComplete indicates a request to plan completed. @@ -3451,31 +3617,18 @@ type PlanComplete struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` - Resources []*Resource `protobuf:"bytes,2,rep,name=resources,proto3" json:"resources,omitempty"` - Parameters []*RichParameter `protobuf:"bytes,3,rep,name=parameters,proto3" json:"parameters,omitempty"` - ExternalAuthProviders []*ExternalAuthProviderResource `protobuf:"bytes,4,rep,name=external_auth_providers,json=externalAuthProviders,proto3" json:"external_auth_providers,omitempty"` - Timings []*Timing `protobuf:"bytes,6,rep,name=timings,proto3" json:"timings,omitempty"` - Modules []*Module `protobuf:"bytes,7,rep,name=modules,proto3" json:"modules,omitempty"` - Presets []*Preset `protobuf:"bytes,8,rep,name=presets,proto3" json:"presets,omitempty"` - Plan []byte `protobuf:"bytes,9,opt,name=plan,proto3" json:"plan,omitempty"` - ResourceReplacements []*ResourceReplacement `protobuf:"bytes,10,rep,name=resource_replacements,json=resourceReplacements,proto3" json:"resource_replacements,omitempty"` - ModuleFiles []byte `protobuf:"bytes,11,opt,name=module_files,json=moduleFiles,proto3" json:"module_files,omitempty"` - ModuleFilesHash []byte `protobuf:"bytes,12,opt,name=module_files_hash,json=moduleFilesHash,proto3" json:"module_files_hash,omitempty"` - // Whether a template has any `coder_ai_task` resources defined, even if not planned for creation. - // During a template import, a plan is run which may not yield in any `coder_ai_task` resources, but nonetheless we - // still need to know that such resources are defined. - // - // See `hasAITaskResources` in provisioner/terraform/resources.go for more details. - HasAiTasks bool `protobuf:"varint,13,opt,name=has_ai_tasks,json=hasAiTasks,proto3" json:"has_ai_tasks,omitempty"` - AiTasks []*AITask `protobuf:"bytes,14,rep,name=ai_tasks,json=aiTasks,proto3" json:"ai_tasks,omitempty"` - HasExternalAgents bool `protobuf:"varint,15,opt,name=has_external_agents,json=hasExternalAgents,proto3" json:"has_external_agents,omitempty"` + Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` + Timings []*Timing `protobuf:"bytes,2,rep,name=timings,proto3" json:"timings,omitempty"` + Plan []byte `protobuf:"bytes,3,opt,name=plan,proto3" json:"plan,omitempty"` + DailyCost int32 `protobuf:"varint,4,opt,name=dailyCost,proto3" json:"dailyCost,omitempty"` + ResourceReplacements []*ResourceReplacement `protobuf:"bytes,5,rep,name=resource_replacements,json=resourceReplacements,proto3" json:"resource_replacements,omitempty"` + AiTaskCount int32 `protobuf:"varint,6,opt,name=ai_task_count,json=aiTaskCount,proto3" json:"ai_task_count,omitempty"` } func (x *PlanComplete) Reset() { *x = PlanComplete{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[38] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3488,7 +3641,7 @@ func (x *PlanComplete) String() string { func (*PlanComplete) ProtoMessage() {} func (x *PlanComplete) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[38] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3501,7 +3654,7 @@ func (x *PlanComplete) ProtoReflect() protoreflect.Message { // Deprecated: Use PlanComplete.ProtoReflect.Descriptor instead. func (*PlanComplete) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{38} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{40} } func (x *PlanComplete) GetError() string { @@ -3511,124 +3664,189 @@ func (x *PlanComplete) GetError() string { return "" } -func (x *PlanComplete) GetResources() []*Resource { +func (x *PlanComplete) GetTimings() []*Timing { if x != nil { - return x.Resources + return x.Timings } return nil } -func (x *PlanComplete) GetParameters() []*RichParameter { +func (x *PlanComplete) GetPlan() []byte { if x != nil { - return x.Parameters + return x.Plan } return nil } -func (x *PlanComplete) GetExternalAuthProviders() []*ExternalAuthProviderResource { +func (x *PlanComplete) GetDailyCost() int32 { if x != nil { - return x.ExternalAuthProviders + return x.DailyCost } - return nil + return 0 } -func (x *PlanComplete) GetTimings() []*Timing { +func (x *PlanComplete) GetResourceReplacements() []*ResourceReplacement { if x != nil { - return x.Timings + return x.ResourceReplacements } return nil } -func (x *PlanComplete) GetModules() []*Module { +func (x *PlanComplete) GetAiTaskCount() int32 { if x != nil { - return x.Modules + return x.AiTaskCount } - return nil + return 0 } -func (x *PlanComplete) GetPresets() []*Preset { - if x != nil { - return x.Presets +// ApplyRequest asks the provisioner to apply the changes. Apply MUST be preceded by a successful plan request/response +// in the same Session. The plan data is not transmitted over the wire and is cached by the provisioner in the Session. +type ApplyRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` + // state is the provisioner state (if any) + State []byte `protobuf:"bytes,6,opt,name=state,proto3" json:"state,omitempty"` +} + +func (x *ApplyRequest) Reset() { + *x = ApplyRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[41] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (x *PlanComplete) GetPlan() []byte { - if x != nil { - return x.Plan +func (x *ApplyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApplyRequest) ProtoMessage() {} + +func (x *ApplyRequest) ProtoReflect() protoreflect.Message { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[41] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return nil + return mi.MessageOf(x) } -func (x *PlanComplete) GetResourceReplacements() []*ResourceReplacement { +// Deprecated: Use ApplyRequest.ProtoReflect.Descriptor instead. +func (*ApplyRequest) Descriptor() ([]byte, []int) { + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{41} +} + +func (x *ApplyRequest) GetMetadata() *Metadata { if x != nil { - return x.ResourceReplacements + return x.Metadata } return nil } -func (x *PlanComplete) GetModuleFiles() []byte { +func (x *ApplyRequest) GetState() []byte { if x != nil { - return x.ModuleFiles + return x.State } return nil } -func (x *PlanComplete) GetModuleFilesHash() []byte { - if x != nil { - return x.ModuleFilesHash +// ApplyComplete indicates a request to apply completed. +type ApplyComplete struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + State []byte `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"` + Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` + Timings []*Timing `protobuf:"bytes,3,rep,name=timings,proto3" json:"timings,omitempty"` +} + +func (x *ApplyComplete) Reset() { + *x = ApplyComplete{} + if protoimpl.UnsafeEnabled { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (x *PlanComplete) GetHasAiTasks() bool { - if x != nil { - return x.HasAiTasks +func (x *ApplyComplete) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApplyComplete) ProtoMessage() {} + +func (x *ApplyComplete) ProtoReflect() protoreflect.Message { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[42] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return false + return mi.MessageOf(x) +} + +// Deprecated: Use ApplyComplete.ProtoReflect.Descriptor instead. +func (*ApplyComplete) Descriptor() ([]byte, []int) { + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{42} } -func (x *PlanComplete) GetAiTasks() []*AITask { +func (x *ApplyComplete) GetState() []byte { if x != nil { - return x.AiTasks + return x.State } return nil } -func (x *PlanComplete) GetHasExternalAgents() bool { +func (x *ApplyComplete) GetError() string { if x != nil { - return x.HasExternalAgents + return x.Error } - return false + return "" } -// ApplyRequest asks the provisioner to apply the changes. Apply MUST be preceded by a successful plan request/response -// in the same Session. The plan data is not transmitted over the wire and is cached by the provisioner in the Session. -type ApplyRequest struct { +func (x *ApplyComplete) GetTimings() []*Timing { + if x != nil { + return x.Timings + } + return nil +} + +type GraphRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` + Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` + Source GraphSource `protobuf:"varint,2,opt,name=source,proto3,enum=provisioner.GraphSource" json:"source,omitempty"` } -func (x *ApplyRequest) Reset() { - *x = ApplyRequest{} +func (x *GraphRequest) Reset() { + *x = GraphRequest{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[39] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ApplyRequest) String() string { +func (x *GraphRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ApplyRequest) ProtoMessage() {} +func (*GraphRequest) ProtoMessage() {} -func (x *ApplyRequest) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[39] +func (x *GraphRequest) ProtoReflect() protoreflect.Message { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3639,50 +3857,63 @@ func (x *ApplyRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ApplyRequest.ProtoReflect.Descriptor instead. -func (*ApplyRequest) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{39} +// Deprecated: Use GraphRequest.ProtoReflect.Descriptor instead. +func (*GraphRequest) Descriptor() ([]byte, []int) { + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{43} } -func (x *ApplyRequest) GetMetadata() *Metadata { +func (x *GraphRequest) GetMetadata() *Metadata { if x != nil { return x.Metadata } return nil } -// ApplyComplete indicates a request to apply completed. -type ApplyComplete struct { +func (x *GraphRequest) GetSource() GraphSource { + if x != nil { + return x.Source + } + return GraphSource_SOURCE_UNKNOWN +} + +type GraphComplete struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - State []byte `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"` - Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` + Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` + Timings []*Timing `protobuf:"bytes,2,rep,name=timings,proto3" json:"timings,omitempty"` Resources []*Resource `protobuf:"bytes,3,rep,name=resources,proto3" json:"resources,omitempty"` Parameters []*RichParameter `protobuf:"bytes,4,rep,name=parameters,proto3" json:"parameters,omitempty"` ExternalAuthProviders []*ExternalAuthProviderResource `protobuf:"bytes,5,rep,name=external_auth_providers,json=externalAuthProviders,proto3" json:"external_auth_providers,omitempty"` - Timings []*Timing `protobuf:"bytes,6,rep,name=timings,proto3" json:"timings,omitempty"` - AiTasks []*AITask `protobuf:"bytes,7,rep,name=ai_tasks,json=aiTasks,proto3" json:"ai_tasks,omitempty"` + Presets []*Preset `protobuf:"bytes,6,rep,name=presets,proto3" json:"presets,omitempty"` + // Whether a template has any `coder_ai_task` resources defined, even if not planned for creation. + // During a template import, a plan is run which may not yield in any `coder_ai_task` resources, but nonetheless we + // still need to know that such resources are defined. + // + // See `hasAITaskResources` in provisioner/terraform/resources.go for more details. + HasAiTasks bool `protobuf:"varint,7,opt,name=has_ai_tasks,json=hasAiTasks,proto3" json:"has_ai_tasks,omitempty"` + AiTasks []*AITask `protobuf:"bytes,8,rep,name=ai_tasks,json=aiTasks,proto3" json:"ai_tasks,omitempty"` + HasExternalAgents bool `protobuf:"varint,9,opt,name=has_external_agents,json=hasExternalAgents,proto3" json:"has_external_agents,omitempty"` } -func (x *ApplyComplete) Reset() { - *x = ApplyComplete{} +func (x *GraphComplete) Reset() { + *x = GraphComplete{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[40] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ApplyComplete) String() string { +func (x *GraphComplete) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ApplyComplete) ProtoMessage() {} +func (*GraphComplete) ProtoMessage() {} -func (x *ApplyComplete) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[40] +func (x *GraphComplete) ProtoReflect() protoreflect.Message { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3693,60 +3924,74 @@ func (x *ApplyComplete) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ApplyComplete.ProtoReflect.Descriptor instead. -func (*ApplyComplete) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{40} +// Deprecated: Use GraphComplete.ProtoReflect.Descriptor instead. +func (*GraphComplete) Descriptor() ([]byte, []int) { + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{44} } -func (x *ApplyComplete) GetState() []byte { +func (x *GraphComplete) GetError() string { if x != nil { - return x.State + return x.Error } - return nil + return "" } -func (x *ApplyComplete) GetError() string { +func (x *GraphComplete) GetTimings() []*Timing { if x != nil { - return x.Error + return x.Timings } - return "" + return nil } -func (x *ApplyComplete) GetResources() []*Resource { +func (x *GraphComplete) GetResources() []*Resource { if x != nil { return x.Resources } return nil } -func (x *ApplyComplete) GetParameters() []*RichParameter { +func (x *GraphComplete) GetParameters() []*RichParameter { if x != nil { return x.Parameters } return nil } -func (x *ApplyComplete) GetExternalAuthProviders() []*ExternalAuthProviderResource { +func (x *GraphComplete) GetExternalAuthProviders() []*ExternalAuthProviderResource { if x != nil { return x.ExternalAuthProviders } return nil } -func (x *ApplyComplete) GetTimings() []*Timing { +func (x *GraphComplete) GetPresets() []*Preset { if x != nil { - return x.Timings + return x.Presets } return nil } -func (x *ApplyComplete) GetAiTasks() []*AITask { +func (x *GraphComplete) GetHasAiTasks() bool { + if x != nil { + return x.HasAiTasks + } + return false +} + +func (x *GraphComplete) GetAiTasks() []*AITask { if x != nil { return x.AiTasks } return nil } +func (x *GraphComplete) GetHasExternalAgents() bool { + if x != nil { + return x.HasExternalAgents + } + return false +} + type Timing struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3764,7 +4009,7 @@ type Timing struct { func (x *Timing) Reset() { *x = Timing{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[41] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3777,7 +4022,7 @@ func (x *Timing) String() string { func (*Timing) ProtoMessage() {} func (x *Timing) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[41] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3790,7 +4035,7 @@ func (x *Timing) ProtoReflect() protoreflect.Message { // Deprecated: Use Timing.ProtoReflect.Descriptor instead. func (*Timing) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{41} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{45} } func (x *Timing) GetStart() *timestamppb.Timestamp { @@ -3852,7 +4097,7 @@ type CancelRequest struct { func (x *CancelRequest) Reset() { *x = CancelRequest{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[42] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3865,7 +4110,7 @@ func (x *CancelRequest) String() string { func (*CancelRequest) ProtoMessage() {} func (x *CancelRequest) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[42] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3878,7 +4123,7 @@ func (x *CancelRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CancelRequest.ProtoReflect.Descriptor instead. func (*CancelRequest) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{42} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{46} } type Request struct { @@ -3890,8 +4135,10 @@ type Request struct { // // *Request_Config // *Request_Parse + // *Request_Init // *Request_Plan // *Request_Apply + // *Request_Graph // *Request_Cancel Type isRequest_Type `protobuf_oneof:"type"` } @@ -3899,7 +4146,7 @@ type Request struct { func (x *Request) Reset() { *x = Request{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[43] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3912,7 +4159,7 @@ func (x *Request) String() string { func (*Request) ProtoMessage() {} func (x *Request) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[43] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3925,7 +4172,7 @@ func (x *Request) ProtoReflect() protoreflect.Message { // Deprecated: Use Request.ProtoReflect.Descriptor instead. func (*Request) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{43} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{47} } func (m *Request) GetType() isRequest_Type { @@ -3949,6 +4196,13 @@ func (x *Request) GetParse() *ParseRequest { return nil } +func (x *Request) GetInit() *InitRequest { + if x, ok := x.GetType().(*Request_Init); ok { + return x.Init + } + return nil +} + func (x *Request) GetPlan() *PlanRequest { if x, ok := x.GetType().(*Request_Plan); ok { return x.Plan @@ -3963,6 +4217,13 @@ func (x *Request) GetApply() *ApplyRequest { return nil } +func (x *Request) GetGraph() *GraphRequest { + if x, ok := x.GetType().(*Request_Graph); ok { + return x.Graph + } + return nil +} + func (x *Request) GetCancel() *CancelRequest { if x, ok := x.GetType().(*Request_Cancel); ok { return x.Cancel @@ -3982,26 +4243,38 @@ type Request_Parse struct { Parse *ParseRequest `protobuf:"bytes,2,opt,name=parse,proto3,oneof"` } +type Request_Init struct { + Init *InitRequest `protobuf:"bytes,3,opt,name=init,proto3,oneof"` +} + type Request_Plan struct { - Plan *PlanRequest `protobuf:"bytes,3,opt,name=plan,proto3,oneof"` + Plan *PlanRequest `protobuf:"bytes,4,opt,name=plan,proto3,oneof"` } type Request_Apply struct { - Apply *ApplyRequest `protobuf:"bytes,4,opt,name=apply,proto3,oneof"` + Apply *ApplyRequest `protobuf:"bytes,5,opt,name=apply,proto3,oneof"` +} + +type Request_Graph struct { + Graph *GraphRequest `protobuf:"bytes,6,opt,name=graph,proto3,oneof"` } type Request_Cancel struct { - Cancel *CancelRequest `protobuf:"bytes,5,opt,name=cancel,proto3,oneof"` + Cancel *CancelRequest `protobuf:"bytes,7,opt,name=cancel,proto3,oneof"` } func (*Request_Config) isRequest_Type() {} func (*Request_Parse) isRequest_Type() {} +func (*Request_Init) isRequest_Type() {} + func (*Request_Plan) isRequest_Type() {} func (*Request_Apply) isRequest_Type() {} +func (*Request_Graph) isRequest_Type() {} + func (*Request_Cancel) isRequest_Type() {} type Response struct { @@ -4013,8 +4286,10 @@ type Response struct { // // *Response_Log // *Response_Parse + // *Response_Init // *Response_Plan // *Response_Apply + // *Response_Graph // *Response_DataUpload // *Response_ChunkPiece Type isResponse_Type `protobuf_oneof:"type"` @@ -4023,7 +4298,7 @@ type Response struct { func (x *Response) Reset() { *x = Response{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[44] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4036,7 +4311,7 @@ func (x *Response) String() string { func (*Response) ProtoMessage() {} func (x *Response) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[44] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4049,7 +4324,7 @@ func (x *Response) ProtoReflect() protoreflect.Message { // Deprecated: Use Response.ProtoReflect.Descriptor instead. func (*Response) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{44} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{48} } func (m *Response) GetType() isResponse_Type { @@ -4073,6 +4348,13 @@ func (x *Response) GetParse() *ParseComplete { return nil } +func (x *Response) GetInit() *InitComplete { + if x, ok := x.GetType().(*Response_Init); ok { + return x.Init + } + return nil +} + func (x *Response) GetPlan() *PlanComplete { if x, ok := x.GetType().(*Response_Plan); ok { return x.Plan @@ -4087,6 +4369,13 @@ func (x *Response) GetApply() *ApplyComplete { return nil } +func (x *Response) GetGraph() *GraphComplete { + if x, ok := x.GetType().(*Response_Graph); ok { + return x.Graph + } + return nil +} + func (x *Response) GetDataUpload() *DataUpload { if x, ok := x.GetType().(*Response_DataUpload); ok { return x.DataUpload @@ -4113,30 +4402,42 @@ type Response_Parse struct { Parse *ParseComplete `protobuf:"bytes,2,opt,name=parse,proto3,oneof"` } +type Response_Init struct { + Init *InitComplete `protobuf:"bytes,3,opt,name=init,proto3,oneof"` +} + type Response_Plan struct { - Plan *PlanComplete `protobuf:"bytes,3,opt,name=plan,proto3,oneof"` + Plan *PlanComplete `protobuf:"bytes,4,opt,name=plan,proto3,oneof"` } type Response_Apply struct { - Apply *ApplyComplete `protobuf:"bytes,4,opt,name=apply,proto3,oneof"` + Apply *ApplyComplete `protobuf:"bytes,5,opt,name=apply,proto3,oneof"` +} + +type Response_Graph struct { + Graph *GraphComplete `protobuf:"bytes,6,opt,name=graph,proto3,oneof"` } type Response_DataUpload struct { - DataUpload *DataUpload `protobuf:"bytes,5,opt,name=data_upload,json=dataUpload,proto3,oneof"` + DataUpload *DataUpload `protobuf:"bytes,7,opt,name=data_upload,json=dataUpload,proto3,oneof"` } type Response_ChunkPiece struct { - ChunkPiece *ChunkPiece `protobuf:"bytes,6,opt,name=chunk_piece,json=chunkPiece,proto3,oneof"` + ChunkPiece *ChunkPiece `protobuf:"bytes,8,opt,name=chunk_piece,json=chunkPiece,proto3,oneof"` } func (*Response_Log) isResponse_Type() {} func (*Response_Parse) isResponse_Type() {} +func (*Response_Init) isResponse_Type() {} + func (*Response_Plan) isResponse_Type() {} func (*Response_Apply) isResponse_Type() {} +func (*Response_Graph) isResponse_Type() {} + func (*Response_DataUpload) isResponse_Type() {} func (*Response_ChunkPiece) isResponse_Type() {} @@ -4159,7 +4460,7 @@ type DataUpload struct { func (x *DataUpload) Reset() { *x = DataUpload{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[45] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4172,7 +4473,7 @@ func (x *DataUpload) String() string { func (*DataUpload) ProtoMessage() {} func (x *DataUpload) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[45] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4185,7 +4486,7 @@ func (x *DataUpload) ProtoReflect() protoreflect.Message { // Deprecated: Use DataUpload.ProtoReflect.Descriptor instead. func (*DataUpload) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{45} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{49} } func (x *DataUpload) GetUploadType() DataUploadType { @@ -4232,7 +4533,7 @@ type ChunkPiece struct { func (x *ChunkPiece) Reset() { *x = ChunkPiece{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[46] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4245,7 +4546,7 @@ func (x *ChunkPiece) String() string { func (*ChunkPiece) ProtoMessage() {} func (x *ChunkPiece) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[46] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4258,7 +4559,7 @@ func (x *ChunkPiece) ProtoReflect() protoreflect.Message { // Deprecated: Use ChunkPiece.ProtoReflect.Descriptor instead. func (*ChunkPiece) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{46} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{50} } func (x *ChunkPiece) GetData() []byte { @@ -4298,7 +4599,7 @@ type Agent_Metadata struct { func (x *Agent_Metadata) Reset() { *x = Agent_Metadata{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[47] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4311,7 +4612,7 @@ func (x *Agent_Metadata) String() string { func (*Agent_Metadata) ProtoMessage() {} func (x *Agent_Metadata) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[47] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4383,7 +4684,7 @@ type Resource_Metadata struct { func (x *Resource_Metadata) Reset() { *x = Resource_Metadata{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[49] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4396,7 +4697,7 @@ func (x *Resource_Metadata) String() string { func (*Resource_Metadata) ProtoMessage() {} func (x *Resource_Metadata) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[49] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4884,266 +5185,291 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{ 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x18, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0xf7, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x12, 0x36, 0x0a, 0x17, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x15, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x32, - 0x0a, 0x15, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x5f, 0x6c, 0x6f, - 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, - 0x65, 0x6c, 0x12, 0x24, 0x0a, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x69, - 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0a, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x13, 0x74, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x46, 0x0a, - 0x1d, 0x65, 0x78, 0x70, 0x5f, 0x72, 0x65, 0x75, 0x73, 0x65, 0x5f, 0x74, 0x65, 0x72, 0x72, 0x61, - 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x08, 0x48, 0x02, 0x52, 0x1a, 0x65, 0x78, 0x70, 0x52, 0x65, 0x75, 0x73, 0x65, - 0x54, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x5f, 0x69, 0x64, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x42, 0x20, 0x0a, - 0x1e, 0x5f, 0x65, 0x78, 0x70, 0x5f, 0x72, 0x65, 0x75, 0x73, 0x65, 0x5f, 0x74, 0x65, 0x72, 0x72, - 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, - 0x0e, 0x0a, 0x0c, 0x50, 0x61, 0x72, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0xa3, 0x02, 0x0a, 0x0d, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, - 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x4c, 0x0a, 0x12, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, - 0x6c, 0x65, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, - 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x12, 0x54, 0x0a, - 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, - 0x65, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x61, 0x67, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, - 0x61, 0x67, 0x73, 0x1a, 0x40, 0x0a, 0x12, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xbe, 0x03, 0x0a, 0x0b, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, - 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 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, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, - 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x72, 0x69, 0x63, 0x68, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x43, 0x0a, - 0x0f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x0e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x12, 0x59, 0x0a, 0x17, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61, - 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x15, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x5b, 0x0a, - 0x19, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, - 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, + 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0xa9, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x5f, + 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x13, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x4c, + 0x65, 0x76, 0x65, 0x6c, 0x12, 0x24, 0x0a, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0a, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x13, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, + 0x46, 0x0a, 0x1d, 0x65, 0x78, 0x70, 0x5f, 0x72, 0x65, 0x75, 0x73, 0x65, 0x5f, 0x74, 0x65, 0x72, + 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x02, 0x52, 0x1a, 0x65, 0x78, 0x70, 0x52, 0x65, 0x75, + 0x73, 0x65, 0x54, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x57, 0x6f, 0x72, 0x6b, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x74, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x74, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x42, + 0x20, 0x0a, 0x1e, 0x5f, 0x65, 0x78, 0x70, 0x5f, 0x72, 0x65, 0x75, 0x73, 0x65, 0x5f, 0x74, 0x65, + 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x22, 0x0e, 0x0a, 0x0c, 0x50, 0x61, 0x72, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x22, 0xa3, 0x02, 0x0a, 0x0d, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, + 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x4c, 0x0a, 0x12, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, + 0x61, 0x62, 0x6c, 0x65, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, + 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x12, + 0x54, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x61, 0x67, + 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, + 0x65, 0x74, 0x65, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x61, 0x67, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x54, 0x61, 0x67, 0x73, 0x1a, 0x40, 0x0a, 0x12, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x71, 0x0a, 0x0b, 0x49, 0x6e, 0x69, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x17, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x15, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0x2a, + 0x0a, 0x11, 0x6f, 0x6d, 0x69, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x66, 0x69, + 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6f, 0x6d, 0x69, 0x74, 0x4d, + 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x22, 0xd1, 0x01, 0x0a, 0x0c, 0x49, + 0x6e, 0x69, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x12, 0x2d, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, + 0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, + 0x12, 0x2d, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, + 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, + 0x21, 0x0a, 0x0c, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x69, 0x6c, + 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x6c, + 0x65, 0x73, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x6d, + 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x48, 0x61, 0x73, 0x68, 0x22, 0xa8, + 0x03, 0x0a, 0x0b, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, + 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 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, 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, 0x17, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6f, 0x6d, - 0x69, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6f, 0x6d, 0x69, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, - 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x22, 0xc1, 0x05, 0x0a, 0x0c, 0x50, 0x6c, 0x61, 0x6e, 0x43, - 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x33, 0x0a, - 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x65, 0x52, 0x13, 0x72, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, + 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, + 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x76, 0x61, 0x72, + 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x59, 0x0a, 0x17, 0x65, + 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, + 0x15, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x5b, 0x0a, 0x19, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, + 0x75, 0x73, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x18, 0x05, 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, 0x17, 0x70, 0x72, 0x65, 0x76, + 0x69, 0x6f, 0x75, 0x73, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x80, 0x02, 0x0a, 0x0c, 0x50, 0x6c, + 0x61, 0x6e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x12, 0x2d, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, + 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x12, + 0x12, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x70, + 0x6c, 0x61, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x43, 0x6f, 0x73, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x43, 0x6f, 0x73, + 0x74, 0x12, 0x55, 0x0a, 0x15, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, + 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x52, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, + 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x61, 0x69, 0x5f, 0x74, + 0x61, 0x73, 0x6b, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x0b, 0x61, 0x69, 0x54, 0x61, 0x73, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x57, 0x0a, 0x0c, + 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x08, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, + 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x6a, 0x0a, 0x0d, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x6f, + 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x12, 0x2d, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x72, 0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, + 0x73, 0x22, 0x73, 0x0a, 0x0c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x12, 0x30, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x06, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0xd9, 0x03, 0x0a, 0x0d, 0x47, 0x72, 0x61, 0x70, 0x68, + 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x2d, + 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x69, + 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x33, 0x0a, + 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x61, 0x0a, 0x17, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x15, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x06, 0x20, 0x03, + 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, - 0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, - 0x12, 0x2d, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, - 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, - 0x2d, 0x0a, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, - 0x72, 0x65, 0x73, 0x65, 0x74, 0x52, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x74, 0x73, 0x12, 0x12, - 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x70, 0x6c, - 0x61, 0x6e, 0x12, 0x55, 0x0a, 0x15, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, - 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x52, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x70, - 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x11, - 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x68, 0x61, 0x73, - 0x68, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, - 0x69, 0x6c, 0x65, 0x73, 0x48, 0x61, 0x73, 0x68, 0x12, 0x20, 0x0a, 0x0c, 0x68, 0x61, 0x73, 0x5f, - 0x61, 0x69, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, - 0x68, 0x61, 0x73, 0x41, 0x69, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x2e, 0x0a, 0x08, 0x61, 0x69, - 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x49, 0x54, 0x61, 0x73, - 0x6b, 0x52, 0x07, 0x61, 0x69, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x68, 0x61, - 0x73, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x68, 0x61, 0x73, 0x45, 0x78, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x41, 0x0a, 0x0c, 0x41, 0x70, - 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0xee, 0x02, - 0x0a, 0x0d, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, - 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x33, 0x0a, 0x09, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, - 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, - 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x61, 0x0a, 0x17, - 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x45, 0x78, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x15, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, - 0x2d, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, - 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x2e, - 0x0a, 0x08, 0x61, 0x69, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, - 0x49, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x07, 0x61, 0x69, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x22, 0xfa, - 0x01, 0x0a, 0x06, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x12, 0x30, 0x0a, 0x05, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2c, 0x0a, 0x03, 0x65, - 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x12, 0x2e, 0x0a, 0x05, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x0f, 0x0a, 0x0d, 0x43, - 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8c, 0x02, 0x0a, - 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, - 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x31, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x48, 0x00, 0x52, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x70, 0x6c, - 0x61, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x31, 0x0a, 0x05, 0x61, 0x70, - 0x70, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x12, 0x34, 0x0a, - 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x43, 0x61, 0x6e, 0x63, - 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xc9, 0x02, 0x0a, 0x08, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x48, 0x00, 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x12, 0x32, - 0x0a, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, - 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x05, 0x70, 0x61, 0x72, - 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, - 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x04, 0x70, - 0x6c, 0x61, 0x6e, 0x12, 0x32, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, - 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, - 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x12, 0x3a, 0x0a, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x5f, - 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x55, - 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x00, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x6c, - 0x6f, 0x61, 0x64, 0x12, 0x3a, 0x0a, 0x0b, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x70, 0x69, 0x65, - 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x50, 0x69, 0x65, 0x63, - 0x65, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x50, 0x69, 0x65, 0x63, 0x65, 0x42, - 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x9c, 0x01, 0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, - 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x3c, 0x0a, 0x0b, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x55, 0x70, - 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x68, 0x61, 0x73, - 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x48, 0x61, 0x73, - 0x68, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x16, - 0x0a, 0x06, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, - 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x22, 0x67, 0x0a, 0x0a, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x50, - 0x69, 0x65, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x24, 0x0a, 0x0e, 0x66, 0x75, 0x6c, 0x6c, - 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x0c, 0x66, 0x75, 0x6c, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1f, - 0x0a, 0x0b, 0x70, 0x69, 0x65, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0a, 0x70, 0x69, 0x65, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x2a, - 0xa8, 0x01, 0x0a, 0x11, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x46, 0x6f, 0x72, - 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, - 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x4d, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, - 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x41, 0x44, 0x49, 0x4f, 0x10, 0x02, 0x12, 0x0c, 0x0a, - 0x08, 0x44, 0x52, 0x4f, 0x50, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x49, - 0x4e, 0x50, 0x55, 0x54, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x54, 0x45, 0x58, 0x54, 0x41, 0x52, - 0x45, 0x41, 0x10, 0x05, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x4c, 0x49, 0x44, 0x45, 0x52, 0x10, 0x06, - 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x42, 0x4f, 0x58, 0x10, 0x07, 0x12, 0x0a, - 0x0a, 0x06, 0x53, 0x57, 0x49, 0x54, 0x43, 0x48, 0x10, 0x08, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x41, - 0x47, 0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 0x10, 0x09, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x55, 0x4c, - 0x54, 0x49, 0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 0x10, 0x0a, 0x2a, 0x3f, 0x0a, 0x08, 0x4c, 0x6f, - 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, - 0x00, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, - 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x03, - 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x2a, 0x3b, 0x0a, 0x0f, 0x41, - 0x70, 0x70, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, - 0x0a, 0x05, 0x4f, 0x57, 0x4e, 0x45, 0x52, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x55, 0x54, - 0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, - 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x02, 0x2a, 0x35, 0x0a, 0x09, 0x41, 0x70, 0x70, 0x4f, - 0x70, 0x65, 0x6e, 0x49, 0x6e, 0x12, 0x0e, 0x0a, 0x06, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x10, - 0x00, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x4c, 0x49, 0x4d, 0x5f, 0x57, 0x49, - 0x4e, 0x44, 0x4f, 0x57, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x41, 0x42, 0x10, 0x02, 0x2a, - 0x37, 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, - 0x00, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x54, 0x4f, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x44, - 0x45, 0x53, 0x54, 0x52, 0x4f, 0x59, 0x10, 0x02, 0x2a, 0x3e, 0x0a, 0x1b, 0x50, 0x72, 0x65, 0x62, - 0x75, 0x69, 0x6c, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, - 0x6c, 0x64, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, - 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x01, 0x12, 0x09, 0x0a, - 0x05, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x10, 0x02, 0x2a, 0x35, 0x0a, 0x0b, 0x54, 0x69, 0x6d, 0x69, - 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x41, 0x52, 0x54, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, - 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x2a, - 0x47, 0x0a, 0x0e, 0x44, 0x61, 0x74, 0x61, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x17, 0x0a, 0x13, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x55, 0x50, - 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x55, 0x4c, 0x45, - 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x10, 0x01, 0x32, 0x49, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x3a, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, - 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, - 0x01, 0x30, 0x01, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, - 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x64, 0x6b, 0x2f, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x50, 0x72, 0x65, 0x73, 0x65, 0x74, 0x52, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x74, 0x73, + 0x12, 0x20, 0x0a, 0x0c, 0x68, 0x61, 0x73, 0x5f, 0x61, 0x69, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x73, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x68, 0x61, 0x73, 0x41, 0x69, 0x54, 0x61, 0x73, + 0x6b, 0x73, 0x12, 0x2e, 0x0a, 0x08, 0x61, 0x69, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x08, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x2e, 0x41, 0x49, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x07, 0x61, 0x69, 0x54, 0x61, 0x73, + 0x6b, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x68, 0x61, 0x73, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x11, 0x68, 0x61, 0x73, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x67, 0x65, 0x6e, + 0x74, 0x73, 0x22, 0xfa, 0x01, 0x0a, 0x06, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x12, 0x30, 0x0a, + 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, + 0x2c, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x16, 0x0a, + 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1a, 0x0a, + 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, + 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x12, + 0x2e, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x69, 0x6d, + 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, + 0x0f, 0x0a, 0x0d, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x22, 0xef, 0x02, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x06, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x48, 0x00, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x31, 0x0a, 0x05, 0x70, + 0x61, 0x72, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x12, 0x2e, + 0x0a, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x12, 0x2e, + 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x31, + 0x0a, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x6c, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, + 0x79, 0x12, 0x31, 0x0a, 0x05, 0x67, 0x72, 0x61, 0x70, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x47, + 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x67, + 0x72, 0x61, 0x70, 0x68, 0x12, 0x34, 0x0a, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x22, 0xae, 0x03, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x24, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x48, 0x00, + 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x12, 0x32, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, + 0x48, 0x00, 0x52, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x69, 0x6e, 0x69, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, + 0x74, 0x65, 0x48, 0x00, 0x52, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x12, 0x2f, 0x0a, 0x04, 0x70, 0x6c, + 0x61, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, + 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x32, 0x0a, 0x05, 0x61, + 0x70, 0x70, 0x6c, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x6f, + 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x12, + 0x32, 0x0a, 0x05, 0x67, 0x72, 0x61, 0x70, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x47, 0x72, 0x61, + 0x70, 0x68, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x05, 0x67, 0x72, + 0x61, 0x70, 0x68, 0x12, 0x3a, 0x0a, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x75, 0x70, 0x6c, 0x6f, + 0x61, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x55, 0x70, 0x6c, 0x6f, 0x61, + 0x64, 0x48, 0x00, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, + 0x3a, 0x0a, 0x0b, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x70, 0x69, 0x65, 0x63, 0x65, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x2e, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x50, 0x69, 0x65, 0x63, 0x65, 0x48, 0x00, 0x52, + 0x0a, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x50, 0x69, 0x65, 0x63, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x22, 0x9c, 0x01, 0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x55, 0x70, 0x6c, 0x6f, + 0x61, 0x64, 0x12, 0x3c, 0x0a, 0x0b, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, + 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1b, 0x0a, + 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x68, + 0x75, 0x6e, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x63, 0x68, 0x75, 0x6e, + 0x6b, 0x73, 0x22, 0x67, 0x0a, 0x0a, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x50, 0x69, 0x65, 0x63, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, + 0x64, 0x61, 0x74, 0x61, 0x12, 0x24, 0x0a, 0x0e, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x64, 0x61, 0x74, + 0x61, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x66, 0x75, + 0x6c, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x69, + 0x65, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x0a, 0x70, 0x69, 0x65, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x2a, 0xa8, 0x01, 0x0a, 0x11, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x46, 0x6f, 0x72, 0x6d, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x0e, + 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x4d, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x09, + 0x0a, 0x05, 0x52, 0x41, 0x44, 0x49, 0x4f, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x52, 0x4f, + 0x50, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x49, 0x4e, 0x50, 0x55, 0x54, + 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x54, 0x45, 0x58, 0x54, 0x41, 0x52, 0x45, 0x41, 0x10, 0x05, + 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x4c, 0x49, 0x44, 0x45, 0x52, 0x10, 0x06, 0x12, 0x0c, 0x0a, 0x08, + 0x43, 0x48, 0x45, 0x43, 0x4b, 0x42, 0x4f, 0x58, 0x10, 0x07, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x57, + 0x49, 0x54, 0x43, 0x48, 0x10, 0x08, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x41, 0x47, 0x53, 0x45, 0x4c, + 0x45, 0x43, 0x54, 0x10, 0x09, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x53, 0x45, + 0x4c, 0x45, 0x43, 0x54, 0x10, 0x0a, 0x2a, 0x3f, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, + 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x00, 0x12, 0x09, 0x0a, + 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, + 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, + 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x2a, 0x3b, 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x53, 0x68, + 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x57, + 0x4e, 0x45, 0x52, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, + 0x49, 0x43, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c, + 0x49, 0x43, 0x10, 0x02, 0x2a, 0x35, 0x0a, 0x09, 0x41, 0x70, 0x70, 0x4f, 0x70, 0x65, 0x6e, 0x49, + 0x6e, 0x12, 0x0e, 0x0a, 0x06, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x10, 0x00, 0x1a, 0x02, 0x08, + 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x4c, 0x49, 0x4d, 0x5f, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, + 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x41, 0x42, 0x10, 0x02, 0x2a, 0x37, 0x0a, 0x13, 0x57, + 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, + 0x04, 0x53, 0x54, 0x4f, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x53, 0x54, 0x52, + 0x4f, 0x59, 0x10, 0x02, 0x2a, 0x3e, 0x0a, 0x1b, 0x50, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x74, + 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, + 0x61, 0x67, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, + 0x06, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x43, 0x4c, 0x41, + 0x49, 0x4d, 0x10, 0x02, 0x2a, 0x44, 0x0a, 0x0b, 0x47, 0x72, 0x61, 0x70, 0x68, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x55, 0x4e, + 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x4f, 0x55, 0x52, 0x43, + 0x45, 0x5f, 0x50, 0x4c, 0x41, 0x4e, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x4f, 0x55, 0x52, + 0x43, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x10, 0x02, 0x2a, 0x35, 0x0a, 0x0b, 0x54, 0x69, + 0x6d, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x41, + 0x52, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, + 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, + 0x02, 0x2a, 0x47, 0x0a, 0x0e, 0x44, 0x61, 0x74, 0x61, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x13, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, + 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x55, + 0x4c, 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x10, 0x01, 0x32, 0x49, 0x0a, 0x0b, 0x50, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x3a, 0x0a, 0x07, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x28, 0x01, 0x30, 0x01, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, + 0x76, 0x32, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x64, + 0x6b, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -5158,8 +5484,8 @@ func file_provisionersdk_proto_provisioner_proto_rawDescGZIP() []byte { return file_provisionersdk_proto_provisioner_proto_rawDescData } -var file_provisionersdk_proto_provisioner_proto_enumTypes = make([]protoimpl.EnumInfo, 8) -var file_provisionersdk_proto_provisioner_proto_msgTypes = make([]protoimpl.MessageInfo, 51) +var file_provisionersdk_proto_provisioner_proto_enumTypes = make([]protoimpl.EnumInfo, 9) +var file_provisionersdk_proto_provisioner_proto_msgTypes = make([]protoimpl.MessageInfo, 55) var file_provisionersdk_proto_provisioner_proto_goTypes = []interface{}{ (ParameterFormType)(0), // 0: provisioner.ParameterFormType (LogLevel)(0), // 1: provisioner.LogLevel @@ -5167,133 +5493,142 @@ var file_provisionersdk_proto_provisioner_proto_goTypes = []interface{}{ (AppOpenIn)(0), // 3: provisioner.AppOpenIn (WorkspaceTransition)(0), // 4: provisioner.WorkspaceTransition (PrebuiltWorkspaceBuildStage)(0), // 5: provisioner.PrebuiltWorkspaceBuildStage - (TimingState)(0), // 6: provisioner.TimingState - (DataUploadType)(0), // 7: provisioner.DataUploadType - (*Empty)(nil), // 8: provisioner.Empty - (*TemplateVariable)(nil), // 9: provisioner.TemplateVariable - (*RichParameterOption)(nil), // 10: provisioner.RichParameterOption - (*RichParameter)(nil), // 11: provisioner.RichParameter - (*RichParameterValue)(nil), // 12: provisioner.RichParameterValue - (*ExpirationPolicy)(nil), // 13: provisioner.ExpirationPolicy - (*Schedule)(nil), // 14: provisioner.Schedule - (*Scheduling)(nil), // 15: provisioner.Scheduling - (*Prebuild)(nil), // 16: provisioner.Prebuild - (*Preset)(nil), // 17: provisioner.Preset - (*PresetParameter)(nil), // 18: provisioner.PresetParameter - (*ResourceReplacement)(nil), // 19: provisioner.ResourceReplacement - (*VariableValue)(nil), // 20: provisioner.VariableValue - (*Log)(nil), // 21: provisioner.Log - (*InstanceIdentityAuth)(nil), // 22: provisioner.InstanceIdentityAuth - (*ExternalAuthProviderResource)(nil), // 23: provisioner.ExternalAuthProviderResource - (*ExternalAuthProvider)(nil), // 24: provisioner.ExternalAuthProvider - (*Agent)(nil), // 25: provisioner.Agent - (*ResourcesMonitoring)(nil), // 26: provisioner.ResourcesMonitoring - (*MemoryResourceMonitor)(nil), // 27: provisioner.MemoryResourceMonitor - (*VolumeResourceMonitor)(nil), // 28: provisioner.VolumeResourceMonitor - (*DisplayApps)(nil), // 29: provisioner.DisplayApps - (*Env)(nil), // 30: provisioner.Env - (*Script)(nil), // 31: provisioner.Script - (*Devcontainer)(nil), // 32: provisioner.Devcontainer - (*App)(nil), // 33: provisioner.App - (*Healthcheck)(nil), // 34: provisioner.Healthcheck - (*Resource)(nil), // 35: provisioner.Resource - (*Module)(nil), // 36: provisioner.Module - (*Role)(nil), // 37: provisioner.Role - (*RunningAgentAuthToken)(nil), // 38: provisioner.RunningAgentAuthToken - (*AITaskSidebarApp)(nil), // 39: provisioner.AITaskSidebarApp - (*AITask)(nil), // 40: provisioner.AITask - (*Metadata)(nil), // 41: provisioner.Metadata - (*Config)(nil), // 42: provisioner.Config - (*ParseRequest)(nil), // 43: provisioner.ParseRequest - (*ParseComplete)(nil), // 44: provisioner.ParseComplete - (*PlanRequest)(nil), // 45: provisioner.PlanRequest - (*PlanComplete)(nil), // 46: provisioner.PlanComplete - (*ApplyRequest)(nil), // 47: provisioner.ApplyRequest - (*ApplyComplete)(nil), // 48: provisioner.ApplyComplete - (*Timing)(nil), // 49: provisioner.Timing - (*CancelRequest)(nil), // 50: provisioner.CancelRequest - (*Request)(nil), // 51: provisioner.Request - (*Response)(nil), // 52: provisioner.Response - (*DataUpload)(nil), // 53: provisioner.DataUpload - (*ChunkPiece)(nil), // 54: provisioner.ChunkPiece - (*Agent_Metadata)(nil), // 55: provisioner.Agent.Metadata - nil, // 56: provisioner.Agent.EnvEntry - (*Resource_Metadata)(nil), // 57: provisioner.Resource.Metadata - nil, // 58: provisioner.ParseComplete.WorkspaceTagsEntry - (*timestamppb.Timestamp)(nil), // 59: google.protobuf.Timestamp + (GraphSource)(0), // 6: provisioner.GraphSource + (TimingState)(0), // 7: provisioner.TimingState + (DataUploadType)(0), // 8: provisioner.DataUploadType + (*Empty)(nil), // 9: provisioner.Empty + (*TemplateVariable)(nil), // 10: provisioner.TemplateVariable + (*RichParameterOption)(nil), // 11: provisioner.RichParameterOption + (*RichParameter)(nil), // 12: provisioner.RichParameter + (*RichParameterValue)(nil), // 13: provisioner.RichParameterValue + (*ExpirationPolicy)(nil), // 14: provisioner.ExpirationPolicy + (*Schedule)(nil), // 15: provisioner.Schedule + (*Scheduling)(nil), // 16: provisioner.Scheduling + (*Prebuild)(nil), // 17: provisioner.Prebuild + (*Preset)(nil), // 18: provisioner.Preset + (*PresetParameter)(nil), // 19: provisioner.PresetParameter + (*ResourceReplacement)(nil), // 20: provisioner.ResourceReplacement + (*VariableValue)(nil), // 21: provisioner.VariableValue + (*Log)(nil), // 22: provisioner.Log + (*InstanceIdentityAuth)(nil), // 23: provisioner.InstanceIdentityAuth + (*ExternalAuthProviderResource)(nil), // 24: provisioner.ExternalAuthProviderResource + (*ExternalAuthProvider)(nil), // 25: provisioner.ExternalAuthProvider + (*Agent)(nil), // 26: provisioner.Agent + (*ResourcesMonitoring)(nil), // 27: provisioner.ResourcesMonitoring + (*MemoryResourceMonitor)(nil), // 28: provisioner.MemoryResourceMonitor + (*VolumeResourceMonitor)(nil), // 29: provisioner.VolumeResourceMonitor + (*DisplayApps)(nil), // 30: provisioner.DisplayApps + (*Env)(nil), // 31: provisioner.Env + (*Script)(nil), // 32: provisioner.Script + (*Devcontainer)(nil), // 33: provisioner.Devcontainer + (*App)(nil), // 34: provisioner.App + (*Healthcheck)(nil), // 35: provisioner.Healthcheck + (*Resource)(nil), // 36: provisioner.Resource + (*Module)(nil), // 37: provisioner.Module + (*Role)(nil), // 38: provisioner.Role + (*RunningAgentAuthToken)(nil), // 39: provisioner.RunningAgentAuthToken + (*AITaskSidebarApp)(nil), // 40: provisioner.AITaskSidebarApp + (*AITask)(nil), // 41: provisioner.AITask + (*Metadata)(nil), // 42: provisioner.Metadata + (*Config)(nil), // 43: provisioner.Config + (*ParseRequest)(nil), // 44: provisioner.ParseRequest + (*ParseComplete)(nil), // 45: provisioner.ParseComplete + (*InitRequest)(nil), // 46: provisioner.InitRequest + (*InitComplete)(nil), // 47: provisioner.InitComplete + (*PlanRequest)(nil), // 48: provisioner.PlanRequest + (*PlanComplete)(nil), // 49: provisioner.PlanComplete + (*ApplyRequest)(nil), // 50: provisioner.ApplyRequest + (*ApplyComplete)(nil), // 51: provisioner.ApplyComplete + (*GraphRequest)(nil), // 52: provisioner.GraphRequest + (*GraphComplete)(nil), // 53: provisioner.GraphComplete + (*Timing)(nil), // 54: provisioner.Timing + (*CancelRequest)(nil), // 55: provisioner.CancelRequest + (*Request)(nil), // 56: provisioner.Request + (*Response)(nil), // 57: provisioner.Response + (*DataUpload)(nil), // 58: provisioner.DataUpload + (*ChunkPiece)(nil), // 59: provisioner.ChunkPiece + (*Agent_Metadata)(nil), // 60: provisioner.Agent.Metadata + nil, // 61: provisioner.Agent.EnvEntry + (*Resource_Metadata)(nil), // 62: provisioner.Resource.Metadata + nil, // 63: provisioner.ParseComplete.WorkspaceTagsEntry + (*timestamppb.Timestamp)(nil), // 64: google.protobuf.Timestamp } var file_provisionersdk_proto_provisioner_proto_depIdxs = []int32{ - 10, // 0: provisioner.RichParameter.options:type_name -> provisioner.RichParameterOption + 11, // 0: provisioner.RichParameter.options:type_name -> provisioner.RichParameterOption 0, // 1: provisioner.RichParameter.form_type:type_name -> provisioner.ParameterFormType - 14, // 2: provisioner.Scheduling.schedule:type_name -> provisioner.Schedule - 13, // 3: provisioner.Prebuild.expiration_policy:type_name -> provisioner.ExpirationPolicy - 15, // 4: provisioner.Prebuild.scheduling:type_name -> provisioner.Scheduling - 18, // 5: provisioner.Preset.parameters:type_name -> provisioner.PresetParameter - 16, // 6: provisioner.Preset.prebuild:type_name -> provisioner.Prebuild + 15, // 2: provisioner.Scheduling.schedule:type_name -> provisioner.Schedule + 14, // 3: provisioner.Prebuild.expiration_policy:type_name -> provisioner.ExpirationPolicy + 16, // 4: provisioner.Prebuild.scheduling:type_name -> provisioner.Scheduling + 19, // 5: provisioner.Preset.parameters:type_name -> provisioner.PresetParameter + 17, // 6: provisioner.Preset.prebuild:type_name -> provisioner.Prebuild 1, // 7: provisioner.Log.level:type_name -> provisioner.LogLevel - 56, // 8: provisioner.Agent.env:type_name -> provisioner.Agent.EnvEntry - 33, // 9: provisioner.Agent.apps:type_name -> provisioner.App - 55, // 10: provisioner.Agent.metadata:type_name -> provisioner.Agent.Metadata - 29, // 11: provisioner.Agent.display_apps:type_name -> provisioner.DisplayApps - 31, // 12: provisioner.Agent.scripts:type_name -> provisioner.Script - 30, // 13: provisioner.Agent.extra_envs:type_name -> provisioner.Env - 26, // 14: provisioner.Agent.resources_monitoring:type_name -> provisioner.ResourcesMonitoring - 32, // 15: provisioner.Agent.devcontainers:type_name -> provisioner.Devcontainer - 27, // 16: provisioner.ResourcesMonitoring.memory:type_name -> provisioner.MemoryResourceMonitor - 28, // 17: provisioner.ResourcesMonitoring.volumes:type_name -> provisioner.VolumeResourceMonitor - 34, // 18: provisioner.App.healthcheck:type_name -> provisioner.Healthcheck + 61, // 8: provisioner.Agent.env:type_name -> provisioner.Agent.EnvEntry + 34, // 9: provisioner.Agent.apps:type_name -> provisioner.App + 60, // 10: provisioner.Agent.metadata:type_name -> provisioner.Agent.Metadata + 30, // 11: provisioner.Agent.display_apps:type_name -> provisioner.DisplayApps + 32, // 12: provisioner.Agent.scripts:type_name -> provisioner.Script + 31, // 13: provisioner.Agent.extra_envs:type_name -> provisioner.Env + 27, // 14: provisioner.Agent.resources_monitoring:type_name -> provisioner.ResourcesMonitoring + 33, // 15: provisioner.Agent.devcontainers:type_name -> provisioner.Devcontainer + 28, // 16: provisioner.ResourcesMonitoring.memory:type_name -> provisioner.MemoryResourceMonitor + 29, // 17: provisioner.ResourcesMonitoring.volumes:type_name -> provisioner.VolumeResourceMonitor + 35, // 18: provisioner.App.healthcheck:type_name -> provisioner.Healthcheck 2, // 19: provisioner.App.sharing_level:type_name -> provisioner.AppSharingLevel 3, // 20: provisioner.App.open_in:type_name -> provisioner.AppOpenIn - 25, // 21: provisioner.Resource.agents:type_name -> provisioner.Agent - 57, // 22: provisioner.Resource.metadata:type_name -> provisioner.Resource.Metadata - 39, // 23: provisioner.AITask.sidebar_app:type_name -> provisioner.AITaskSidebarApp + 26, // 21: provisioner.Resource.agents:type_name -> provisioner.Agent + 62, // 22: provisioner.Resource.metadata:type_name -> provisioner.Resource.Metadata + 40, // 23: provisioner.AITask.sidebar_app:type_name -> provisioner.AITaskSidebarApp 4, // 24: provisioner.Metadata.workspace_transition:type_name -> provisioner.WorkspaceTransition - 37, // 25: provisioner.Metadata.workspace_owner_rbac_roles:type_name -> provisioner.Role + 38, // 25: provisioner.Metadata.workspace_owner_rbac_roles:type_name -> provisioner.Role 5, // 26: provisioner.Metadata.prebuilt_workspace_build_stage:type_name -> provisioner.PrebuiltWorkspaceBuildStage - 38, // 27: provisioner.Metadata.running_agent_auth_tokens:type_name -> provisioner.RunningAgentAuthToken - 9, // 28: provisioner.ParseComplete.template_variables:type_name -> provisioner.TemplateVariable - 58, // 29: provisioner.ParseComplete.workspace_tags:type_name -> provisioner.ParseComplete.WorkspaceTagsEntry - 41, // 30: provisioner.PlanRequest.metadata:type_name -> provisioner.Metadata - 12, // 31: provisioner.PlanRequest.rich_parameter_values:type_name -> provisioner.RichParameterValue - 20, // 32: provisioner.PlanRequest.variable_values:type_name -> provisioner.VariableValue - 24, // 33: provisioner.PlanRequest.external_auth_providers:type_name -> provisioner.ExternalAuthProvider - 12, // 34: provisioner.PlanRequest.previous_parameter_values:type_name -> provisioner.RichParameterValue - 35, // 35: provisioner.PlanComplete.resources:type_name -> provisioner.Resource - 11, // 36: provisioner.PlanComplete.parameters:type_name -> provisioner.RichParameter - 23, // 37: provisioner.PlanComplete.external_auth_providers:type_name -> provisioner.ExternalAuthProviderResource - 49, // 38: provisioner.PlanComplete.timings:type_name -> provisioner.Timing - 36, // 39: provisioner.PlanComplete.modules:type_name -> provisioner.Module - 17, // 40: provisioner.PlanComplete.presets:type_name -> provisioner.Preset - 19, // 41: provisioner.PlanComplete.resource_replacements:type_name -> provisioner.ResourceReplacement - 40, // 42: provisioner.PlanComplete.ai_tasks:type_name -> provisioner.AITask - 41, // 43: provisioner.ApplyRequest.metadata:type_name -> provisioner.Metadata - 35, // 44: provisioner.ApplyComplete.resources:type_name -> provisioner.Resource - 11, // 45: provisioner.ApplyComplete.parameters:type_name -> provisioner.RichParameter - 23, // 46: provisioner.ApplyComplete.external_auth_providers:type_name -> provisioner.ExternalAuthProviderResource - 49, // 47: provisioner.ApplyComplete.timings:type_name -> provisioner.Timing - 40, // 48: provisioner.ApplyComplete.ai_tasks:type_name -> provisioner.AITask - 59, // 49: provisioner.Timing.start:type_name -> google.protobuf.Timestamp - 59, // 50: provisioner.Timing.end:type_name -> google.protobuf.Timestamp - 6, // 51: provisioner.Timing.state:type_name -> provisioner.TimingState - 42, // 52: provisioner.Request.config:type_name -> provisioner.Config - 43, // 53: provisioner.Request.parse:type_name -> provisioner.ParseRequest - 45, // 54: provisioner.Request.plan:type_name -> provisioner.PlanRequest - 47, // 55: provisioner.Request.apply:type_name -> provisioner.ApplyRequest - 50, // 56: provisioner.Request.cancel:type_name -> provisioner.CancelRequest - 21, // 57: provisioner.Response.log:type_name -> provisioner.Log - 44, // 58: provisioner.Response.parse:type_name -> provisioner.ParseComplete - 46, // 59: provisioner.Response.plan:type_name -> provisioner.PlanComplete - 48, // 60: provisioner.Response.apply:type_name -> provisioner.ApplyComplete - 53, // 61: provisioner.Response.data_upload:type_name -> provisioner.DataUpload - 54, // 62: provisioner.Response.chunk_piece:type_name -> provisioner.ChunkPiece - 7, // 63: provisioner.DataUpload.upload_type:type_name -> provisioner.DataUploadType - 51, // 64: provisioner.Provisioner.Session:input_type -> provisioner.Request - 52, // 65: provisioner.Provisioner.Session:output_type -> provisioner.Response - 65, // [65:66] is the sub-list for method output_type - 64, // [64:65] is the sub-list for method input_type - 64, // [64:64] is the sub-list for extension type_name - 64, // [64:64] is the sub-list for extension extendee - 0, // [0:64] is the sub-list for field type_name + 39, // 27: provisioner.Metadata.running_agent_auth_tokens:type_name -> provisioner.RunningAgentAuthToken + 10, // 28: provisioner.ParseComplete.template_variables:type_name -> provisioner.TemplateVariable + 63, // 29: provisioner.ParseComplete.workspace_tags:type_name -> provisioner.ParseComplete.WorkspaceTagsEntry + 54, // 30: provisioner.InitComplete.timings:type_name -> provisioner.Timing + 37, // 31: provisioner.InitComplete.modules:type_name -> provisioner.Module + 42, // 32: provisioner.PlanRequest.metadata:type_name -> provisioner.Metadata + 13, // 33: provisioner.PlanRequest.rich_parameter_values:type_name -> provisioner.RichParameterValue + 21, // 34: provisioner.PlanRequest.variable_values:type_name -> provisioner.VariableValue + 25, // 35: provisioner.PlanRequest.external_auth_providers:type_name -> provisioner.ExternalAuthProvider + 13, // 36: provisioner.PlanRequest.previous_parameter_values:type_name -> provisioner.RichParameterValue + 54, // 37: provisioner.PlanComplete.timings:type_name -> provisioner.Timing + 20, // 38: provisioner.PlanComplete.resource_replacements:type_name -> provisioner.ResourceReplacement + 42, // 39: provisioner.ApplyRequest.metadata:type_name -> provisioner.Metadata + 54, // 40: provisioner.ApplyComplete.timings:type_name -> provisioner.Timing + 42, // 41: provisioner.GraphRequest.metadata:type_name -> provisioner.Metadata + 6, // 42: provisioner.GraphRequest.source:type_name -> provisioner.GraphSource + 54, // 43: provisioner.GraphComplete.timings:type_name -> provisioner.Timing + 36, // 44: provisioner.GraphComplete.resources:type_name -> provisioner.Resource + 12, // 45: provisioner.GraphComplete.parameters:type_name -> provisioner.RichParameter + 24, // 46: provisioner.GraphComplete.external_auth_providers:type_name -> provisioner.ExternalAuthProviderResource + 18, // 47: provisioner.GraphComplete.presets:type_name -> provisioner.Preset + 41, // 48: provisioner.GraphComplete.ai_tasks:type_name -> provisioner.AITask + 64, // 49: provisioner.Timing.start:type_name -> google.protobuf.Timestamp + 64, // 50: provisioner.Timing.end:type_name -> google.protobuf.Timestamp + 7, // 51: provisioner.Timing.state:type_name -> provisioner.TimingState + 43, // 52: provisioner.Request.config:type_name -> provisioner.Config + 44, // 53: provisioner.Request.parse:type_name -> provisioner.ParseRequest + 46, // 54: provisioner.Request.init:type_name -> provisioner.InitRequest + 48, // 55: provisioner.Request.plan:type_name -> provisioner.PlanRequest + 50, // 56: provisioner.Request.apply:type_name -> provisioner.ApplyRequest + 52, // 57: provisioner.Request.graph:type_name -> provisioner.GraphRequest + 55, // 58: provisioner.Request.cancel:type_name -> provisioner.CancelRequest + 22, // 59: provisioner.Response.log:type_name -> provisioner.Log + 45, // 60: provisioner.Response.parse:type_name -> provisioner.ParseComplete + 47, // 61: provisioner.Response.init:type_name -> provisioner.InitComplete + 49, // 62: provisioner.Response.plan:type_name -> provisioner.PlanComplete + 51, // 63: provisioner.Response.apply:type_name -> provisioner.ApplyComplete + 53, // 64: provisioner.Response.graph:type_name -> provisioner.GraphComplete + 58, // 65: provisioner.Response.data_upload:type_name -> provisioner.DataUpload + 59, // 66: provisioner.Response.chunk_piece:type_name -> provisioner.ChunkPiece + 8, // 67: provisioner.DataUpload.upload_type:type_name -> provisioner.DataUploadType + 56, // 68: provisioner.Provisioner.Session:input_type -> provisioner.Request + 57, // 69: provisioner.Provisioner.Session:output_type -> provisioner.Response + 69, // [69:70] is the sub-list for method output_type + 68, // [68:69] is the sub-list for method input_type + 68, // [68:68] is the sub-list for extension type_name + 68, // [68:68] is the sub-list for extension extendee + 0, // [0:68] is the sub-list for field type_name } func init() { file_provisionersdk_proto_provisioner_proto_init() } @@ -5747,7 +6082,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PlanRequest); i { + switch v := v.(*InitRequest); i { case 0: return &v.state case 1: @@ -5759,7 +6094,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PlanComplete); i { + switch v := v.(*InitComplete); i { case 0: return &v.state case 1: @@ -5771,7 +6106,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ApplyRequest); i { + switch v := v.(*PlanRequest); i { case 0: return &v.state case 1: @@ -5783,7 +6118,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ApplyComplete); i { + switch v := v.(*PlanComplete); i { case 0: return &v.state case 1: @@ -5795,7 +6130,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Timing); i { + switch v := v.(*ApplyRequest); i { case 0: return &v.state case 1: @@ -5807,7 +6142,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CancelRequest); i { + switch v := v.(*ApplyComplete); i { case 0: return &v.state case 1: @@ -5819,7 +6154,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Request); i { + switch v := v.(*GraphRequest); i { case 0: return &v.state case 1: @@ -5831,7 +6166,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Response); i { + switch v := v.(*GraphComplete); i { case 0: return &v.state case 1: @@ -5843,7 +6178,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DataUpload); i { + switch v := v.(*Timing); i { case 0: return &v.state case 1: @@ -5855,7 +6190,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChunkPiece); i { + switch v := v.(*CancelRequest); i { case 0: return &v.state case 1: @@ -5867,7 +6202,19 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Agent_Metadata); i { + switch v := v.(*Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_provisionersdk_proto_provisioner_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Response); i { case 0: return &v.state case 1: @@ -5879,6 +6226,42 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DataUpload); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_provisionersdk_proto_provisioner_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChunkPiece); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_provisionersdk_proto_provisioner_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Agent_Metadata); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_provisionersdk_proto_provisioner_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Resource_Metadata); i { case 0: return &v.state @@ -5898,18 +6281,22 @@ func file_provisionersdk_proto_provisioner_proto_init() { } file_provisionersdk_proto_provisioner_proto_msgTypes[32].OneofWrappers = []interface{}{} file_provisionersdk_proto_provisioner_proto_msgTypes[34].OneofWrappers = []interface{}{} - file_provisionersdk_proto_provisioner_proto_msgTypes[43].OneofWrappers = []interface{}{ + file_provisionersdk_proto_provisioner_proto_msgTypes[47].OneofWrappers = []interface{}{ (*Request_Config)(nil), (*Request_Parse)(nil), + (*Request_Init)(nil), (*Request_Plan)(nil), (*Request_Apply)(nil), + (*Request_Graph)(nil), (*Request_Cancel)(nil), } - file_provisionersdk_proto_provisioner_proto_msgTypes[44].OneofWrappers = []interface{}{ + file_provisionersdk_proto_provisioner_proto_msgTypes[48].OneofWrappers = []interface{}{ (*Response_Log)(nil), (*Response_Parse)(nil), + (*Response_Init)(nil), (*Response_Plan)(nil), (*Response_Apply)(nil), + (*Response_Graph)(nil), (*Response_DataUpload)(nil), (*Response_ChunkPiece)(nil), } @@ -5918,8 +6305,8 @@ func file_provisionersdk_proto_provisioner_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_provisionersdk_proto_provisioner_proto_rawDesc, - NumEnums: 8, - NumMessages: 51, + NumEnums: 9, + NumMessages: 55, NumExtensions: 0, NumServices: 1, }, diff --git a/provisionersdk/proto/provisioner.proto b/provisionersdk/proto/provisioner.proto index 89a69ce7022ca..eb8a81b61e834 100644 --- a/provisionersdk/proto/provisioner.proto +++ b/provisionersdk/proto/provisioner.proto @@ -369,16 +369,12 @@ message Metadata { // Config represents execution configuration shared by all subsequent requests in the Session message Config { - // template_source_archive is a tar of the template source files - bytes template_source_archive = 1; - // state is the provisioner state (if any) - bytes state = 2; - string provisioner_log_level = 3; + string provisioner_log_level = 1; // Template imports can omit template id - optional string template_id = 4; + optional string template_id = 2; // Dry runs omit version id - optional string template_version_id = 5; - optional bool exp_reuse_terraform_workspace = 6; // Whether to reuse existing terraform workspaces if they exist. + optional string template_version_id = 3; + optional bool exp_reuse_terraform_workspace = 4; // Whether to reuse existing terraform workspaces if they exist. } // ParseRequest consumes source-code to produce inputs. @@ -393,6 +389,25 @@ message ParseComplete { map workspace_tags = 4; } +message InitRequest { + // template_source_archive is a tar of the template source files + bytes template_source_archive = 1; + + // If true, the provisioner can safely assume the caller does not need the + // module files downloaded by the `terraform init` command. + // Ideally this boolean would be flipped in its truthy value, however since + // this is costly, the zero value omitting the module files is preferred. + bool omit_module_files = 3; +} + +message InitComplete { + string error = 1; + repeated Timing timings = 2; + repeated Module modules = 3; + bytes module_files = 4; + bytes module_files_hash = 5; +} + // PlanRequest asks the provisioner to plan what resources & parameters it will create message PlanRequest { Metadata metadata = 1; @@ -401,52 +416,61 @@ message PlanRequest { repeated ExternalAuthProvider external_auth_providers = 4; repeated RichParameterValue previous_parameter_values = 5; - // If true, the provisioner can safely assume the caller does not need the - // module files downloaded by the `terraform init` command. - // Ideally this boolean would be flipped in its truthy value, however for - // backwards compatibility reasons, the zero value should be the previous - // behavior of downloading the module files. - bool omit_module_files = 6; + // state is the provisioner state (if any) + bytes state = 6; } // PlanComplete indicates a request to plan completed. message PlanComplete { string error = 1; - repeated Resource resources = 2; - repeated RichParameter parameters = 3; - repeated ExternalAuthProviderResource external_auth_providers = 4; - repeated Timing timings = 6; - repeated Module modules = 7; - repeated Preset presets = 8; - bytes plan = 9; - repeated ResourceReplacement resource_replacements = 10; - bytes module_files = 11; - bytes module_files_hash = 12; - // Whether a template has any `coder_ai_task` resources defined, even if not planned for creation. - // During a template import, a plan is run which may not yield in any `coder_ai_task` resources, but nonetheless we - // still need to know that such resources are defined. - // - // See `hasAITaskResources` in provisioner/terraform/resources.go for more details. - bool has_ai_tasks = 13; - repeated provisioner.AITask ai_tasks = 14; - bool has_external_agents = 15; + repeated Timing timings = 2; + bytes plan = 3; + int32 dailyCost = 4; + repeated ResourceReplacement resource_replacements = 5; + int32 ai_task_count = 6; } // ApplyRequest asks the provisioner to apply the changes. Apply MUST be preceded by a successful plan request/response // in the same Session. The plan data is not transmitted over the wire and is cached by the provisioner in the Session. message ApplyRequest { Metadata metadata = 1; + // state is the provisioner state (if any) + bytes state = 6; } // ApplyComplete indicates a request to apply completed. message ApplyComplete { bytes state = 1; string error = 2; + repeated Timing timings = 3; +} + +enum GraphSource { + SOURCE_UNKNOWN = 0; + SOURCE_PLAN = 1; + SOURCE_STATE = 2; +} + +message GraphRequest { + Metadata metadata = 1; + GraphSource source = 2; +} + +message GraphComplete { + string error = 1; + repeated Timing timings = 2; repeated Resource resources = 3; repeated RichParameter parameters = 4; repeated ExternalAuthProviderResource external_auth_providers = 5; - repeated Timing timings = 6; - repeated provisioner.AITask ai_tasks = 7; + repeated Preset presets = 6; + // Whether a template has any `coder_ai_task` resources defined, even if not planned for creation. + // During a template import, a plan is run which may not yield in any `coder_ai_task` resources, but nonetheless we + // still need to know that such resources are defined. + // + // See `hasAITaskResources` in provisioner/terraform/resources.go for more details. + bool has_ai_tasks = 7; + repeated provisioner.AITask ai_tasks = 8; + bool has_external_agents = 9; } message Timing { @@ -472,9 +496,11 @@ message Request { oneof type { Config config = 1; ParseRequest parse = 2; - PlanRequest plan = 3; - ApplyRequest apply = 4; - CancelRequest cancel = 5; + InitRequest init = 3; + PlanRequest plan = 4; + ApplyRequest apply = 5; + GraphRequest graph = 6; + CancelRequest cancel = 7; } } @@ -482,10 +508,12 @@ message Response { oneof type { Log log = 1; ParseComplete parse = 2; - PlanComplete plan = 3; - ApplyComplete apply = 4; - DataUpload data_upload = 5; - ChunkPiece chunk_piece = 6; + InitComplete init = 3; + PlanComplete plan = 4; + ApplyComplete apply = 5; + GraphComplete graph = 6; + DataUpload data_upload = 7; + ChunkPiece chunk_piece = 8; } } diff --git a/provisionersdk/serve.go b/provisionersdk/serve.go index 3bac226e58379..2deb8a0815f83 100644 --- a/provisionersdk/serve.go +++ b/provisionersdk/serve.go @@ -35,9 +35,11 @@ type ServeOptions struct { } type Server interface { + Init(s *Session, r *proto.InitRequest, canceledOrComplete <-chan struct{}) *proto.InitComplete Parse(s *Session, r *proto.ParseRequest, canceledOrComplete <-chan struct{}) *proto.ParseComplete Plan(s *Session, r *proto.PlanRequest, canceledOrComplete <-chan struct{}) *proto.PlanComplete Apply(s *Session, r *proto.ApplyRequest, canceledOrComplete <-chan struct{}) *proto.ApplyComplete + Graph(s *Session, r *proto.GraphRequest, canceledOrComplete <-chan struct{}) *proto.GraphComplete } // Serve starts a dRPC connection for the provisioner and transport provided. diff --git a/provisionersdk/serve_test.go b/provisionersdk/serve_test.go index 4fc7342b1eed2..ff79c3c16b226 100644 --- a/provisionersdk/serve_test.go +++ b/provisionersdk/serve_test.go @@ -44,6 +44,11 @@ func TestProvisionerSDK(t *testing.T) { err = s.Send(&proto.Request{Type: &proto.Request_Config{Config: &proto.Config{}}}) require.NoError(t, err) + err = s.Send(&proto.Request{Type: &proto.Request_Init{Init: &proto.InitRequest{}}}) + require.NoError(t, err) + _, err = s.Recv() + require.NoError(t, err) + err = s.Send(&proto.Request{Type: &proto.Request_Parse{Parse: &proto.ParseRequest{}}}) require.NoError(t, err) msg, err := s.Recv() @@ -102,6 +107,11 @@ func TestProvisionerSDK(t *testing.T) { err = s.Send(&proto.Request{Type: &proto.Request_Config{Config: &proto.Config{}}}) require.NoError(t, err) + err = s.Send(&proto.Request{Type: &proto.Request_Init{Init: &proto.InitRequest{}}}) + require.NoError(t, err) + _, err = s.Recv() + require.NoError(t, err) + err = s.Send(&proto.Request{Type: &proto.Request_Parse{Parse: &proto.ParseRequest{}}}) require.NoError(t, err) msg, err := s.Recv() @@ -135,8 +145,18 @@ func TestProvisionerSDK(t *testing.T) { }) } +var _ provisionersdk.Server = unimplementedServer{} + type unimplementedServer struct{} +func (unimplementedServer) Init(s *provisionersdk.Session, r *proto.InitRequest, canceledOrComplete <-chan struct{}) *proto.InitComplete { + return &proto.InitComplete{} +} + +func (unimplementedServer) Graph(s *provisionersdk.Session, r *proto.GraphRequest, canceledOrComplete <-chan struct{}) *proto.GraphComplete { + return &proto.GraphComplete{Error: "unimplemented"} +} + func (unimplementedServer) Parse(_ *provisionersdk.Session, _ *proto.ParseRequest, _ <-chan struct{}) *proto.ParseComplete { return &proto.ParseComplete{Error: "unimplemented"} } diff --git a/provisionersdk/session.go b/provisionersdk/session.go index 59034a761e09d..2640a6dcf03d0 100644 --- a/provisionersdk/session.go +++ b/provisionersdk/session.go @@ -66,10 +66,6 @@ func (p *protoServer) Session(stream proto.DRPCProvisioner_SessionStream) error return xerrors.Errorf("unable to clean stale sessions %q: %w", s.Files, err) } - err = s.Files.ExtractArchive(s.Context(), s.Logger, afero.NewOsFs(), s.Config) - if err != nil { - return xerrors.Errorf("extract archive: %w", err) - } return s.handleRequests() } @@ -110,6 +106,10 @@ func (s *Session) handleRequests() error { } resp := &proto.Response{} if parse := req.GetParse(); parse != nil { + if !s.initialized { + // Files must be initialized before parsing. + return xerrors.New("cannot parse before successful init") + } r := &request[*proto.ParseRequest, *proto.ParseComplete]{ req: parse, session: s, @@ -129,48 +129,28 @@ func (s *Session) handleRequests() error { } resp.Type = &proto.Response_Parse{Parse: complete} } - if plan := req.GetPlan(); plan != nil { - r := &request[*proto.PlanRequest, *proto.PlanComplete]{ - req: plan, - session: s, - serverFn: s.server.Plan, - cancels: requests, + if init := req.GetInit(); init != nil { + if s.initialized { + return xerrors.New("cannot init more than once per session") } - complete, err := r.do() + initResp, err := s.handleInitRequest(init, requests) if err != nil { return err } - resp.Type = &proto.Response_Plan{Plan: complete} - - if protobuf.Size(resp) > drpcsdk.MaxMessageSize { - // It is likely the modules that is pushing the message size over the limit. - // Send the modules over a stream of messages instead. - s.Logger.Info(s.Context(), "plan response too large, sending modules as stream", - slog.F("size_bytes", len(complete.ModuleFiles)), - ) - dataUp, chunks := proto.BytesToDataUpload(proto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, complete.ModuleFiles) - - complete.ModuleFiles = nil // sent over the stream - complete.ModuleFilesHash = dataUp.DataHash - resp.Type = &proto.Response_Plan{Plan: complete} - - err := s.stream.Send(&proto.Response{Type: &proto.Response_DataUpload{DataUpload: dataUp}}) - if err != nil { - complete.Error = fmt.Sprintf("send data upload: %s", err.Error()) - } else { - for i, chunk := range chunks { - err := s.stream.Send(&proto.Response{Type: &proto.Response_ChunkPiece{ChunkPiece: chunk}}) - if err != nil { - complete.Error = fmt.Sprintf("send data piece upload %d/%d: %s", i, dataUp.Chunks, err.Error()) - break - } - } - } + resp.Type = &proto.Response_Init{Init: initResp} + } + if plan := req.GetPlan(); plan != nil { + if !s.initialized { + return xerrors.New("cannot plan before successful init") } - - if complete.Error == "" { + planResp, err := s.handlePlanRequest(plan, requests) + if err != nil { + return err + } + if planResp.Error == "" { planned = true } + resp.Type = &proto.Response_Plan{Plan: planResp} } if apply := req.GetApply(); apply != nil { if !planned { @@ -188,6 +168,23 @@ func (s *Session) handleRequests() error { } resp.Type = &proto.Response_Apply{Apply: complete} } + if graph := req.GetGraph(); graph != nil { + if !s.initialized { + return xerrors.New("cannot graph before successful init") + } + + r := &request[*proto.GraphRequest, *proto.GraphComplete]{ + req: graph, + session: s, + serverFn: s.server.Graph, + cancels: requests, + } + complete, err := r.do() + if err != nil { + return err + } + resp.Type = &proto.Response_Graph{Graph: complete} + } err := s.stream.Send(resp) if err != nil { return xerrors.Errorf("send response: %w", err) @@ -196,16 +193,82 @@ func (s *Session) handleRequests() error { return nil } +func (s *Session) handleInitRequest(init *proto.InitRequest, requests <-chan *proto.Request) (*proto.InitComplete, error) { + r := &request[*proto.InitRequest, *proto.InitComplete]{ + req: init, + session: s, + serverFn: s.server.Init, + cancels: requests, + } + complete, err := r.do() + if err != nil { + return nil, err + } + if complete.Error != "" { + return complete, nil + } + + // If the size of the complete message is too large, we need to stream the module files separately. + if protobuf.Size(&proto.Response{Type: &proto.Response_Init{Init: complete}}) > drpcsdk.MaxMessageSize { + // It is likely the modules that is pushing the message size over the limit. + // Send the modules over a stream of messages instead. + s.Logger.Info(s.Context(), "plan response too large, sending modules as stream", + slog.F("size_bytes", len(complete.ModuleFiles)), + ) + dataUp, chunks := proto.BytesToDataUpload(proto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, complete.ModuleFiles) + + complete.ModuleFiles = nil // sent over the stream + complete.ModuleFilesHash = dataUp.DataHash + + err := s.stream.Send(&proto.Response{Type: &proto.Response_DataUpload{DataUpload: dataUp}}) + if err != nil { + complete.Error = fmt.Sprintf("send data upload: %s", err.Error()) + } else { + for i, chunk := range chunks { + err := s.stream.Send(&proto.Response{Type: &proto.Response_ChunkPiece{ChunkPiece: chunk}}) + if err != nil { + complete.Error = fmt.Sprintf("send data piece upload %d/%d: %s", i, dataUp.Chunks, err.Error()) + break + } + } + } + } + s.initialized = true + + return complete, nil +} + +func (s *Session) handlePlanRequest(plan *proto.PlanRequest, requests <-chan *proto.Request) (*proto.PlanComplete, error) { + r := &request[*proto.PlanRequest, *proto.PlanComplete]{ + req: plan, + session: s, + serverFn: s.server.Plan, + cancels: requests, + } + complete, err := r.do() + if err != nil { + return nil, err + } + + return complete, nil +} + type Session struct { Logger slog.Logger Files tfpath.Layouter Config *proto.Config + // initialized indicates if an init was run. + // Required for plan/apply. + initialized bool + server Server stream proto.DRPCProvisioner_SessionStream logLevel int32 } +type initialized struct{} + func (s *Session) Context() context.Context { return s.stream.Context() } @@ -226,11 +289,11 @@ func (s *Session) ProvisionLog(level proto.LogLevel, output string) { } type pRequest interface { - *proto.ParseRequest | *proto.PlanRequest | *proto.ApplyRequest + *proto.ParseRequest | *proto.InitRequest | *proto.PlanRequest | *proto.ApplyRequest | *proto.GraphRequest } type pComplete interface { - *proto.ParseComplete | *proto.PlanComplete | *proto.ApplyComplete + *proto.ParseComplete | *proto.InitComplete | *proto.PlanComplete | *proto.ApplyComplete | *proto.GraphComplete } // request processes a single request call to the Server and returns its complete result, while also processing cancel diff --git a/provisionersdk/tfpath/tfpath.go b/provisionersdk/tfpath/tfpath.go index 019552e48d0de..c9db7ee9a4256 100644 --- a/provisionersdk/tfpath/tfpath.go +++ b/provisionersdk/tfpath/tfpath.go @@ -16,7 +16,6 @@ import ( "golang.org/x/xerrors" "cdr.dev/slog" - "github.com/coder/coder/v2/provisionersdk/proto" ) type Layouter interface { @@ -28,7 +27,7 @@ type Layouter interface { TerraformMetadataDir() string ModulesDirectory() string ModulesFilePath() string - ExtractArchive(ctx context.Context, logger slog.Logger, fs afero.Fs, cfg *proto.Config) error + ExtractArchive(ctx context.Context, logger slog.Logger, fs afero.Fs, templateSourceArchive []byte) error Cleanup(ctx context.Context, logger slog.Logger, fs afero.Fs) CleanStaleSessions(ctx context.Context, logger slog.Logger, fs afero.Fs, now time.Time) error } @@ -93,9 +92,9 @@ func (l Layout) ModulesFilePath() string { return filepath.Join(l.ModulesDirectory(), "modules.json") } -func (l Layout) ExtractArchive(ctx context.Context, logger slog.Logger, fs afero.Fs, cfg *proto.Config) error { +func (l Layout) ExtractArchive(ctx context.Context, logger slog.Logger, fs afero.Fs, templateSourceArchive []byte) error { logger.Info(ctx, "unpacking template source archive", - slog.F("size_bytes", len(cfg.TemplateSourceArchive)), + slog.F("size_bytes", len(templateSourceArchive)), ) err := fs.MkdirAll(l.WorkDirectory(), 0o700) @@ -103,11 +102,7 @@ func (l Layout) ExtractArchive(ctx context.Context, logger slog.Logger, fs afero return xerrors.Errorf("create work directory %q: %w", l.WorkDirectory(), err) } - // TODO: Pass in cfg.TemplateSourceArchive, not the full config. - // niling out the config field is a bit hacky. - reader := tar.NewReader(bytes.NewBuffer(cfg.TemplateSourceArchive)) - // for safety, nil out the reference on Config, since the reader now owns it. - cfg.TemplateSourceArchive = nil + reader := tar.NewReader(bytes.NewBuffer(templateSourceArchive)) for { header, err := reader.Next() if err != nil { diff --git a/provisionersdk/tfpath/x/tfpath.go b/provisionersdk/tfpath/x/tfpath.go index c6b9f5d669e94..50af1bbfe149c 100644 --- a/provisionersdk/tfpath/x/tfpath.go +++ b/provisionersdk/tfpath/x/tfpath.go @@ -140,9 +140,9 @@ func (td Layout) Cleanup(ctx context.Context, logger slog.Logger, fs afero.Fs) { slog.F("path", path), slog.Error(err)) } -func (td Layout) ExtractArchive(ctx context.Context, logger slog.Logger, fs afero.Fs, cfg *proto.Config) error { +func (td Layout) ExtractArchive(ctx context.Context, logger slog.Logger, fs afero.Fs, archive []byte) error { logger.Info(ctx, "unpacking template source archive", - slog.F("size_bytes", len(cfg.TemplateSourceArchive)), + slog.F("size_bytes", len(archive)), ) err := fs.MkdirAll(td.WorkDirectory(), 0o700) @@ -163,9 +163,7 @@ func (td Layout) ExtractArchive(ctx context.Context, logger slog.Logger, fs afer return xerrors.Errorf("select terraform workspace: %w", err) } - reader := tar.NewReader(bytes.NewBuffer(cfg.TemplateSourceArchive)) - // for safety, nil out the reference on Config, since the reader now owns it. - cfg.TemplateSourceArchive = nil + reader := tar.NewReader(bytes.NewBuffer(archive)) for { header, err := reader.Next() if err != nil { diff --git a/site/e2e/provisionerGenerated.ts b/site/e2e/provisionerGenerated.ts index ba9071ab625e8..8f6ac8cb165ed 100644 --- a/site/e2e/provisionerGenerated.ts +++ b/site/e2e/provisionerGenerated.ts @@ -1598,15 +1598,29 @@ export const ChunkPiece = { export interface Provisioner { /** * Session represents provisioning a single template import or workspace. The daemon always sends Config followed - * by one of the requests (ParseRequest, PlanRequest, ApplyRequest). The provisioner should respond with a stream - * of zero or more Logs, followed by the corresponding complete message (ParseComplete, PlanComplete, - * ApplyComplete). The daemon may then send a new request. A request to apply MUST be preceded by a request plan, - * and the provisioner should store the plan data on the Session after a successful plan, so that the daemon may - * request an apply. If the daemon closes the Session without an apply, the plan data may be safely discarded. + * by one of the requests (InitRequest, ParseRequest, PlanRequest, ApplyRequest, GraphRequest). The provisioner + * should respond with a stream of zero or more Logs, followed by the corresponding complete message + * (InitComplete, ParseComplete, PlanComplete, ApplyComplete, GraphComplete). + * The daemon may then send a new request. + * + * A request to Parse or Plan MUST be preceded by a request init. The provisioner should store the init data on + * the session after a successful init. If the daemon closes the session, the init data may be safely discarded. + * + * A request to apply MUST be preceded by a request plan, and the provisioner should store the plan data on the + * Session after a successful plan, so that the daemon may request an apply. If the daemon closes + * the Session without an apply, the plan data may be safely discarded. + * + * A request to graph MUST be preceded by a plan or an apply. + * + * The order of requests is then one of the following: + * 1. Init -> Parse + * 2. Init -> Plan -> Graph + * 3. Init -> Plan -> Apply -> Graph * - * The daemon may send a CancelRequest, asynchronously to ask the provisioner to cancel the previous ParseRequest, - * PlanRequest, or ApplyRequest. The provisioner MUST reply with a complete message corresponding to the request - * that was canceled. If the provisioner has already completed the request, it may ignore the CancelRequest. + * The daemon may send a CancelRequest, asynchronously to ask the provisioner to cancel the previous InitRequest, + * ParseRequest, PlanRequest, ApplyRequest, or GraphRequest. The provisioner MUST reply with a complete message + * corresponding to the request that was canceled. If the provisioner has already completed the request, + * it may ignore the CancelRequest. */ Session(request: Observable): Observable; } diff --git a/site/src/modules/workspaces/WorkspaceTiming/StagesChart.tsx b/site/src/modules/workspaces/WorkspaceTiming/StagesChart.tsx index 103d4717f20c6..b1c8c50befbde 100644 --- a/site/src/modules/workspaces/WorkspaceTiming/StagesChart.tsx +++ b/site/src/modules/workspaces/WorkspaceTiming/StagesChart.tsx @@ -246,31 +246,31 @@ export const provisioningStages: Stage[] = [ }, }, { - name: "graph", - label: "graph", + name: "apply", + label: "apply", section: "provisioning", tooltip: { title: ( <> - Terraform graph + Terraform apply - List all resources in plan, used to update coderd database. + Execute Terraform plan to create/modify/delete resources into + desired states. ), }, }, { - name: "apply", - label: "apply", + name: "graph", + label: "graph", section: "provisioning", tooltip: { title: ( <> - Terraform apply + Terraform graph - Execute Terraform plan to create/modify/delete resources into - desired states. + List all resources in plan, used to update coderd database. ),