diff --git a/agent/agent.go b/agent/agent.go
index 7525ecf051f69..492a915f4b4ab 100644
--- a/agent/agent.go
+++ b/agent/agent.go
@@ -363,9 +363,11 @@ func (a *agent) runLoop() {
if ctx.Err() != nil {
// Context canceled errors may come from websocket pings, so we
// don't want to use `errors.Is(err, context.Canceled)` here.
+ a.logger.Warn(ctx, "runLoop exited with error", slog.Error(ctx.Err()))
return
}
if a.isClosed() {
+ a.logger.Warn(ctx, "runLoop exited because agent is closed")
return
}
if errors.Is(err, io.EOF) {
@@ -1046,7 +1048,11 @@ func (a *agent) run() (retErr error) {
return a.statsReporter.reportLoop(ctx, aAPI)
})
- return connMan.wait()
+ err = connMan.wait()
+ if err != nil {
+ a.logger.Info(context.Background(), "connection manager errored", slog.Error(err))
+ }
+ return err
}
// handleManifest returns a function that fetches and processes the manifest
diff --git a/cli/agent.go b/cli/agent.go
index 5d6cdbd66b4e0..0153c5f05b44d 100644
--- a/cli/agent.go
+++ b/cli/agent.go
@@ -25,6 +25,8 @@ import (
"cdr.dev/slog/sloggers/sloghuman"
"cdr.dev/slog/sloggers/slogjson"
"cdr.dev/slog/sloggers/slogstackdriver"
+ "github.com/coder/serpent"
+
"github.com/coder/coder/v2/agent"
"github.com/coder/coder/v2/agent/agentexec"
"github.com/coder/coder/v2/agent/agentssh"
@@ -33,7 +35,6 @@ import (
"github.com/coder/coder/v2/cli/clilog"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/agentsdk"
- "github.com/coder/serpent"
)
func (r *RootCmd) workspaceAgent() *serpent.Command {
@@ -62,8 +63,10 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
// This command isn't useful to manually execute.
Hidden: true,
Handler: func(inv *serpent.Invocation) error {
- ctx, cancel := context.WithCancel(inv.Context())
- defer cancel()
+ ctx, cancel := context.WithCancelCause(inv.Context())
+ defer func() {
+ cancel(xerrors.New("agent exited"))
+ }()
var (
ignorePorts = map[int]string{}
@@ -280,7 +283,6 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
return xerrors.Errorf("add executable to $PATH: %w", err)
}
- prometheusRegistry := prometheus.NewRegistry()
subsystemsRaw := inv.Environ.Get(agent.EnvAgentSubsystem)
subsystems := []codersdk.AgentSubsystem{}
for _, s := range strings.Split(subsystemsRaw, ",") {
@@ -324,45 +326,70 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
logger.Info(ctx, "agent devcontainer detection not enabled")
}
- agnt := agent.New(agent.Options{
- Client: client,
- Logger: logger,
- LogDir: logDir,
- ScriptDataDir: scriptDataDir,
- // #nosec G115 - Safe conversion as tailnet listen port is within uint16 range (0-65535)
- TailnetListenPort: uint16(tailnetListenPort),
- ExchangeToken: func(ctx context.Context) (string, error) {
- if exchangeToken == nil {
- return client.SDK.SessionToken(), nil
- }
- resp, err := exchangeToken(ctx)
- if err != nil {
- return "", err
- }
- client.SetSessionToken(resp.SessionToken)
- return resp.SessionToken, nil
- },
- EnvironmentVariables: environmentVariables,
- IgnorePorts: ignorePorts,
- SSHMaxTimeout: sshMaxTimeout,
- Subsystems: subsystems,
-
- PrometheusRegistry: prometheusRegistry,
- BlockFileTransfer: blockFileTransfer,
- Execer: execer,
-
- ExperimentalDevcontainersEnabled: experimentalDevcontainersEnabled,
- })
-
- promHandler := agent.PrometheusMetricsHandler(prometheusRegistry, logger)
- prometheusSrvClose := ServeHandler(ctx, logger, promHandler, prometheusAddress, "prometheus")
- defer prometheusSrvClose()
-
- debugSrvClose := ServeHandler(ctx, logger, agnt.HTTPDebug(), debugAddress, "debug")
- defer debugSrvClose()
-
- <-ctx.Done()
- return agnt.Close()
+ reinitEvents := agentsdk.WaitForReinitLoop(ctx, logger, client)
+
+ var (
+ lastErr error
+ mustExit bool
+ )
+ for {
+ prometheusRegistry := prometheus.NewRegistry()
+
+ agnt := agent.New(agent.Options{
+ Client: client,
+ Logger: logger,
+ LogDir: logDir,
+ ScriptDataDir: scriptDataDir,
+ // #nosec G115 - Safe conversion as tailnet listen port is within uint16 range (0-65535)
+ TailnetListenPort: uint16(tailnetListenPort),
+ ExchangeToken: func(ctx context.Context) (string, error) {
+ if exchangeToken == nil {
+ return client.SDK.SessionToken(), nil
+ }
+ resp, err := exchangeToken(ctx)
+ if err != nil {
+ return "", err
+ }
+ client.SetSessionToken(resp.SessionToken)
+ return resp.SessionToken, nil
+ },
+ EnvironmentVariables: environmentVariables,
+ IgnorePorts: ignorePorts,
+ SSHMaxTimeout: sshMaxTimeout,
+ Subsystems: subsystems,
+
+ PrometheusRegistry: prometheusRegistry,
+ BlockFileTransfer: blockFileTransfer,
+ Execer: execer,
+
+ ExperimentalDevcontainersEnabled: experimentalDevcontainersEnabled,
+ })
+
+ promHandler := agent.PrometheusMetricsHandler(prometheusRegistry, logger)
+ prometheusSrvClose := ServeHandler(ctx, logger, promHandler, prometheusAddress, "prometheus")
+
+ debugSrvClose := ServeHandler(ctx, logger, agnt.HTTPDebug(), debugAddress, "debug")
+
+ select {
+ case <-ctx.Done():
+ logger.Info(ctx, "agent shutting down", slog.Error(context.Cause(ctx)))
+ mustExit = true
+ case event := <-reinitEvents:
+ logger.Info(ctx, "agent received instruction to reinitialize",
+ slog.F("workspace_id", event.WorkspaceID), slog.F("reason", event.Reason))
+ }
+
+ lastErr = agnt.Close()
+ debugSrvClose()
+ prometheusSrvClose()
+
+ if mustExit {
+ break
+ }
+
+ logger.Info(ctx, "agent reinitializing")
+ }
+ return lastErr
},
}
diff --git a/cli/server.go b/cli/server.go
index 580dae369446c..4d3ef4d8b7f36 100644
--- a/cli/server.go
+++ b/cli/server.go
@@ -910,6 +910,37 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
options.StatsBatcher = batcher
defer closeBatcher()
+ // Manage notifications.
+ var (
+ notificationsCfg = options.DeploymentValues.Notifications
+ notificationsManager *notifications.Manager
+ )
+
+ metrics := notifications.NewMetrics(options.PrometheusRegistry)
+ helpers := templateHelpers(options)
+
+ // The enqueuer is responsible for enqueueing notifications to the given store.
+ enqueuer, err := notifications.NewStoreEnqueuer(notificationsCfg, options.Database, helpers, logger.Named("notifications.enqueuer"), quartz.NewReal())
+ if err != nil {
+ return xerrors.Errorf("failed to instantiate notification store enqueuer: %w", err)
+ }
+ options.NotificationsEnqueuer = enqueuer
+
+ // The notification manager is responsible for:
+ // - creating notifiers and managing their lifecycles (notifiers are responsible for dequeueing/sending notifications)
+ // - keeping the store updated with status updates
+ notificationsManager, err = notifications.NewManager(notificationsCfg, options.Database, options.Pubsub, helpers, metrics, logger.Named("notifications.manager"))
+ if err != nil {
+ return xerrors.Errorf("failed to instantiate notification manager: %w", err)
+ }
+
+ // nolint:gocritic // We need to run the manager in a notifier context.
+ notificationsManager.Run(dbauthz.AsNotifier(ctx))
+
+ // Run report generator to distribute periodic reports.
+ notificationReportGenerator := reports.NewReportGenerator(ctx, logger.Named("notifications.report_generator"), options.Database, options.NotificationsEnqueuer, quartz.NewReal())
+ defer notificationReportGenerator.Close()
+
// We use a separate coderAPICloser so the Enterprise API
// can have its own close functions. This is cleaner
// than abstracting the Coder API itself.
@@ -957,37 +988,6 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
return xerrors.Errorf("write config url: %w", err)
}
- // Manage notifications.
- var (
- notificationsCfg = options.DeploymentValues.Notifications
- notificationsManager *notifications.Manager
- )
-
- metrics := notifications.NewMetrics(options.PrometheusRegistry)
- helpers := templateHelpers(options)
-
- // The enqueuer is responsible for enqueueing notifications to the given store.
- enqueuer, err := notifications.NewStoreEnqueuer(notificationsCfg, options.Database, helpers, logger.Named("notifications.enqueuer"), quartz.NewReal())
- if err != nil {
- return xerrors.Errorf("failed to instantiate notification store enqueuer: %w", err)
- }
- options.NotificationsEnqueuer = enqueuer
-
- // The notification manager is responsible for:
- // - creating notifiers and managing their lifecycles (notifiers are responsible for dequeueing/sending notifications)
- // - keeping the store updated with status updates
- notificationsManager, err = notifications.NewManager(notificationsCfg, options.Database, options.Pubsub, helpers, metrics, logger.Named("notifications.manager"))
- if err != nil {
- return xerrors.Errorf("failed to instantiate notification manager: %w", err)
- }
-
- // nolint:gocritic // We need to run the manager in a notifier context.
- notificationsManager.Run(dbauthz.AsNotifier(ctx))
-
- // Run report generator to distribute periodic reports.
- notificationReportGenerator := reports.NewReportGenerator(ctx, logger.Named("notifications.report_generator"), options.Database, options.NotificationsEnqueuer, quartz.NewReal())
- defer notificationReportGenerator.Close()
-
// Since errCh only has one buffered slot, all routines
// sending on it must be wrapped in a select/default to
// avoid leaving dangling goroutines waiting for the
diff --git a/cli/testdata/coder_provisioner_list_--output_json.golden b/cli/testdata/coder_provisioner_list_--output_json.golden
index f619dce028cde..3daeb89febcb4 100644
--- a/cli/testdata/coder_provisioner_list_--output_json.golden
+++ b/cli/testdata/coder_provisioner_list_--output_json.golden
@@ -7,7 +7,7 @@
"last_seen_at": "====[timestamp]=====",
"name": "test",
"version": "v0.0.0-devel",
- "api_version": "1.4",
+ "api_version": "1.5",
"provisioners": [
"echo"
],
diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go
index daef10a90d422..60340de45b8b5 100644
--- a/coderd/apidoc/docs.go
+++ b/coderd/apidoc/docs.go
@@ -8252,6 +8252,31 @@ const docTemplate = `{
}
}
},
+ "/workspaceagents/me/reinit": {
+ "get": {
+ "security": [
+ {
+ "CoderSessionToken": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Agents"
+ ],
+ "summary": "Get workspace agent reinitialization",
+ "operationId": "get-workspace-agent-reinitialization",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/agentsdk.ReinitializationEvent"
+ }
+ }
+ }
+ }
+ },
"/workspaceagents/me/rpc": {
"get": {
"security": [
@@ -10297,6 +10322,26 @@ const docTemplate = `{
}
}
},
+ "agentsdk.ReinitializationEvent": {
+ "type": "object",
+ "properties": {
+ "reason": {
+ "$ref": "#/definitions/agentsdk.ReinitializationReason"
+ },
+ "workspaceID": {
+ "type": "string"
+ }
+ }
+ },
+ "agentsdk.ReinitializationReason": {
+ "type": "string",
+ "enum": [
+ "prebuild_claimed"
+ ],
+ "x-enum-varnames": [
+ "ReinitializeReasonPrebuildClaimed"
+ ]
+ },
"coderd.SCIMUser": {
"type": "object",
"properties": {
diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json
index 3a7bc4c2c71ed..93454fede9fe1 100644
--- a/coderd/apidoc/swagger.json
+++ b/coderd/apidoc/swagger.json
@@ -7295,6 +7295,27 @@
}
}
},
+ "/workspaceagents/me/reinit": {
+ "get": {
+ "security": [
+ {
+ "CoderSessionToken": []
+ }
+ ],
+ "produces": ["application/json"],
+ "tags": ["Agents"],
+ "summary": "Get workspace agent reinitialization",
+ "operationId": "get-workspace-agent-reinitialization",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/agentsdk.ReinitializationEvent"
+ }
+ }
+ }
+ }
+ },
"/workspaceagents/me/rpc": {
"get": {
"security": [
@@ -9134,6 +9155,22 @@
}
}
},
+ "agentsdk.ReinitializationEvent": {
+ "type": "object",
+ "properties": {
+ "reason": {
+ "$ref": "#/definitions/agentsdk.ReinitializationReason"
+ },
+ "workspaceID": {
+ "type": "string"
+ }
+ }
+ },
+ "agentsdk.ReinitializationReason": {
+ "type": "string",
+ "enum": ["prebuild_claimed"],
+ "x-enum-varnames": ["ReinitializeReasonPrebuildClaimed"]
+ },
"coderd.SCIMUser": {
"type": "object",
"properties": {
diff --git a/coderd/coderd.go b/coderd/coderd.go
index 288671c6cb6e9..d1a49747920ca 100644
--- a/coderd/coderd.go
+++ b/coderd/coderd.go
@@ -19,6 +19,8 @@ import (
"sync/atomic"
"time"
+ "github.com/coder/coder/v2/coderd/prebuilds"
+
"github.com/andybalholm/brotli"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
@@ -45,7 +47,6 @@ import (
"github.com/coder/coder/v2/coderd/entitlements"
"github.com/coder/coder/v2/coderd/files"
"github.com/coder/coder/v2/coderd/idpsync"
- "github.com/coder/coder/v2/coderd/prebuilds"
"github.com/coder/coder/v2/coderd/runtimeconfig"
"github.com/coder/coder/v2/coderd/webpush"
@@ -1278,6 +1279,7 @@ func New(options *Options) *API {
r.Get("/external-auth", api.workspaceAgentsExternalAuth)
r.Get("/gitsshkey", api.agentGitSSHKey)
r.Post("/log-source", api.workspaceAgentPostLogSource)
+ r.Get("/reinit", api.workspaceAgentReinit)
})
r.Route("/{workspaceagent}", func(r chi.Router) {
r.Use(
@@ -1772,6 +1774,7 @@ func (api *API) CreateInMemoryTaggedProvisionerDaemon(dialCtx context.Context, n
Clock: api.Clock,
},
api.NotificationsEnqueuer,
+ &api.PrebuildsReconciler,
)
if err != nil {
return nil, err
diff --git a/coderd/coderdtest/coderdtest.go b/coderd/coderdtest/coderdtest.go
index dbf1f62abfb28..93e17048e054e 100644
--- a/coderd/coderdtest/coderdtest.go
+++ b/coderd/coderdtest/coderdtest.go
@@ -1105,6 +1105,69 @@ func (w WorkspaceAgentWaiter) MatchResources(m func([]codersdk.WorkspaceResource
return w
}
+// WaitForAgentFn represents a boolean assertion to be made against each agent
+// that a given WorkspaceAgentWaited knows about. Each WaitForAgentFn should apply
+// the check to a single agent, but it should be named for plural, because `func (w WorkspaceAgentWaiter) WaitFor`
+// applies the check to all agents that it is aware of. This ensures that the public API of the waiter
+// reads correctly. For example:
+//
+// waiter := coderdtest.NewWorkspaceAgentWaiter(t, client, r.Workspace.ID)
+// waiter.WaitFor(coderdtest.AgentsReady)
+type WaitForAgentFn func(agent codersdk.WorkspaceAgent) bool
+
+// AgentsReady checks that the latest lifecycle state of an agent is "Ready".
+func AgentsReady(agent codersdk.WorkspaceAgent) bool {
+ return agent.LifecycleState == codersdk.WorkspaceAgentLifecycleReady
+}
+
+// AgentsNotReady checks that the latest lifecycle state of an agent is anything except "Ready".
+func AgentsNotReady(agent codersdk.WorkspaceAgent) bool {
+ return !AgentsReady(agent)
+}
+
+func (w WorkspaceAgentWaiter) WaitFor(criteria ...WaitForAgentFn) {
+ w.t.Helper()
+
+ agentNamesMap := make(map[string]struct{}, len(w.agentNames))
+ for _, name := range w.agentNames {
+ agentNamesMap[name] = struct{}{}
+ }
+
+ ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
+ defer cancel()
+
+ w.t.Logf("waiting for workspace agents (workspace %s)", w.workspaceID)
+ require.Eventually(w.t, func() bool {
+ var err error
+ workspace, err := w.client.Workspace(ctx, w.workspaceID)
+ if err != nil {
+ return false
+ }
+ if workspace.LatestBuild.Job.CompletedAt == nil {
+ return false
+ }
+ if workspace.LatestBuild.Job.CompletedAt.IsZero() {
+ return false
+ }
+
+ for _, resource := range workspace.LatestBuild.Resources {
+ for _, agent := range resource.Agents {
+ if len(w.agentNames) > 0 {
+ if _, ok := agentNamesMap[agent.Name]; !ok {
+ continue
+ }
+ }
+ for _, criterium := range criteria {
+ if !criterium(agent) {
+ return false
+ }
+ }
+ }
+ }
+ return true
+ }, testutil.WaitLong, testutil.IntervalMedium)
+}
+
// Wait waits for the agent(s) to connect and fails the test if they do not within testutil.WaitLong
func (w WorkspaceAgentWaiter) Wait() []codersdk.WorkspaceResource {
w.t.Helper()
diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go
index ceb5ba7f2a15a..7194c9267f638 100644
--- a/coderd/database/dbauthz/dbauthz.go
+++ b/coderd/database/dbauthz/dbauthz.go
@@ -3001,6 +3001,15 @@ func (q *querier) GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids []uui
return q.db.GetWorkspaceAgentsByResourceIDs(ctx, ids)
}
+func (q *querier) GetWorkspaceAgentsByWorkspaceAndBuildNumber(ctx context.Context, arg database.GetWorkspaceAgentsByWorkspaceAndBuildNumberParams) ([]database.WorkspaceAgent, error) {
+ _, err := q.GetWorkspaceByID(ctx, arg.WorkspaceID)
+ if err != nil {
+ return nil, err
+ }
+
+ return q.db.GetWorkspaceAgentsByWorkspaceAndBuildNumber(ctx, arg)
+}
+
func (q *querier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, createdAt time.Time) ([]database.WorkspaceAgent, error) {
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err != nil {
return nil, err
diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go
index e562bbd1f7160..3f6726110f025 100644
--- a/coderd/database/dbauthz/dbauthz_test.go
+++ b/coderd/database/dbauthz/dbauthz_test.go
@@ -2009,6 +2009,38 @@ func (s *MethodTestSuite) TestWorkspace() {
agt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: res.ID})
check.Args(agt.ID).Asserts(w, policy.ActionRead).Returns(agt)
}))
+ s.Run("GetWorkspaceAgentsByWorkspaceAndBuildNumber", s.Subtest(func(db database.Store, check *expects) {
+ u := dbgen.User(s.T(), db, database.User{})
+ o := dbgen.Organization(s.T(), db, database.Organization{})
+ tpl := dbgen.Template(s.T(), db, database.Template{
+ OrganizationID: o.ID,
+ CreatedBy: u.ID,
+ })
+ tv := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{
+ TemplateID: uuid.NullUUID{UUID: tpl.ID, Valid: true},
+ OrganizationID: o.ID,
+ CreatedBy: u.ID,
+ })
+ w := dbgen.Workspace(s.T(), db, database.WorkspaceTable{
+ TemplateID: tpl.ID,
+ OrganizationID: o.ID,
+ OwnerID: u.ID,
+ })
+ j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{
+ Type: database.ProvisionerJobTypeWorkspaceBuild,
+ })
+ b := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{
+ JobID: j.ID,
+ WorkspaceID: w.ID,
+ TemplateVersionID: tv.ID,
+ })
+ res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: b.JobID})
+ agt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: res.ID})
+ check.Args(database.GetWorkspaceAgentsByWorkspaceAndBuildNumberParams{
+ WorkspaceID: w.ID,
+ BuildNumber: 1,
+ }).Asserts(w, policy.ActionRead).Returns([]database.WorkspaceAgent{agt})
+ }))
s.Run("GetWorkspaceAgentLifecycleStateByID", s.Subtest(func(db database.Store, check *expects) {
u := dbgen.User(s.T(), db, database.User{})
o := dbgen.Organization(s.T(), db, database.Organization{})
diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go
index abadd78f07b36..fb2ea4bfd56b1 100644
--- a/coderd/database/dbfake/dbfake.go
+++ b/coderd/database/dbfake/dbfake.go
@@ -294,6 +294,8 @@ type TemplateVersionBuilder struct {
ps pubsub.Pubsub
resources []*sdkproto.Resource
params []database.TemplateVersionParameter
+ presets []database.TemplateVersionPreset
+ presetParams []database.TemplateVersionPresetParameter
promote bool
autoCreateTemplate bool
}
@@ -339,6 +341,13 @@ func (t TemplateVersionBuilder) Params(ps ...database.TemplateVersionParameter)
return t
}
+func (t TemplateVersionBuilder) Preset(preset database.TemplateVersionPreset, params ...database.TemplateVersionPresetParameter) TemplateVersionBuilder {
+ // nolint: revive // returns modified struct
+ t.presets = append(t.presets, preset)
+ t.presetParams = append(t.presetParams, params...)
+ return t
+}
+
func (t TemplateVersionBuilder) SkipCreateTemplate() TemplateVersionBuilder {
// nolint: revive // returns modified struct
t.autoCreateTemplate = false
@@ -378,6 +387,25 @@ func (t TemplateVersionBuilder) Do() TemplateVersionResponse {
require.NoError(t.t, err)
}
+ for _, preset := range t.presets {
+ dbgen.Preset(t.t, t.db, database.InsertPresetParams{
+ ID: preset.ID,
+ TemplateVersionID: version.ID,
+ Name: preset.Name,
+ CreatedAt: version.CreatedAt,
+ DesiredInstances: preset.DesiredInstances,
+ InvalidateAfterSecs: preset.InvalidateAfterSecs,
+ })
+ }
+
+ for _, presetParam := range t.presetParams {
+ dbgen.PresetParameter(t.t, t.db, database.InsertPresetParametersParams{
+ TemplateVersionPresetID: presetParam.TemplateVersionPresetID,
+ Names: []string{presetParam.Name},
+ Values: []string{presetParam.Value},
+ })
+ }
+
payload, err := json.Marshal(provisionerdserver.TemplateVersionImportJob{
TemplateVersionID: t.seed.ID,
})
diff --git a/coderd/database/dbgen/dbgen.go b/coderd/database/dbgen/dbgen.go
index 854c7c2974fe6..193c107d51da9 100644
--- a/coderd/database/dbgen/dbgen.go
+++ b/coderd/database/dbgen/dbgen.go
@@ -1198,6 +1198,7 @@ func TelemetryItem(t testing.TB, db database.Store, seed database.TelemetryItem)
func Preset(t testing.TB, db database.Store, seed database.InsertPresetParams) database.TemplateVersionPreset {
preset, err := db.InsertPreset(genCtx, database.InsertPresetParams{
+ ID: takeFirst(seed.ID, uuid.New()),
TemplateVersionID: takeFirst(seed.TemplateVersionID, uuid.New()),
Name: takeFirst(seed.Name, testutil.GetRandomName(t)),
CreatedAt: takeFirst(seed.CreatedAt, dbtime.Now()),
diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go
index 1359d2e63484d..e2d9bf5613524 100644
--- a/coderd/database/dbmem/dbmem.go
+++ b/coderd/database/dbmem/dbmem.go
@@ -7592,6 +7592,30 @@ func (q *FakeQuerier) GetWorkspaceAgentsByResourceIDs(ctx context.Context, resou
return q.getWorkspaceAgentsByResourceIDsNoLock(ctx, resourceIDs)
}
+func (q *FakeQuerier) GetWorkspaceAgentsByWorkspaceAndBuildNumber(ctx context.Context, arg database.GetWorkspaceAgentsByWorkspaceAndBuildNumberParams) ([]database.WorkspaceAgent, error) {
+ err := validateDatabaseType(arg)
+ if err != nil {
+ return nil, err
+ }
+
+ build, err := q.GetWorkspaceBuildByWorkspaceIDAndBuildNumber(ctx, database.GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams(arg))
+ if err != nil {
+ return nil, err
+ }
+
+ resources, err := q.getWorkspaceResourcesByJobIDNoLock(ctx, build.JobID)
+ if err != nil {
+ return nil, err
+ }
+
+ var resourceIDs []uuid.UUID
+ for _, resource := range resources {
+ resourceIDs = append(resourceIDs, resource.ID)
+ }
+
+ return q.GetWorkspaceAgentsByResourceIDs(ctx, resourceIDs)
+}
+
func (q *FakeQuerier) GetWorkspaceAgentsCreatedAfter(_ context.Context, after time.Time) ([]database.WorkspaceAgent, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
diff --git a/coderd/database/dbmetrics/querymetrics.go b/coderd/database/dbmetrics/querymetrics.go
index b76d70c764cf6..5433570e475fb 100644
--- a/coderd/database/dbmetrics/querymetrics.go
+++ b/coderd/database/dbmetrics/querymetrics.go
@@ -1726,6 +1726,13 @@ func (m queryMetricsStore) GetWorkspaceAgentsByResourceIDs(ctx context.Context,
return agents, err
}
+func (m queryMetricsStore) GetWorkspaceAgentsByWorkspaceAndBuildNumber(ctx context.Context, arg database.GetWorkspaceAgentsByWorkspaceAndBuildNumberParams) ([]database.WorkspaceAgent, error) {
+ start := time.Now()
+ r0, r1 := m.s.GetWorkspaceAgentsByWorkspaceAndBuildNumber(ctx, arg)
+ m.queryLatencies.WithLabelValues("GetWorkspaceAgentsByWorkspaceAndBuildNumber").Observe(time.Since(start).Seconds())
+ return r0, r1
+}
+
func (m queryMetricsStore) GetWorkspaceAgentsCreatedAfter(ctx context.Context, createdAt time.Time) ([]database.WorkspaceAgent, error) {
start := time.Now()
agents, err := m.s.GetWorkspaceAgentsCreatedAfter(ctx, createdAt)
diff --git a/coderd/database/dbmock/dbmock.go b/coderd/database/dbmock/dbmock.go
index 10adfd7c5a408..7937a105791eb 100644
--- a/coderd/database/dbmock/dbmock.go
+++ b/coderd/database/dbmock/dbmock.go
@@ -3619,6 +3619,21 @@ func (mr *MockStoreMockRecorder) GetWorkspaceAgentsByResourceIDs(ctx, ids any) *
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspaceAgentsByResourceIDs", reflect.TypeOf((*MockStore)(nil).GetWorkspaceAgentsByResourceIDs), ctx, ids)
}
+// GetWorkspaceAgentsByWorkspaceAndBuildNumber mocks base method.
+func (m *MockStore) GetWorkspaceAgentsByWorkspaceAndBuildNumber(ctx context.Context, arg database.GetWorkspaceAgentsByWorkspaceAndBuildNumberParams) ([]database.WorkspaceAgent, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetWorkspaceAgentsByWorkspaceAndBuildNumber", ctx, arg)
+ ret0, _ := ret[0].([]database.WorkspaceAgent)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetWorkspaceAgentsByWorkspaceAndBuildNumber indicates an expected call of GetWorkspaceAgentsByWorkspaceAndBuildNumber.
+func (mr *MockStoreMockRecorder) GetWorkspaceAgentsByWorkspaceAndBuildNumber(ctx, arg any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspaceAgentsByWorkspaceAndBuildNumber", reflect.TypeOf((*MockStore)(nil).GetWorkspaceAgentsByWorkspaceAndBuildNumber), ctx, arg)
+}
+
// GetWorkspaceAgentsCreatedAfter mocks base method.
func (m *MockStore) GetWorkspaceAgentsCreatedAfter(ctx context.Context, createdAt time.Time) ([]database.WorkspaceAgent, error) {
m.ctrl.T.Helper()
diff --git a/coderd/database/migrations/000324_resource_replacements_notification.down.sql b/coderd/database/migrations/000324_resource_replacements_notification.down.sql
new file mode 100644
index 0000000000000..8da13f718b635
--- /dev/null
+++ b/coderd/database/migrations/000324_resource_replacements_notification.down.sql
@@ -0,0 +1 @@
+DELETE FROM notification_templates WHERE id = '89d9745a-816e-4695-a17f-3d0a229e2b8d';
diff --git a/coderd/database/migrations/000324_resource_replacements_notification.up.sql b/coderd/database/migrations/000324_resource_replacements_notification.up.sql
new file mode 100644
index 0000000000000..395332adaee20
--- /dev/null
+++ b/coderd/database/migrations/000324_resource_replacements_notification.up.sql
@@ -0,0 +1,34 @@
+INSERT INTO notification_templates
+ (id, name, title_template, body_template, "group", actions)
+VALUES ('89d9745a-816e-4695-a17f-3d0a229e2b8d',
+ 'Prebuilt Workspace Resource Replaced',
+ E'There might be a problem with a recently claimed prebuilt workspace',
+ $$
+Workspace **{{.Labels.workspace}}** was claimed from a prebuilt workspace by **{{.Labels.claimant}}**.
+
+During the claim, Terraform destroyed and recreated the following resources
+because one or more immutable attributes changed:
+
+{{range $resource, $paths := .Data.replacements -}}
+- _{{ $resource }}_ was replaced due to changes to _{{ $paths }}_
+{{end}}
+
+When Terraform must change an immutable attribute, it replaces the entire resource.
+If you’re using prebuilds to speed up provisioning, unexpected replacements will slow down
+workspace startup—even when claiming a prebuilt environment.
+
+For tips on preventing replacements and improving claim performance, see [this guide](https://coder.com/docs/admin/templates/extending-templates/prebuilt-workspaces#preventing-resource-replacement).
+
+NOTE: this prebuilt workspace used the **{{.Labels.preset}}** preset.
+$$,
+ 'Template Events',
+ '[
+ {
+ "label": "View workspace build",
+ "url": "{{base_url}}/@{{.Labels.claimant}}/{{.Labels.workspace}}/builds/{{.Labels.workspace_build_num}}"
+ },
+ {
+ "label": "View template version",
+ "url": "{{base_url}}/templates/{{.Labels.org}}/{{.Labels.template}}/versions/{{.Labels.template_version}}"
+ }
+ ]'::jsonb);
diff --git a/coderd/database/querier.go b/coderd/database/querier.go
index 9fbfbde410d40..fad83fb1668c1 100644
--- a/coderd/database/querier.go
+++ b/coderd/database/querier.go
@@ -396,6 +396,7 @@ type sqlcQuerier interface {
GetWorkspaceAgentUsageStats(ctx context.Context, createdAt time.Time) ([]GetWorkspaceAgentUsageStatsRow, error)
GetWorkspaceAgentUsageStatsAndLabels(ctx context.Context, createdAt time.Time) ([]GetWorkspaceAgentUsageStatsAndLabelsRow, error)
GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgent, error)
+ GetWorkspaceAgentsByWorkspaceAndBuildNumber(ctx context.Context, arg GetWorkspaceAgentsByWorkspaceAndBuildNumberParams) ([]WorkspaceAgent, error)
GetWorkspaceAgentsCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceAgent, error)
GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) ([]WorkspaceAgent, error)
GetWorkspaceAppByAgentIDAndSlug(ctx context.Context, arg GetWorkspaceAppByAgentIDAndSlugParams) (WorkspaceApp, error)
diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go
index 60416b1a35730..66535a9d5b5a9 100644
--- a/coderd/database/queries.sql.go
+++ b/coderd/database/queries.sql.go
@@ -5914,6 +5914,7 @@ WHERE w.id IN (
AND b.template_version_id = t.active_version_id
AND p.current_preset_id = $3::uuid
AND p.ready
+ AND NOT t.deleted
LIMIT 1 FOR UPDATE OF p SKIP LOCKED -- Ensure that a concurrent request will not select the same prebuild.
)
RETURNING w.id, w.name
@@ -5949,6 +5950,7 @@ FROM workspace_latest_builds wlb
-- prebuilds that are still building.
INNER JOIN templates t ON t.active_version_id = wlb.template_version_id
WHERE wlb.job_status IN ('pending'::provisioner_job_status, 'running'::provisioner_job_status)
+ -- AND NOT t.deleted -- We don't exclude deleted templates because there's no constraint in the DB preventing a soft deletion on a template while workspaces are running.
GROUP BY t.id, wpb.template_version_id, wpb.transition, wlb.template_version_preset_id
`
@@ -6063,6 +6065,7 @@ WITH filtered_builds AS (
WHERE tvp.desired_instances IS NOT NULL -- Consider only presets that have a prebuild configuration.
AND wlb.transition = 'start'::workspace_transition
AND w.owner_id = 'c42fdf75-3097-471c-8c33-fb52454d81c0'
+ AND NOT t.deleted
),
time_sorted_builds AS (
-- Group builds by preset, then sort each group by created_at.
@@ -6214,6 +6217,7 @@ FROM templates t
INNER JOIN template_version_presets tvp ON tvp.template_version_id = tv.id
INNER JOIN organizations o ON o.id = t.organization_id
WHERE tvp.desired_instances IS NOT NULL -- Consider only presets that have a prebuild configuration.
+ -- AND NOT t.deleted -- We don't exclude deleted templates because there's no constraint in the DB preventing a soft deletion on a template while workspaces are running.
AND (t.id = $1::uuid OR $1 IS NULL)
`
@@ -6443,6 +6447,7 @@ func (q *sqlQuerier) GetPresetsByTemplateVersionID(ctx context.Context, template
const insertPreset = `-- name: InsertPreset :one
INSERT INTO template_version_presets (
+ id,
template_version_id,
name,
created_at,
@@ -6454,11 +6459,13 @@ VALUES (
$2,
$3,
$4,
- $5
+ $5,
+ $6
) RETURNING id, template_version_id, name, created_at, desired_instances, invalidate_after_secs
`
type InsertPresetParams struct {
+ ID uuid.UUID `db:"id" json:"id"`
TemplateVersionID uuid.UUID `db:"template_version_id" json:"template_version_id"`
Name string `db:"name" json:"name"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
@@ -6468,6 +6475,7 @@ type InsertPresetParams struct {
func (q *sqlQuerier) InsertPreset(ctx context.Context, arg InsertPresetParams) (TemplateVersionPreset, error) {
row := q.db.QueryRowContext(ctx, insertPreset,
+ arg.ID,
arg.TemplateVersionID,
arg.Name,
arg.CreatedAt,
@@ -14164,6 +14172,80 @@ func (q *sqlQuerier) GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids []
return items, nil
}
+const getWorkspaceAgentsByWorkspaceAndBuildNumber = `-- name: GetWorkspaceAgentsByWorkspaceAndBuildNumber :many
+SELECT
+ workspace_agents.id, workspace_agents.created_at, workspace_agents.updated_at, workspace_agents.name, workspace_agents.first_connected_at, workspace_agents.last_connected_at, workspace_agents.disconnected_at, workspace_agents.resource_id, workspace_agents.auth_token, workspace_agents.auth_instance_id, workspace_agents.architecture, workspace_agents.environment_variables, workspace_agents.operating_system, workspace_agents.instance_metadata, workspace_agents.resource_metadata, workspace_agents.directory, workspace_agents.version, workspace_agents.last_connected_replica_id, workspace_agents.connection_timeout_seconds, workspace_agents.troubleshooting_url, workspace_agents.motd_file, workspace_agents.lifecycle_state, workspace_agents.expanded_directory, workspace_agents.logs_length, workspace_agents.logs_overflowed, workspace_agents.started_at, workspace_agents.ready_at, workspace_agents.subsystems, workspace_agents.display_apps, workspace_agents.api_version, workspace_agents.display_order
+FROM
+ workspace_agents
+JOIN
+ workspace_resources ON workspace_agents.resource_id = workspace_resources.id
+JOIN
+ workspace_builds ON workspace_resources.job_id = workspace_builds.job_id
+WHERE
+ workspace_builds.workspace_id = $1 :: uuid AND
+ workspace_builds.build_number = $2 :: int
+`
+
+type GetWorkspaceAgentsByWorkspaceAndBuildNumberParams struct {
+ WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
+ BuildNumber int32 `db:"build_number" json:"build_number"`
+}
+
+func (q *sqlQuerier) GetWorkspaceAgentsByWorkspaceAndBuildNumber(ctx context.Context, arg GetWorkspaceAgentsByWorkspaceAndBuildNumberParams) ([]WorkspaceAgent, error) {
+ rows, err := q.db.QueryContext(ctx, getWorkspaceAgentsByWorkspaceAndBuildNumber, arg.WorkspaceID, arg.BuildNumber)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []WorkspaceAgent
+ for rows.Next() {
+ var i WorkspaceAgent
+ if err := rows.Scan(
+ &i.ID,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ &i.Name,
+ &i.FirstConnectedAt,
+ &i.LastConnectedAt,
+ &i.DisconnectedAt,
+ &i.ResourceID,
+ &i.AuthToken,
+ &i.AuthInstanceID,
+ &i.Architecture,
+ &i.EnvironmentVariables,
+ &i.OperatingSystem,
+ &i.InstanceMetadata,
+ &i.ResourceMetadata,
+ &i.Directory,
+ &i.Version,
+ &i.LastConnectedReplicaID,
+ &i.ConnectionTimeoutSeconds,
+ &i.TroubleshootingURL,
+ &i.MOTDFile,
+ &i.LifecycleState,
+ &i.ExpandedDirectory,
+ &i.LogsLength,
+ &i.LogsOverflowed,
+ &i.StartedAt,
+ &i.ReadyAt,
+ pq.Array(&i.Subsystems),
+ pq.Array(&i.DisplayApps),
+ &i.APIVersion,
+ &i.DisplayOrder,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Close(); err != nil {
+ return nil, err
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
const getWorkspaceAgentsCreatedAfter = `-- name: GetWorkspaceAgentsCreatedAfter :many
SELECT id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps, api_version, display_order FROM workspace_agents WHERE created_at > $1
`
diff --git a/coderd/database/queries/prebuilds.sql b/coderd/database/queries/prebuilds.sql
index 1d3a827c98586..8c27ddf62b7c3 100644
--- a/coderd/database/queries/prebuilds.sql
+++ b/coderd/database/queries/prebuilds.sql
@@ -15,6 +15,7 @@ WHERE w.id IN (
AND b.template_version_id = t.active_version_id
AND p.current_preset_id = @preset_id::uuid
AND p.ready
+ AND NOT t.deleted
LIMIT 1 FOR UPDATE OF p SKIP LOCKED -- Ensure that a concurrent request will not select the same prebuild.
)
RETURNING w.id, w.name;
@@ -40,6 +41,7 @@ FROM templates t
INNER JOIN template_version_presets tvp ON tvp.template_version_id = tv.id
INNER JOIN organizations o ON o.id = t.organization_id
WHERE tvp.desired_instances IS NOT NULL -- Consider only presets that have a prebuild configuration.
+ -- AND NOT t.deleted -- We don't exclude deleted templates because there's no constraint in the DB preventing a soft deletion on a template while workspaces are running.
AND (t.id = sqlc.narg('template_id')::uuid OR sqlc.narg('template_id') IS NULL);
-- name: GetRunningPrebuiltWorkspaces :many
@@ -70,6 +72,7 @@ FROM workspace_latest_builds wlb
-- prebuilds that are still building.
INNER JOIN templates t ON t.active_version_id = wlb.template_version_id
WHERE wlb.job_status IN ('pending'::provisioner_job_status, 'running'::provisioner_job_status)
+ -- AND NOT t.deleted -- We don't exclude deleted templates because there's no constraint in the DB preventing a soft deletion on a template while workspaces are running.
GROUP BY t.id, wpb.template_version_id, wpb.transition, wlb.template_version_preset_id;
-- GetPresetsBackoff groups workspace builds by preset ID.
@@ -98,6 +101,7 @@ WITH filtered_builds AS (
WHERE tvp.desired_instances IS NOT NULL -- Consider only presets that have a prebuild configuration.
AND wlb.transition = 'start'::workspace_transition
AND w.owner_id = 'c42fdf75-3097-471c-8c33-fb52454d81c0'
+ AND NOT t.deleted
),
time_sorted_builds AS (
-- Group builds by preset, then sort each group by created_at.
diff --git a/coderd/database/queries/presets.sql b/coderd/database/queries/presets.sql
index 15bcea0c28fb5..6d5646a285b4a 100644
--- a/coderd/database/queries/presets.sql
+++ b/coderd/database/queries/presets.sql
@@ -1,5 +1,6 @@
-- name: InsertPreset :one
INSERT INTO template_version_presets (
+ id,
template_version_id,
name,
created_at,
@@ -7,6 +8,7 @@ INSERT INTO template_version_presets (
invalidate_after_secs
)
VALUES (
+ @id,
@template_version_id,
@name,
@created_at,
diff --git a/coderd/database/queries/workspaceagents.sql b/coderd/database/queries/workspaceagents.sql
index 52d8b5275fc97..0d4a5343f78ba 100644
--- a/coderd/database/queries/workspaceagents.sql
+++ b/coderd/database/queries/workspaceagents.sql
@@ -252,6 +252,19 @@ WHERE
wb.workspace_id = @workspace_id :: uuid
);
+-- name: GetWorkspaceAgentsByWorkspaceAndBuildNumber :many
+SELECT
+ workspace_agents.*
+FROM
+ workspace_agents
+JOIN
+ workspace_resources ON workspace_agents.resource_id = workspace_resources.id
+JOIN
+ workspace_builds ON workspace_resources.job_id = workspace_builds.job_id
+WHERE
+ workspace_builds.workspace_id = @workspace_id :: uuid AND
+ workspace_builds.build_number = @build_number :: int;
+
-- name: GetWorkspaceAgentAndLatestBuildByAuthToken :one
SELECT
sqlc.embed(workspaces),
diff --git a/coderd/notifications/events.go b/coderd/notifications/events.go
index 2f45205bf33ec..35d9925055da5 100644
--- a/coderd/notifications/events.go
+++ b/coderd/notifications/events.go
@@ -39,6 +39,7 @@ var (
TemplateTemplateDeprecated = uuid.MustParse("f40fae84-55a2-42cd-99fa-b41c1ca64894")
TemplateWorkspaceBuildsFailedReport = uuid.MustParse("34a20db2-e9cc-4a93-b0e4-8569699d7a00")
+ TemplateWorkspaceResourceReplaced = uuid.MustParse("89d9745a-816e-4695-a17f-3d0a229e2b8d")
)
// Notification-related events.
diff --git a/coderd/notifications/notifications_test.go b/coderd/notifications/notifications_test.go
index 12372b74a14c3..8f8a3c82441e0 100644
--- a/coderd/notifications/notifications_test.go
+++ b/coderd/notifications/notifications_test.go
@@ -35,6 +35,9 @@ import (
"golang.org/x/xerrors"
"cdr.dev/slog"
+ "github.com/coder/quartz"
+ "github.com/coder/serpent"
+
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbauthz"
@@ -48,8 +51,6 @@ import (
"github.com/coder/coder/v2/coderd/util/syncmap"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/testutil"
- "github.com/coder/quartz"
- "github.com/coder/serpent"
)
// updateGoldenFiles is a flag that can be set to update golden files.
@@ -1226,6 +1227,29 @@ func TestNotificationTemplates_Golden(t *testing.T) {
Labels: map[string]string{},
},
},
+ {
+ name: "TemplateWorkspaceResourceReplaced",
+ id: notifications.TemplateWorkspaceResourceReplaced,
+ payload: types.MessagePayload{
+ UserName: "Bobby",
+ UserEmail: "bobby@coder.com",
+ UserUsername: "bobby",
+ Labels: map[string]string{
+ "org": "cern",
+ "workspace": "my-workspace",
+ "workspace_build_num": "2",
+ "template": "docker",
+ "template_version": "angry_torvalds",
+ "preset": "particle-accelerator",
+ "claimant": "prebuilds-claimer",
+ },
+ Data: map[string]any{
+ "replacements": map[string]string{
+ "docker_container[0]": "env, hostname",
+ },
+ },
+ },
+ },
}
// We must have a test case for every notification_template. This is enforced below:
diff --git a/coderd/notifications/notificationstest/fake_enqueuer.go b/coderd/notifications/notificationstest/fake_enqueuer.go
index 8fbc2cee25806..568091818295c 100644
--- a/coderd/notifications/notificationstest/fake_enqueuer.go
+++ b/coderd/notifications/notificationstest/fake_enqueuer.go
@@ -9,6 +9,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/coder/coder/v2/coderd/database/dbauthz"
+ "github.com/coder/coder/v2/coderd/notifications"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/rbac/policy"
)
@@ -19,6 +20,12 @@ type FakeEnqueuer struct {
sent []*FakeNotification
}
+var _ notifications.Enqueuer = &FakeEnqueuer{}
+
+func NewFakeEnqueuer() *FakeEnqueuer {
+ return &FakeEnqueuer{}
+}
+
type FakeNotification struct {
UserID, TemplateID uuid.UUID
Labels map[string]string
diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceResourceReplaced.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceResourceReplaced.html.golden
new file mode 100644
index 0000000000000..6d64eed0249a7
--- /dev/null
+++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceResourceReplaced.html.golden
@@ -0,0 +1,131 @@
+From: system@coder.com
+To: bobby@coder.com
+Subject: There might be a problem with a recently claimed prebuilt workspace
+Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48
+Date: Fri, 11 Oct 2024 09:03:06 +0000
+Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4
+MIME-Version: 1.0
+
+--bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4
+Content-Transfer-Encoding: quoted-printable
+Content-Type: text/plain; charset=UTF-8
+
+Hi Bobby,
+
+Workspace my-workspace was claimed from a prebuilt workspace by prebuilds-c=
+laimer.
+
+During the claim, Terraform destroyed and recreated the following resources
+because one or more immutable attributes changed:
+
+docker_container[0] was replaced due to changes to env, hostname
+
+When Terraform must change an immutable attribute, it replaces the entire r=
+esource.
+If you=E2=80=99re using prebuilds to speed up provisioning, unexpected repl=
+acements will slow down
+workspace startup=E2=80=94even when claiming a prebuilt environment.
+
+For tips on preventing replacements and improving claim performance, see th=
+is guide (https://coder.com/docs/admin/templates/extending-templates/prebui=
+lt-workspaces#preventing-resource-replacement).
+
+NOTE: this prebuilt workspace used the particle-accelerator preset.
+
+
+View workspace build: http://test.com/@prebuilds-claimer/my-workspace/build=
+s/2
+
+View template version: http://test.com/templates/cern/docker/versions/angry=
+_torvalds
+
+--bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4
+Content-Transfer-Encoding: quoted-printable
+Content-Type: text/html; charset=UTF-8
+
+
+
+
+
+
+ There might be a problem with a recently claimed prebuilt worksp=
+ace
+
+
+
+
+

+
+
+ There might be a problem with a recently claimed prebuilt workspace
+
+
+
Hi Bobby,
+
Workspace my-workspace was claimed from a prebu=
+ilt workspace by prebuilds-claimer.
+
+
During the claim, Terraform destroyed and recreated the following resour=
+ces
+because one or more immutable attributes changed:
+
+
+- _dockercontainer[0] was replaced due to changes to env, h=
+ostname
+
+
+
+
When Terraform must change an immutable attribute, it replaces the entir=
+e resource.
+If you=E2=80=99re using prebuilds to speed up provisioning, unexpected repl=
+acements will slow down
+workspace startup=E2=80=94even when claiming a prebuilt environment.
+
+
For tips on preventing replacements and improving claim performance, see=
+ this guide.
+
+
NOTE: this prebuilt workspace used the particle-accelerator preset.
+
+
+
+
+
+
+
+--bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4--
diff --git a/coderd/notifications/testdata/rendered-templates/webhook/TemplateWorkspaceResourceReplaced.json.golden b/coderd/notifications/testdata/rendered-templates/webhook/TemplateWorkspaceResourceReplaced.json.golden
new file mode 100644
index 0000000000000..09bf9431cdeed
--- /dev/null
+++ b/coderd/notifications/testdata/rendered-templates/webhook/TemplateWorkspaceResourceReplaced.json.golden
@@ -0,0 +1,42 @@
+{
+ "_version": "1.1",
+ "msg_id": "00000000-0000-0000-0000-000000000000",
+ "payload": {
+ "_version": "1.2",
+ "notification_name": "Prebuilt Workspace Resource Replaced",
+ "notification_template_id": "00000000-0000-0000-0000-000000000000",
+ "user_id": "00000000-0000-0000-0000-000000000000",
+ "user_email": "bobby@coder.com",
+ "user_name": "Bobby",
+ "user_username": "bobby",
+ "actions": [
+ {
+ "label": "View workspace build",
+ "url": "http://test.com/@prebuilds-claimer/my-workspace/builds/2"
+ },
+ {
+ "label": "View template version",
+ "url": "http://test.com/templates/cern/docker/versions/angry_torvalds"
+ }
+ ],
+ "labels": {
+ "claimant": "prebuilds-claimer",
+ "org": "cern",
+ "preset": "particle-accelerator",
+ "template": "docker",
+ "template_version": "angry_torvalds",
+ "workspace": "my-workspace",
+ "workspace_build_num": "2"
+ },
+ "data": {
+ "replacements": {
+ "docker_container[0]": "env, hostname"
+ }
+ },
+ "targets": null
+ },
+ "title": "There might be a problem with a recently claimed prebuilt workspace",
+ "title_markdown": "There might be a problem with a recently claimed prebuilt workspace",
+ "body": "Workspace my-workspace was claimed from a prebuilt workspace by prebuilds-claimer.\n\nDuring the claim, Terraform destroyed and recreated the following resources\nbecause one or more immutable attributes changed:\n\ndocker_container[0] was replaced due to changes to env, hostname\n\nWhen Terraform must change an immutable attribute, it replaces the entire resource.\nIf you’re using prebuilds to speed up provisioning, unexpected replacements will slow down\nworkspace startup—even when claiming a prebuilt environment.\n\nFor tips on preventing replacements and improving claim performance, see this guide (https://coder.com/docs/admin/templates/extending-templates/prebuilt-workspaces#preventing-resource-replacement).\n\nNOTE: this prebuilt workspace used the particle-accelerator preset.",
+ "body_markdown": "\nWorkspace **my-workspace** was claimed from a prebuilt workspace by **prebuilds-claimer**.\n\nDuring the claim, Terraform destroyed and recreated the following resources\nbecause one or more immutable attributes changed:\n\n- _docker_container[0]_ was replaced due to changes to _env, hostname_\n\n\nWhen Terraform must change an immutable attribute, it replaces the entire resource.\nIf you’re using prebuilds to speed up provisioning, unexpected replacements will slow down\nworkspace startup—even when claiming a prebuilt environment.\n\nFor tips on preventing replacements and improving claim performance, see [this guide](https://coder.com/docs/admin/templates/extending-templates/prebuilt-workspaces#preventing-resource-replacement).\n\nNOTE: this prebuilt workspace used the **particle-accelerator** preset.\n"
+}
\ No newline at end of file
diff --git a/coderd/prebuilds/api.go b/coderd/prebuilds/api.go
index 00129eae37491..3092d27421d26 100644
--- a/coderd/prebuilds/api.go
+++ b/coderd/prebuilds/api.go
@@ -7,6 +7,7 @@ import (
"golang.org/x/xerrors"
"github.com/coder/coder/v2/coderd/database"
+ sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
)
var (
@@ -27,6 +28,11 @@ type ReconciliationOrchestrator interface {
// Stop gracefully shuts down the orchestrator with the given cause.
// The cause is used for logging and error reporting.
Stop(ctx context.Context, cause error)
+
+ // TrackResourceReplacement handles a pathological situation whereby a terraform resource is replaced due to drift,
+ // which can obviate the whole point of pre-provisioning a prebuilt workspace.
+ // See more detail at https://coder.com/docs/admin/templates/extending-templates/prebuilt-workspaces#preventing-resource-replacement.
+ TrackResourceReplacement(ctx context.Context, workspaceID, buildID uuid.UUID, replacements []*sdkproto.ResourceReplacement)
}
type Reconciler interface {
diff --git a/coderd/prebuilds/claim.go b/coderd/prebuilds/claim.go
new file mode 100644
index 0000000000000..b5155b8f2a568
--- /dev/null
+++ b/coderd/prebuilds/claim.go
@@ -0,0 +1,82 @@
+package prebuilds
+
+import (
+ "context"
+ "sync"
+
+ "github.com/google/uuid"
+ "golang.org/x/xerrors"
+
+ "cdr.dev/slog"
+ "github.com/coder/coder/v2/coderd/database/pubsub"
+ "github.com/coder/coder/v2/codersdk/agentsdk"
+)
+
+func NewPubsubWorkspaceClaimPublisher(ps pubsub.Pubsub) *PubsubWorkspaceClaimPublisher {
+ return &PubsubWorkspaceClaimPublisher{ps: ps}
+}
+
+type PubsubWorkspaceClaimPublisher struct {
+ ps pubsub.Pubsub
+}
+
+func (p PubsubWorkspaceClaimPublisher) PublishWorkspaceClaim(claim agentsdk.ReinitializationEvent) error {
+ channel := agentsdk.PrebuildClaimedChannel(claim.WorkspaceID)
+ if err := p.ps.Publish(channel, []byte(claim.Reason)); err != nil {
+ return xerrors.Errorf("failed to trigger prebuilt workspace agent reinitialization: %w", err)
+ }
+ return nil
+}
+
+func NewPubsubWorkspaceClaimListener(ps pubsub.Pubsub, logger slog.Logger) *PubsubWorkspaceClaimListener {
+ return &PubsubWorkspaceClaimListener{ps: ps, logger: logger}
+}
+
+type PubsubWorkspaceClaimListener struct {
+ logger slog.Logger
+ ps pubsub.Pubsub
+}
+
+// ListenForWorkspaceClaims subscribes to a pubsub channel and sends any received events on the chan that it returns.
+// pubsub.Pubsub does not communicate when its last callback has been called after it has been closed. As such the chan
+// returned by this method is never closed. Call the returned cancel() function to close the subscription when it is no longer needed.
+// cancel() will be called if ctx expires or is canceled.
+func (p PubsubWorkspaceClaimListener) ListenForWorkspaceClaims(ctx context.Context, workspaceID uuid.UUID, reinitEvents chan<- agentsdk.ReinitializationEvent) (func(), error) {
+ select {
+ case <-ctx.Done():
+ return func() {}, ctx.Err()
+ default:
+ }
+
+ cancelSub, err := p.ps.Subscribe(agentsdk.PrebuildClaimedChannel(workspaceID), func(inner context.Context, reason []byte) {
+ claim := agentsdk.ReinitializationEvent{
+ WorkspaceID: workspaceID,
+ Reason: agentsdk.ReinitializationReason(reason),
+ }
+
+ select {
+ case <-ctx.Done():
+ return
+ case <-inner.Done():
+ return
+ case reinitEvents <- claim:
+ }
+ })
+ if err != nil {
+ return func() {}, xerrors.Errorf("failed to subscribe to prebuild claimed channel: %w", err)
+ }
+
+ var once sync.Once
+ cancel := func() {
+ once.Do(func() {
+ cancelSub()
+ })
+ }
+
+ go func() {
+ <-ctx.Done()
+ cancel()
+ }()
+
+ return cancel, nil
+}
diff --git a/coderd/prebuilds/claim_test.go b/coderd/prebuilds/claim_test.go
new file mode 100644
index 0000000000000..670bb64eec756
--- /dev/null
+++ b/coderd/prebuilds/claim_test.go
@@ -0,0 +1,141 @@
+package prebuilds_test
+
+import (
+ "context"
+ "testing"
+ "time"
+
+ "github.com/google/uuid"
+ "github.com/stretchr/testify/require"
+ "golang.org/x/xerrors"
+
+ "cdr.dev/slog/sloggers/slogtest"
+ "github.com/coder/coder/v2/coderd/database/pubsub"
+ "github.com/coder/coder/v2/coderd/prebuilds"
+ "github.com/coder/coder/v2/codersdk/agentsdk"
+ "github.com/coder/coder/v2/testutil"
+)
+
+func TestPubsubWorkspaceClaimPublisher(t *testing.T) {
+ t.Parallel()
+ t.Run("published claim is received by a listener for the same workspace", func(t *testing.T) {
+ t.Parallel()
+
+ ctx := testutil.Context(t, testutil.WaitShort)
+ logger := testutil.Logger(t)
+ ps := pubsub.NewInMemory()
+ workspaceID := uuid.New()
+ reinitEvents := make(chan agentsdk.ReinitializationEvent, 1)
+ publisher := prebuilds.NewPubsubWorkspaceClaimPublisher(ps)
+ listener := prebuilds.NewPubsubWorkspaceClaimListener(ps, logger)
+
+ cancel, err := listener.ListenForWorkspaceClaims(ctx, workspaceID, reinitEvents)
+ require.NoError(t, err)
+ defer cancel()
+
+ claim := agentsdk.ReinitializationEvent{
+ WorkspaceID: workspaceID,
+ Reason: agentsdk.ReinitializeReasonPrebuildClaimed,
+ }
+ err = publisher.PublishWorkspaceClaim(claim)
+ require.NoError(t, err)
+
+ gotEvent := testutil.RequireReceive(ctx, t, reinitEvents)
+ require.Equal(t, workspaceID, gotEvent.WorkspaceID)
+ require.Equal(t, claim.Reason, gotEvent.Reason)
+ })
+
+ t.Run("fail to publish claim", func(t *testing.T) {
+ t.Parallel()
+
+ ps := &brokenPubsub{}
+
+ publisher := prebuilds.NewPubsubWorkspaceClaimPublisher(ps)
+ claim := agentsdk.ReinitializationEvent{
+ WorkspaceID: uuid.New(),
+ Reason: agentsdk.ReinitializeReasonPrebuildClaimed,
+ }
+
+ err := publisher.PublishWorkspaceClaim(claim)
+ require.ErrorContains(t, err, "failed to trigger prebuilt workspace agent reinitialization")
+ })
+}
+
+func TestPubsubWorkspaceClaimListener(t *testing.T) {
+ t.Parallel()
+ t.Run("finds claim events for its workspace", func(t *testing.T) {
+ t.Parallel()
+
+ ps := pubsub.NewInMemory()
+ listener := prebuilds.NewPubsubWorkspaceClaimListener(ps, slogtest.Make(t, nil))
+
+ claims := make(chan agentsdk.ReinitializationEvent, 1) // Buffer to avoid messing with goroutines in the rest of the test
+
+ workspaceID := uuid.New()
+ cancelFunc, err := listener.ListenForWorkspaceClaims(context.Background(), workspaceID, claims)
+ require.NoError(t, err)
+ defer cancelFunc()
+
+ // Publish a claim
+ channel := agentsdk.PrebuildClaimedChannel(workspaceID)
+ reason := agentsdk.ReinitializeReasonPrebuildClaimed
+ err = ps.Publish(channel, []byte(reason))
+ require.NoError(t, err)
+
+ // Verify we receive the claim
+ ctx := testutil.Context(t, testutil.WaitShort)
+ claim := testutil.RequireReceive(ctx, t, claims)
+ require.Equal(t, workspaceID, claim.WorkspaceID)
+ require.Equal(t, reason, claim.Reason)
+ })
+
+ t.Run("ignores claim events for other workspaces", func(t *testing.T) {
+ t.Parallel()
+
+ ps := pubsub.NewInMemory()
+ listener := prebuilds.NewPubsubWorkspaceClaimListener(ps, slogtest.Make(t, nil))
+
+ claims := make(chan agentsdk.ReinitializationEvent)
+ workspaceID := uuid.New()
+ otherWorkspaceID := uuid.New()
+ cancelFunc, err := listener.ListenForWorkspaceClaims(context.Background(), workspaceID, claims)
+ require.NoError(t, err)
+ defer cancelFunc()
+
+ // Publish a claim for a different workspace
+ channel := agentsdk.PrebuildClaimedChannel(otherWorkspaceID)
+ err = ps.Publish(channel, []byte(agentsdk.ReinitializeReasonPrebuildClaimed))
+ require.NoError(t, err)
+
+ // Verify we don't receive the claim
+ select {
+ case <-claims:
+ t.Fatal("received claim for wrong workspace")
+ case <-time.After(100 * time.Millisecond):
+ // Expected - no claim received
+ }
+ })
+
+ t.Run("communicates the error if it can't subscribe", func(t *testing.T) {
+ t.Parallel()
+
+ claims := make(chan agentsdk.ReinitializationEvent)
+ ps := &brokenPubsub{}
+ listener := prebuilds.NewPubsubWorkspaceClaimListener(ps, slogtest.Make(t, nil))
+
+ _, err := listener.ListenForWorkspaceClaims(context.Background(), uuid.New(), claims)
+ require.ErrorContains(t, err, "failed to subscribe to prebuild claimed channel")
+ })
+}
+
+type brokenPubsub struct {
+ pubsub.Pubsub
+}
+
+func (brokenPubsub) Subscribe(_ string, _ pubsub.Listener) (func(), error) {
+ return nil, xerrors.New("broken")
+}
+
+func (brokenPubsub) Publish(_ string, _ []byte) error {
+ return xerrors.New("broken")
+}
diff --git a/coderd/prebuilds/noop.go b/coderd/prebuilds/noop.go
index 6fb3f7c6a5f1f..3c2dd78a804db 100644
--- a/coderd/prebuilds/noop.go
+++ b/coderd/prebuilds/noop.go
@@ -6,12 +6,15 @@ import (
"github.com/google/uuid"
"github.com/coder/coder/v2/coderd/database"
+ sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
)
type NoopReconciler struct{}
-func (NoopReconciler) Run(context.Context) {}
-func (NoopReconciler) Stop(context.Context, error) {}
+func (NoopReconciler) Run(context.Context) {}
+func (NoopReconciler) Stop(context.Context, error) {}
+func (NoopReconciler) TrackResourceReplacement(context.Context, uuid.UUID, uuid.UUID, []*sdkproto.ResourceReplacement) {
+}
func (NoopReconciler) ReconcileAll(context.Context) error { return nil }
func (NoopReconciler) SnapshotState(context.Context, database.Store) (*GlobalSnapshot, error) {
return &GlobalSnapshot{}, nil
diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go
index 9362d2f3e5a85..e084499f02f18 100644
--- a/coderd/provisionerdserver/provisionerdserver.go
+++ b/coderd/provisionerdserver/provisionerdserver.go
@@ -37,12 +37,14 @@ import (
"github.com/coder/coder/v2/coderd/database/pubsub"
"github.com/coder/coder/v2/coderd/externalauth"
"github.com/coder/coder/v2/coderd/notifications"
+ "github.com/coder/coder/v2/coderd/prebuilds"
"github.com/coder/coder/v2/coderd/promoauth"
"github.com/coder/coder/v2/coderd/schedule"
"github.com/coder/coder/v2/coderd/telemetry"
"github.com/coder/coder/v2/coderd/tracing"
"github.com/coder/coder/v2/coderd/wspubsub"
"github.com/coder/coder/v2/codersdk"
+ "github.com/coder/coder/v2/codersdk/agentsdk"
"github.com/coder/coder/v2/codersdk/drpc"
"github.com/coder/coder/v2/provisioner"
"github.com/coder/coder/v2/provisionerd/proto"
@@ -108,6 +110,7 @@ type server struct {
UserQuietHoursScheduleStore *atomic.Pointer[schedule.UserQuietHoursScheduleStore]
DeploymentValues *codersdk.DeploymentValues
NotificationsEnqueuer notifications.Enqueuer
+ PrebuildsOrchestrator *atomic.Pointer[prebuilds.ReconciliationOrchestrator]
OIDCConfig promoauth.OAuth2Config
@@ -143,8 +146,7 @@ func (t Tags) Valid() error {
return nil
}
-func NewServer(
- lifecycleCtx context.Context,
+func NewServer(lifecycleCtx context.Context,
accessURL *url.URL,
id uuid.UUID,
organizationID uuid.UUID,
@@ -163,6 +165,7 @@ func NewServer(
deploymentValues *codersdk.DeploymentValues,
options Options,
enqueuer notifications.Enqueuer,
+ prebuildsOrchestrator *atomic.Pointer[prebuilds.ReconciliationOrchestrator],
) (proto.DRPCProvisionerDaemonServer, error) {
// Fail-fast if pointers are nil
if lifecycleCtx == nil {
@@ -227,6 +230,7 @@ func NewServer(
acquireJobLongPollDur: options.AcquireJobLongPollDur,
heartbeatInterval: options.HeartbeatInterval,
heartbeatFn: options.HeartbeatFn,
+ PrebuildsOrchestrator: prebuildsOrchestrator,
}
if s.heartbeatFn == nil {
@@ -617,6 +621,30 @@ func (s *server) acquireProtoJob(ctx context.Context, job database.ProvisionerJo
}
}
+ runningAgentAuthTokens := []*sdkproto.RunningAgentAuthToken{}
+ if input.PrebuiltWorkspaceBuildStage == sdkproto.PrebuiltWorkspaceBuildStage_CLAIM {
+ // runningAgentAuthTokens are *only* used for prebuilds. We fetch them when we want to rebuild a prebuilt workspace
+ // but not generate new agent tokens. The provisionerdserver will push them down to
+ // the provisioner (and ultimately to the `coder_agent` resource in the Terraform provider) where they will be
+ // reused. Context: the agent token is often used in immutable attributes of workspace resource (e.g. VM/container)
+ // to initialize the agent, so if that value changes it will necessitate a replacement of that resource, thus
+ // obviating the whole point of the prebuild.
+ agents, err := s.Database.GetWorkspaceAgentsByWorkspaceAndBuildNumber(ctx, database.GetWorkspaceAgentsByWorkspaceAndBuildNumberParams{
+ WorkspaceID: workspace.ID,
+ BuildNumber: 1,
+ })
+ if err != nil {
+ s.Logger.Error(ctx, "failed to retrieve running agents of claimed prebuilt workspace",
+ slog.F("workspace_id", workspace.ID), slog.Error(err))
+ }
+ for _, agent := range agents {
+ runningAgentAuthTokens = append(runningAgentAuthTokens, &sdkproto.RunningAgentAuthToken{
+ AgentId: agent.ID.String(),
+ Token: agent.AuthToken.String(),
+ })
+ }
+ }
+
protoJob.Type = &proto.AcquiredJob_WorkspaceBuild_{
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
WorkspaceBuildId: workspaceBuild.ID.String(),
@@ -645,7 +673,8 @@ func (s *server) acquireProtoJob(ctx context.Context, job database.ProvisionerJo
WorkspaceBuildId: workspaceBuild.ID.String(),
WorkspaceOwnerLoginType: string(owner.LoginType),
WorkspaceOwnerRbacRoles: ownerRbacRoles,
- IsPrebuild: input.IsPrebuild,
+ RunningAgentAuthTokens: runningAgentAuthTokens,
+ PrebuiltWorkspaceBuildStage: input.PrebuiltWorkspaceBuildStage,
},
LogLevel: input.LogLevel,
},
@@ -1722,6 +1751,15 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
})
}
+ if s.PrebuildsOrchestrator != nil && input.PrebuiltWorkspaceBuildStage == sdkproto.PrebuiltWorkspaceBuildStage_CLAIM {
+ // Track resource replacements, if there are any.
+ orchestrator := s.PrebuildsOrchestrator.Load()
+ if resourceReplacements := completed.GetWorkspaceBuild().GetResourceReplacements(); orchestrator != nil && len(resourceReplacements) > 0 {
+ // Fire and forget. Bind to the lifecycle of the server so shutdowns are handled gracefully.
+ go (*orchestrator).TrackResourceReplacement(s.lifecycleCtx, workspace.ID, workspaceBuild.ID, resourceReplacements)
+ }
+ }
+
msg, err := json.Marshal(wspubsub.WorkspaceEvent{
Kind: wspubsub.WorkspaceEventKindStateChange,
WorkspaceID: workspace.ID,
@@ -1733,6 +1771,19 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
if err != nil {
return nil, xerrors.Errorf("update workspace: %w", err)
}
+
+ if input.PrebuiltWorkspaceBuildStage == sdkproto.PrebuiltWorkspaceBuildStage_CLAIM {
+ s.Logger.Info(ctx, "workspace prebuild successfully claimed by user",
+ slog.F("workspace_id", workspace.ID))
+
+ err = prebuilds.NewPubsubWorkspaceClaimPublisher(s.Pubsub).PublishWorkspaceClaim(agentsdk.ReinitializationEvent{
+ WorkspaceID: workspace.ID,
+ Reason: agentsdk.ReinitializeReasonPrebuildClaimed,
+ })
+ if err != nil {
+ s.Logger.Error(ctx, "failed to publish workspace claim event", slog.Error(err))
+ }
+ }
case *proto.CompletedJob_TemplateDryRun_:
for _, resource := range jobType.TemplateDryRun.Resources {
s.Logger.Info(ctx, "inserting template dry-run job resource",
@@ -1876,6 +1927,7 @@ func InsertWorkspacePresetAndParameters(ctx context.Context, db database.Store,
}
}
dbPreset, err := tx.InsertPreset(ctx, database.InsertPresetParams{
+ ID: uuid.New(),
TemplateVersionID: templateVersionID,
Name: protoPreset.Name,
CreatedAt: t,
@@ -2471,11 +2523,10 @@ type TemplateVersionImportJob struct {
// WorkspaceProvisionJob is the payload for the "workspace_provision" job type.
type WorkspaceProvisionJob struct {
- WorkspaceBuildID uuid.UUID `json:"workspace_build_id"`
- DryRun bool `json:"dry_run"`
- IsPrebuild bool `json:"is_prebuild,omitempty"`
- PrebuildClaimedByUser uuid.UUID `json:"prebuild_claimed_by,omitempty"`
- LogLevel string `json:"log_level,omitempty"`
+ WorkspaceBuildID uuid.UUID `json:"workspace_build_id"`
+ DryRun bool `json:"dry_run"`
+ LogLevel string `json:"log_level,omitempty"`
+ PrebuiltWorkspaceBuildStage sdkproto.PrebuiltWorkspaceBuildStage `json:"prebuilt_workspace_stage,omitempty"`
}
// TemplateVersionDryRunJob is the payload for the "template_version_dry_run" job type.
diff --git a/coderd/provisionerdserver/provisionerdserver_test.go b/coderd/provisionerdserver/provisionerdserver_test.go
index caeef8a9793b7..1ba86c50baf8a 100644
--- a/coderd/provisionerdserver/provisionerdserver_test.go
+++ b/coderd/provisionerdserver/provisionerdserver_test.go
@@ -23,7 +23,6 @@ import (
"storj.io/drpc"
"cdr.dev/slog/sloggers/slogtest"
- "github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/quartz"
"github.com/coder/serpent"
@@ -38,12 +37,15 @@ import (
"github.com/coder/coder/v2/coderd/externalauth"
"github.com/coder/coder/v2/coderd/notifications"
"github.com/coder/coder/v2/coderd/notifications/notificationstest"
+ agplprebuilds "github.com/coder/coder/v2/coderd/prebuilds"
"github.com/coder/coder/v2/coderd/provisionerdserver"
+ "github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/schedule"
"github.com/coder/coder/v2/coderd/schedule/cron"
"github.com/coder/coder/v2/coderd/telemetry"
"github.com/coder/coder/v2/coderd/wspubsub"
"github.com/coder/coder/v2/codersdk"
+ "github.com/coder/coder/v2/codersdk/agentsdk"
"github.com/coder/coder/v2/provisionerd/proto"
"github.com/coder/coder/v2/provisionersdk"
sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
@@ -166,8 +168,12 @@ func TestAcquireJob(t *testing.T) {
_, err = tc.acquire(ctx, srv)
require.ErrorContains(t, err, "sql: no rows in result set")
})
- for _, prebuiltWorkspace := range []bool{false, true} {
- prebuiltWorkspace := prebuiltWorkspace
+ for _, prebuiltWorkspaceBuildStage := range []sdkproto.PrebuiltWorkspaceBuildStage{
+ sdkproto.PrebuiltWorkspaceBuildStage_NONE,
+ sdkproto.PrebuiltWorkspaceBuildStage_CREATE,
+ sdkproto.PrebuiltWorkspaceBuildStage_CLAIM,
+ } {
+ prebuiltWorkspaceBuildStage := prebuiltWorkspaceBuildStage
t.Run(tc.name+"_WorkspaceBuildJob", func(t *testing.T) {
t.Parallel()
// Set the max session token lifetime so we can assert we
@@ -211,7 +217,7 @@ func TestAcquireJob(t *testing.T) {
Roles: []string{rbac.RoleOrgAuditor()},
})
- // Add extra erronous roles
+ // Add extra erroneous roles
secondOrg := dbgen.Organization(t, db, database.Organization{})
dbgen.OrganizationMember(t, db, database.OrganizationMember{
UserID: user.ID,
@@ -286,32 +292,74 @@ func TestAcquireJob(t *testing.T) {
Required: true,
Sensitive: false,
})
- workspace := dbgen.Workspace(t, db, database.WorkspaceTable{
+ workspace := database.WorkspaceTable{
TemplateID: template.ID,
OwnerID: user.ID,
OrganizationID: pd.OrganizationID,
- })
- build := dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
+ }
+ workspace = dbgen.Workspace(t, db, workspace)
+ build := database.WorkspaceBuild{
WorkspaceID: workspace.ID,
BuildNumber: 1,
JobID: uuid.New(),
TemplateVersionID: version.ID,
Transition: database.WorkspaceTransitionStart,
Reason: database.BuildReasonInitiator,
- })
- _ = dbgen.ProvisionerJob(t, db, ps, database.ProvisionerJob{
- ID: build.ID,
+ }
+ build = dbgen.WorkspaceBuild(t, db, build)
+ input := provisionerdserver.WorkspaceProvisionJob{
+ WorkspaceBuildID: build.ID,
+ }
+ dbJob := database.ProvisionerJob{
+ ID: build.JobID,
OrganizationID: pd.OrganizationID,
InitiatorID: user.ID,
Provisioner: database.ProvisionerTypeEcho,
StorageMethod: database.ProvisionerStorageMethodFile,
FileID: file.ID,
Type: database.ProvisionerJobTypeWorkspaceBuild,
- Input: must(json.Marshal(provisionerdserver.WorkspaceProvisionJob{
- WorkspaceBuildID: build.ID,
- IsPrebuild: prebuiltWorkspace,
- })),
- })
+ Input: must(json.Marshal(input)),
+ }
+ dbJob = dbgen.ProvisionerJob(t, db, ps, dbJob)
+
+ var agent database.WorkspaceAgent
+ if prebuiltWorkspaceBuildStage == sdkproto.PrebuiltWorkspaceBuildStage_CLAIM {
+ resource := dbgen.WorkspaceResource(t, db, database.WorkspaceResource{
+ JobID: dbJob.ID,
+ })
+ agent = dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{
+ ResourceID: resource.ID,
+ AuthToken: uuid.New(),
+ })
+ // At this point we have an unclaimed workspace and build, now we need to setup the claim
+ // build
+ build = database.WorkspaceBuild{
+ WorkspaceID: workspace.ID,
+ BuildNumber: 2,
+ JobID: uuid.New(),
+ TemplateVersionID: version.ID,
+ Transition: database.WorkspaceTransitionStart,
+ Reason: database.BuildReasonInitiator,
+ InitiatorID: user.ID,
+ }
+ build = dbgen.WorkspaceBuild(t, db, build)
+
+ input = provisionerdserver.WorkspaceProvisionJob{
+ WorkspaceBuildID: build.ID,
+ PrebuiltWorkspaceBuildStage: prebuiltWorkspaceBuildStage,
+ }
+ dbJob = database.ProvisionerJob{
+ ID: build.JobID,
+ OrganizationID: pd.OrganizationID,
+ InitiatorID: user.ID,
+ Provisioner: database.ProvisionerTypeEcho,
+ StorageMethod: database.ProvisionerStorageMethodFile,
+ FileID: file.ID,
+ Type: database.ProvisionerJobTypeWorkspaceBuild,
+ Input: must(json.Marshal(input)),
+ }
+ dbJob = dbgen.ProvisionerJob(t, db, ps, dbJob)
+ }
startPublished := make(chan struct{})
var closed bool
@@ -345,6 +393,19 @@ func TestAcquireJob(t *testing.T) {
<-startPublished
+ if prebuiltWorkspaceBuildStage == sdkproto.PrebuiltWorkspaceBuildStage_CLAIM {
+ for {
+ // In the case of a prebuild claim, there is a second build, which is the
+ // one that we're interested in.
+ job, err = tc.acquire(ctx, srv)
+ require.NoError(t, err)
+ if _, ok := job.Type.(*proto.AcquiredJob_WorkspaceBuild_); ok {
+ break
+ }
+ }
+ <-startPublished
+ }
+
got, err := json.Marshal(job.Type)
require.NoError(t, err)
@@ -379,8 +440,14 @@ func TestAcquireJob(t *testing.T) {
WorkspaceOwnerLoginType: string(user.LoginType),
WorkspaceOwnerRbacRoles: []*sdkproto.Role{{Name: rbac.RoleOrgMember(), OrgId: pd.OrganizationID.String()}, {Name: "member", OrgId: ""}, {Name: rbac.RoleOrgAuditor(), OrgId: pd.OrganizationID.String()}},
}
- if prebuiltWorkspace {
- wantedMetadata.IsPrebuild = true
+ if prebuiltWorkspaceBuildStage == sdkproto.PrebuiltWorkspaceBuildStage_CLAIM {
+ // For claimed prebuilds, we expect the prebuild state to be set to CLAIM
+ // and we expect tokens from the first build to be set for reuse
+ wantedMetadata.PrebuiltWorkspaceBuildStage = prebuiltWorkspaceBuildStage
+ wantedMetadata.RunningAgentAuthTokens = append(wantedMetadata.RunningAgentAuthTokens, &sdkproto.RunningAgentAuthToken{
+ AgentId: agent.ID.String(),
+ Token: agent.AuthToken.String(),
+ })
}
slices.SortFunc(wantedMetadata.WorkspaceOwnerRbacRoles, func(a, b *sdkproto.Role) int {
@@ -1745,6 +1812,210 @@ func TestCompleteJob(t *testing.T) {
})
}
})
+
+ t.Run("ReinitializePrebuiltAgents", func(t *testing.T) {
+ t.Parallel()
+ type testcase struct {
+ name string
+ shouldReinitializeAgent bool
+ }
+
+ for _, tc := range []testcase{
+ // Whether or not there are presets and those presets define prebuilds, etc
+ // are all irrelevant at this level. Those factors are useful earlier in the process.
+ // Everything relevant to this test is determined by the value of `PrebuildClaimedByUser`
+ // on the provisioner job. As such, there are only two significant test cases:
+ {
+ name: "claimed prebuild",
+ shouldReinitializeAgent: true,
+ },
+ {
+ name: "not a claimed prebuild",
+ shouldReinitializeAgent: false,
+ },
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+
+ // GIVEN an enqueued provisioner job and its dependencies:
+
+ srv, db, ps, pd := setup(t, false, &overrides{})
+
+ buildID := uuid.New()
+ jobInput := provisionerdserver.WorkspaceProvisionJob{
+ WorkspaceBuildID: buildID,
+ }
+ if tc.shouldReinitializeAgent { // This is the key lever in the test
+ // GIVEN the enqueued provisioner job is for a workspace being claimed by a user:
+ jobInput.PrebuiltWorkspaceBuildStage = sdkproto.PrebuiltWorkspaceBuildStage_CLAIM
+ }
+ input, err := json.Marshal(jobInput)
+ require.NoError(t, err)
+
+ ctx := testutil.Context(t, testutil.WaitShort)
+ job, err := db.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
+ Input: input,
+ Provisioner: database.ProvisionerTypeEcho,
+ StorageMethod: database.ProvisionerStorageMethodFile,
+ Type: database.ProvisionerJobTypeWorkspaceBuild,
+ })
+ require.NoError(t, err)
+
+ tpl := dbgen.Template(t, db, database.Template{
+ OrganizationID: pd.OrganizationID,
+ })
+ tv := dbgen.TemplateVersion(t, db, database.TemplateVersion{
+ TemplateID: uuid.NullUUID{UUID: tpl.ID, Valid: true},
+ JobID: job.ID,
+ })
+ workspace := dbgen.Workspace(t, db, database.WorkspaceTable{
+ TemplateID: tpl.ID,
+ })
+ _ = dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
+ ID: buildID,
+ JobID: job.ID,
+ WorkspaceID: workspace.ID,
+ TemplateVersionID: tv.ID,
+ })
+ _, err = db.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
+ WorkerID: uuid.NullUUID{
+ UUID: pd.ID,
+ Valid: true,
+ },
+ Types: []database.ProvisionerType{database.ProvisionerTypeEcho},
+ })
+ require.NoError(t, err)
+
+ // GIVEN something is listening to process workspace reinitialization:
+ reinitChan := make(chan agentsdk.ReinitializationEvent, 1) // Buffered to simplify test structure
+ cancel, err := agplprebuilds.NewPubsubWorkspaceClaimListener(ps, testutil.Logger(t)).ListenForWorkspaceClaims(ctx, workspace.ID, reinitChan)
+ require.NoError(t, err)
+ defer cancel()
+
+ // WHEN the job is completed
+ completedJob := proto.CompletedJob{
+ JobId: job.ID.String(),
+ Type: &proto.CompletedJob_WorkspaceBuild_{
+ WorkspaceBuild: &proto.CompletedJob_WorkspaceBuild{},
+ },
+ }
+ _, err = srv.CompleteJob(ctx, &completedJob)
+ require.NoError(t, err)
+
+ if tc.shouldReinitializeAgent {
+ event := testutil.RequireReceive(ctx, t, reinitChan)
+ require.Equal(t, workspace.ID, event.WorkspaceID)
+ } else {
+ select {
+ case <-reinitChan:
+ t.Fatal("unexpected reinitialization event published")
+ default:
+ // OK
+ }
+ }
+ })
+ }
+ })
+
+ t.Run("PrebuiltWorkspaceClaimWithResourceReplacements", func(t *testing.T) {
+ t.Parallel()
+
+ ctx := testutil.Context(t, testutil.WaitLong)
+
+ // Given: a mock prebuild orchestrator which stores calls to TrackResourceReplacement.
+ done := make(chan struct{})
+ orchestrator := &mockPrebuildsOrchestrator{
+ ReconciliationOrchestrator: agplprebuilds.DefaultReconciler,
+ done: done,
+ }
+ srv, db, ps, pd := setup(t, false, &overrides{
+ prebuildsOrchestrator: orchestrator,
+ })
+
+ // Given: a workspace build which simulates claiming a prebuild.
+ user := dbgen.User(t, db, database.User{})
+ template := dbgen.Template(t, db, database.Template{
+ Name: "template",
+ Provisioner: database.ProvisionerTypeEcho,
+ OrganizationID: pd.OrganizationID,
+ })
+ file := dbgen.File(t, db, database.File{CreatedBy: user.ID})
+ workspaceTable := dbgen.Workspace(t, db, database.WorkspaceTable{
+ TemplateID: template.ID,
+ OwnerID: user.ID,
+ OrganizationID: pd.OrganizationID,
+ })
+ version := dbgen.TemplateVersion(t, db, database.TemplateVersion{
+ OrganizationID: pd.OrganizationID,
+ TemplateID: uuid.NullUUID{
+ UUID: template.ID,
+ Valid: true,
+ },
+ JobID: uuid.New(),
+ })
+ build := dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
+ WorkspaceID: workspaceTable.ID,
+ InitiatorID: user.ID,
+ TemplateVersionID: version.ID,
+ Transition: database.WorkspaceTransitionStart,
+ Reason: database.BuildReasonInitiator,
+ })
+ job := dbgen.ProvisionerJob(t, db, ps, database.ProvisionerJob{
+ FileID: file.ID,
+ InitiatorID: user.ID,
+ Type: database.ProvisionerJobTypeWorkspaceBuild,
+ Input: must(json.Marshal(provisionerdserver.WorkspaceProvisionJob{
+ WorkspaceBuildID: build.ID,
+ PrebuiltWorkspaceBuildStage: sdkproto.PrebuiltWorkspaceBuildStage_CLAIM,
+ })),
+ OrganizationID: pd.OrganizationID,
+ })
+ _, err := db.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
+ OrganizationID: pd.OrganizationID,
+ WorkerID: uuid.NullUUID{
+ UUID: pd.ID,
+ Valid: true,
+ },
+ Types: []database.ProvisionerType{database.ProvisionerTypeEcho},
+ })
+ require.NoError(t, err)
+
+ // When: a replacement is encountered.
+ replacements := []*sdkproto.ResourceReplacement{
+ {
+ Resource: "docker_container[0]",
+ Paths: []string{"env"},
+ },
+ }
+
+ // Then: CompleteJob makes a call to TrackResourceReplacement.
+ _, err = srv.CompleteJob(ctx, &proto.CompletedJob{
+ JobId: job.ID.String(),
+ Type: &proto.CompletedJob_WorkspaceBuild_{
+ WorkspaceBuild: &proto.CompletedJob_WorkspaceBuild{
+ State: []byte{},
+ ResourceReplacements: replacements,
+ },
+ },
+ })
+ require.NoError(t, err)
+
+ // Then: the replacements are as we expected.
+ testutil.RequireReceive(ctx, t, done)
+ require.Equal(t, replacements, orchestrator.replacements)
+ })
+}
+
+type mockPrebuildsOrchestrator struct {
+ agplprebuilds.ReconciliationOrchestrator
+
+ replacements []*sdkproto.ResourceReplacement
+ done chan struct{}
+}
+
+func (m *mockPrebuildsOrchestrator) TrackResourceReplacement(_ context.Context, _, _ uuid.UUID, replacements []*sdkproto.ResourceReplacement) {
+ m.replacements = replacements
+ m.done <- struct{}{}
}
func TestInsertWorkspacePresetsAndParameters(t *testing.T) {
@@ -2630,6 +2901,7 @@ type overrides struct {
heartbeatInterval time.Duration
auditor audit.Auditor
notificationEnqueuer notifications.Enqueuer
+ prebuildsOrchestrator agplprebuilds.ReconciliationOrchestrator
}
func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisionerDaemonServer, database.Store, pubsub.Pubsub, database.ProvisionerDaemon) {
@@ -2711,6 +2983,13 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
})
require.NoError(t, err)
+ prebuildsOrchestrator := ov.prebuildsOrchestrator
+ if prebuildsOrchestrator == nil {
+ prebuildsOrchestrator = agplprebuilds.DefaultReconciler
+ }
+ var op atomic.Pointer[agplprebuilds.ReconciliationOrchestrator]
+ op.Store(&prebuildsOrchestrator)
+
srv, err := provisionerdserver.NewServer(
ov.ctx,
&url.URL{},
@@ -2738,6 +3017,7 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
HeartbeatFn: ov.heartbeatFn,
},
notifEnq,
+ &op,
)
require.NoError(t, err)
return srv, db, ps, daemon
diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go
index 050537705d107..5af9fc009b5aa 100644
--- a/coderd/workspaceagents.go
+++ b/coderd/workspaceagents.go
@@ -35,6 +35,7 @@ import (
"github.com/coder/coder/v2/coderd/httpmw"
"github.com/coder/coder/v2/coderd/httpmw/loggermw"
"github.com/coder/coder/v2/coderd/jwtutils"
+ "github.com/coder/coder/v2/coderd/prebuilds"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/rbac/policy"
"github.com/coder/coder/v2/coderd/telemetry"
@@ -1183,6 +1184,60 @@ func (api *API) workspaceAgentPostLogSource(rw http.ResponseWriter, r *http.Requ
httpapi.Write(ctx, rw, http.StatusCreated, apiSource)
}
+// @Summary Get workspace agent reinitialization
+// @ID get-workspace-agent-reinitialization
+// @Security CoderSessionToken
+// @Produce json
+// @Tags Agents
+// @Success 200 {object} agentsdk.ReinitializationEvent
+// @Router /workspaceagents/me/reinit [get]
+func (api *API) workspaceAgentReinit(rw http.ResponseWriter, r *http.Request) {
+ // Allow us to interrupt watch via cancel.
+ ctx, cancel := context.WithCancel(r.Context())
+ defer cancel()
+ r = r.WithContext(ctx) // Rewire context for SSE cancellation.
+
+ workspaceAgent := httpmw.WorkspaceAgent(r)
+ log := api.Logger.Named("workspace_agent_reinit_watcher").With(
+ slog.F("workspace_agent_id", workspaceAgent.ID),
+ )
+
+ workspace, err := api.Database.GetWorkspaceByAgentID(ctx, workspaceAgent.ID)
+ if err != nil {
+ log.Error(ctx, "failed to retrieve workspace from agent token", slog.Error(err))
+ httpapi.InternalServerError(rw, xerrors.New("failed to determine workspace from agent token"))
+ }
+
+ log.Info(ctx, "agent waiting for reinit instruction")
+
+ reinitEvents := make(chan agentsdk.ReinitializationEvent)
+ cancel, err = prebuilds.NewPubsubWorkspaceClaimListener(api.Pubsub, log).ListenForWorkspaceClaims(ctx, workspace.ID, reinitEvents)
+ if err != nil {
+ log.Error(ctx, "subscribe to prebuild claimed channel", slog.Error(err))
+ httpapi.InternalServerError(rw, xerrors.New("failed to subscribe to prebuild claimed channel"))
+ return
+ }
+ defer cancel()
+
+ transmitter := agentsdk.NewSSEAgentReinitTransmitter(log, rw, r)
+
+ err = transmitter.Transmit(ctx, reinitEvents)
+ switch {
+ case errors.Is(err, agentsdk.ErrTransmissionSourceClosed):
+ log.Info(ctx, "agent reinitialization subscription closed", slog.F("workspace_agent_id", workspaceAgent.ID))
+ case errors.Is(err, agentsdk.ErrTransmissionTargetClosed):
+ log.Info(ctx, "agent connection closed", slog.F("workspace_agent_id", workspaceAgent.ID))
+ case errors.Is(err, context.Canceled):
+ log.Info(ctx, "agent reinitialization", slog.Error(err))
+ case err != nil:
+ log.Error(ctx, "failed to stream agent reinit events", slog.Error(err))
+ httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
+ Message: "Internal error streaming agent reinitialization events.",
+ Detail: err.Error(),
+ })
+ }
+}
+
// convertProvisionedApps converts applications that are in the middle of provisioning process.
// It means that they may not have an agent or workspace assigned (dry-run job).
func convertProvisionedApps(dbApps []database.WorkspaceApp) []codersdk.WorkspaceApp {
diff --git a/coderd/workspaceagents_test.go b/coderd/workspaceagents_test.go
index 6b757a52ec06d..10403f1ac00ae 100644
--- a/coderd/workspaceagents_test.go
+++ b/coderd/workspaceagents_test.go
@@ -11,6 +11,7 @@ import (
"runtime"
"strconv"
"strings"
+ "sync"
"sync/atomic"
"testing"
"time"
@@ -44,10 +45,12 @@ import (
"github.com/coder/coder/v2/coderd/database/dbfake"
"github.com/coder/coder/v2/coderd/database/dbgen"
"github.com/coder/coder/v2/coderd/database/dbmem"
+ "github.com/coder/coder/v2/coderd/database/dbtestutil"
"github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/coderd/database/pubsub"
"github.com/coder/coder/v2/coderd/externalauth"
"github.com/coder/coder/v2/coderd/jwtutils"
+ "github.com/coder/coder/v2/coderd/prebuilds"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/telemetry"
"github.com/coder/coder/v2/coderd/util/ptr"
@@ -2641,3 +2644,70 @@ func TestAgentConnectionInfo(t *testing.T) {
require.True(t, info.DisableDirectConnections)
require.True(t, info.DERPForceWebSockets)
}
+
+func TestReinit(t *testing.T) {
+ t.Parallel()
+
+ db, ps := dbtestutil.NewDB(t)
+ pubsubSpy := pubsubReinitSpy{
+ Pubsub: ps,
+ subscribed: make(chan string),
+ }
+ client := coderdtest.New(t, &coderdtest.Options{
+ Database: db,
+ Pubsub: &pubsubSpy,
+ })
+ user := coderdtest.CreateFirstUser(t, client)
+
+ r := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
+ OrganizationID: user.OrganizationID,
+ OwnerID: user.UserID,
+ }).WithAgent().Do()
+
+ pubsubSpy.Mutex.Lock()
+ pubsubSpy.expectedEvent = agentsdk.PrebuildClaimedChannel(r.Workspace.ID)
+ pubsubSpy.Mutex.Unlock()
+
+ agentCtx := testutil.Context(t, testutil.WaitShort)
+ agentClient := agentsdk.New(client.URL)
+ agentClient.SetSessionToken(r.AgentToken)
+
+ agentReinitializedCh := make(chan *agentsdk.ReinitializationEvent)
+ go func() {
+ reinitEvent, err := agentClient.WaitForReinit(agentCtx)
+ assert.NoError(t, err)
+ agentReinitializedCh <- reinitEvent
+ }()
+
+ // We need to subscribe before we publish, lest we miss the event
+ ctx := testutil.Context(t, testutil.WaitShort)
+ testutil.TryReceive(ctx, t, pubsubSpy.subscribed) // Wait for the appropriate subscription
+
+ // Now that we're subscribed, publish the event
+ err := prebuilds.NewPubsubWorkspaceClaimPublisher(ps).PublishWorkspaceClaim(agentsdk.ReinitializationEvent{
+ WorkspaceID: r.Workspace.ID,
+ Reason: agentsdk.ReinitializeReasonPrebuildClaimed,
+ })
+ require.NoError(t, err)
+
+ ctx = testutil.Context(t, testutil.WaitShort)
+ reinitEvent := testutil.TryReceive(ctx, t, agentReinitializedCh)
+ require.NotNil(t, reinitEvent)
+ require.Equal(t, r.Workspace.ID, reinitEvent.WorkspaceID)
+}
+
+type pubsubReinitSpy struct {
+ pubsub.Pubsub
+ sync.Mutex
+ subscribed chan string
+ expectedEvent string
+}
+
+func (p *pubsubReinitSpy) Subscribe(event string, listener pubsub.Listener) (cancel func(), err error) {
+ p.Lock()
+ if p.expectedEvent != "" && event == p.expectedEvent {
+ close(p.subscribed)
+ }
+ p.Unlock()
+ return p.Pubsub.Subscribe(event, listener)
+}
diff --git a/coderd/workspaces.go b/coderd/workspaces.go
index 2ac432d905ae6..dd3327d444b9e 100644
--- a/coderd/workspaces.go
+++ b/coderd/workspaces.go
@@ -641,9 +641,9 @@ func createWorkspace(
err = api.Database.InTx(func(db database.Store) error {
var (
+ prebuildsClaimer = *api.PrebuildsClaimer.Load()
workspaceID uuid.UUID
claimedWorkspace *database.Workspace
- prebuildsClaimer = *api.PrebuildsClaimer.Load()
)
// If a template preset was chosen, try claim a prebuilt workspace.
@@ -717,8 +717,7 @@ func createWorkspace(
Reason(database.BuildReasonInitiator).
Initiator(initiatorID).
ActiveVersion().
- RichParameterValues(req.RichParameterValues).
- TemplateVersionPresetID(req.TemplateVersionPresetID)
+ RichParameterValues(req.RichParameterValues)
if req.TemplateVersionID != uuid.Nil {
builder = builder.VersionID(req.TemplateVersionID)
}
@@ -726,7 +725,7 @@ func createWorkspace(
builder = builder.TemplateVersionPresetID(req.TemplateVersionPresetID)
}
if claimedWorkspace != nil {
- builder = builder.MarkPrebuildClaimedBy(owner.ID)
+ builder = builder.MarkPrebuiltWorkspaceClaim()
}
if req.EnableDynamicParameters && api.Experiments.Enabled(codersdk.ExperimentDynamicParameters) {
diff --git a/coderd/wsbuilder/wsbuilder.go b/coderd/wsbuilder/wsbuilder.go
index 942829004309c..91638c63e436f 100644
--- a/coderd/wsbuilder/wsbuilder.go
+++ b/coderd/wsbuilder/wsbuilder.go
@@ -16,6 +16,7 @@ import (
"github.com/coder/coder/v2/coderd/rbac/policy"
"github.com/coder/coder/v2/provisioner/terraform/tfparse"
"github.com/coder/coder/v2/provisionersdk"
+ sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
"github.com/google/uuid"
"github.com/sqlc-dev/pqtype"
@@ -76,9 +77,7 @@ type Builder struct {
parameterValues *[]string
templateVersionPresetParameterValues []database.TemplateVersionPresetParameter
- prebuild bool
- prebuildClaimedBy uuid.UUID
-
+ prebuiltWorkspaceBuildStage sdkproto.PrebuiltWorkspaceBuildStage
verifyNoLegacyParametersOnce bool
}
@@ -174,15 +173,17 @@ func (b Builder) RichParameterValues(p []codersdk.WorkspaceBuildParameter) Build
return b
}
+// MarkPrebuild indicates that a prebuilt workspace is being built.
func (b Builder) MarkPrebuild() Builder {
// nolint: revive
- b.prebuild = true
+ b.prebuiltWorkspaceBuildStage = sdkproto.PrebuiltWorkspaceBuildStage_CREATE
return b
}
-func (b Builder) MarkPrebuildClaimedBy(userID uuid.UUID) Builder {
+// MarkPrebuiltWorkspaceClaim indicates that a prebuilt workspace is being claimed.
+func (b Builder) MarkPrebuiltWorkspaceClaim() Builder {
// nolint: revive
- b.prebuildClaimedBy = userID
+ b.prebuiltWorkspaceBuildStage = sdkproto.PrebuiltWorkspaceBuildStage_CLAIM
return b
}
@@ -322,10 +323,9 @@ func (b *Builder) buildTx(authFunc func(action policy.Action, object rbac.Object
workspaceBuildID := uuid.New()
input, err := json.Marshal(provisionerdserver.WorkspaceProvisionJob{
- WorkspaceBuildID: workspaceBuildID,
- LogLevel: b.logLevel,
- IsPrebuild: b.prebuild,
- PrebuildClaimedByUser: b.prebuildClaimedBy,
+ WorkspaceBuildID: workspaceBuildID,
+ LogLevel: b.logLevel,
+ PrebuiltWorkspaceBuildStage: b.prebuiltWorkspaceBuildStage,
})
if err != nil {
return nil, nil, nil, BuildError{
diff --git a/codersdk/agentsdk/agentsdk.go b/codersdk/agentsdk/agentsdk.go
index 109d14b84d050..167c7e304ca4b 100644
--- a/codersdk/agentsdk/agentsdk.go
+++ b/codersdk/agentsdk/agentsdk.go
@@ -19,12 +19,15 @@ import (
"tailscale.com/tailcfg"
"cdr.dev/slog"
+ "github.com/coder/retry"
+ "github.com/coder/websocket"
+
"github.com/coder/coder/v2/agent/proto"
"github.com/coder/coder/v2/apiversion"
+ "github.com/coder/coder/v2/coderd/httpapi"
"github.com/coder/coder/v2/codersdk"
drpcsdk "github.com/coder/coder/v2/codersdk/drpc"
tailnetproto "github.com/coder/coder/v2/tailnet/proto"
- "github.com/coder/websocket"
)
// ExternalLogSourceID is the statically-defined ID of a log-source that
@@ -686,3 +689,188 @@ func LogsNotifyChannel(agentID uuid.UUID) string {
type LogsNotifyMessage struct {
CreatedAfter int64 `json:"created_after"`
}
+
+type ReinitializationReason string
+
+const (
+ ReinitializeReasonPrebuildClaimed ReinitializationReason = "prebuild_claimed"
+)
+
+type ReinitializationEvent struct {
+ WorkspaceID uuid.UUID
+ Reason ReinitializationReason `json:"reason"`
+}
+
+func PrebuildClaimedChannel(id uuid.UUID) string {
+ return fmt.Sprintf("prebuild_claimed_%s", id)
+}
+
+// WaitForReinit polls a SSE endpoint, and receives an event back under the following conditions:
+// - ping: ignored, keepalive
+// - prebuild claimed: a prebuilt workspace is claimed, so the agent must reinitialize.
+func (c *Client) WaitForReinit(ctx context.Context) (*ReinitializationEvent, error) {
+ rpcURL, err := c.SDK.URL.Parse("/api/v2/workspaceagents/me/reinit")
+ if err != nil {
+ return nil, xerrors.Errorf("parse url: %w", err)
+ }
+
+ jar, err := cookiejar.New(nil)
+ if err != nil {
+ return nil, xerrors.Errorf("create cookie jar: %w", err)
+ }
+ jar.SetCookies(rpcURL, []*http.Cookie{{
+ Name: codersdk.SessionTokenCookie,
+ Value: c.SDK.SessionToken(),
+ }})
+ httpClient := &http.Client{
+ Jar: jar,
+ Transport: c.SDK.HTTPClient.Transport,
+ }
+
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, rpcURL.String(), nil)
+ if err != nil {
+ return nil, xerrors.Errorf("build request: %w", err)
+ }
+
+ res, err := httpClient.Do(req)
+ if err != nil {
+ return nil, xerrors.Errorf("execute request: %w", err)
+ }
+ defer res.Body.Close()
+
+ if res.StatusCode != http.StatusOK {
+ return nil, codersdk.ReadBodyAsError(res)
+ }
+
+ reinitEvent, err := NewSSEAgentReinitReceiver(res.Body).Receive(ctx)
+ if err != nil {
+ return nil, xerrors.Errorf("listening for reinitialization events: %w", err)
+ }
+ return reinitEvent, nil
+}
+
+func WaitForReinitLoop(ctx context.Context, logger slog.Logger, client *Client) <-chan ReinitializationEvent {
+ reinitEvents := make(chan ReinitializationEvent)
+
+ go func() {
+ for retrier := retry.New(100*time.Millisecond, 10*time.Second); retrier.Wait(ctx); {
+ logger.Debug(ctx, "waiting for agent reinitialization instructions")
+ reinitEvent, err := client.WaitForReinit(ctx)
+ if err != nil {
+ logger.Error(ctx, "failed to wait for agent reinitialization instructions", slog.Error(err))
+ continue
+ }
+ retrier.Reset()
+ select {
+ case <-ctx.Done():
+ close(reinitEvents)
+ return
+ case reinitEvents <- *reinitEvent:
+ }
+ }
+ }()
+
+ return reinitEvents
+}
+
+func NewSSEAgentReinitTransmitter(logger slog.Logger, rw http.ResponseWriter, r *http.Request) *SSEAgentReinitTransmitter {
+ return &SSEAgentReinitTransmitter{logger: logger, rw: rw, r: r}
+}
+
+type SSEAgentReinitTransmitter struct {
+ rw http.ResponseWriter
+ r *http.Request
+ logger slog.Logger
+}
+
+var (
+ ErrTransmissionSourceClosed = xerrors.New("transmission source closed")
+ ErrTransmissionTargetClosed = xerrors.New("transmission target closed")
+)
+
+// Transmit will read from the given chan and send events for as long as:
+// * the chan remains open
+// * the context has not been canceled
+// * not timed out
+// * the connection to the receiver remains open
+func (s *SSEAgentReinitTransmitter) Transmit(ctx context.Context, reinitEvents <-chan ReinitializationEvent) error {
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ default:
+ }
+
+ sseSendEvent, sseSenderClosed, err := httpapi.ServerSentEventSender(s.rw, s.r)
+ if err != nil {
+ return xerrors.Errorf("failed to create sse transmitter: %w", err)
+ }
+
+ defer func() {
+ // Block returning until the ServerSentEventSender is closed
+ // to avoid a race condition where we might write or flush to rw after the handler returns.
+ <-sseSenderClosed
+ }()
+
+ for {
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ case <-sseSenderClosed:
+ return ErrTransmissionTargetClosed
+ case reinitEvent, ok := <-reinitEvents:
+ if !ok {
+ return ErrTransmissionSourceClosed
+ }
+ err := sseSendEvent(codersdk.ServerSentEvent{
+ Type: codersdk.ServerSentEventTypeData,
+ Data: reinitEvent,
+ })
+ if err != nil {
+ return err
+ }
+ }
+ }
+}
+
+func NewSSEAgentReinitReceiver(r io.ReadCloser) *SSEAgentReinitReceiver {
+ return &SSEAgentReinitReceiver{r: r}
+}
+
+type SSEAgentReinitReceiver struct {
+ r io.ReadCloser
+}
+
+func (s *SSEAgentReinitReceiver) Receive(ctx context.Context) (*ReinitializationEvent, error) {
+ nextEvent := codersdk.ServerSentEventReader(ctx, s.r)
+ for {
+ select {
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ default:
+ }
+
+ sse, err := nextEvent()
+ switch {
+ case err != nil:
+ return nil, xerrors.Errorf("failed to read server-sent event: %w", err)
+ case sse.Type == codersdk.ServerSentEventTypeError:
+ return nil, xerrors.Errorf("unexpected server sent event type error")
+ case sse.Type == codersdk.ServerSentEventTypePing:
+ continue
+ case sse.Type != codersdk.ServerSentEventTypeData:
+ return nil, xerrors.Errorf("unexpected server sent event type: %s", sse.Type)
+ }
+
+ // At this point we know that the sent event is of type codersdk.ServerSentEventTypeData
+ var reinitEvent ReinitializationEvent
+ b, ok := sse.Data.([]byte)
+ if !ok {
+ return nil, xerrors.Errorf("expected data as []byte, got %T", sse.Data)
+ }
+ err = json.Unmarshal(b, &reinitEvent)
+ if err != nil {
+ return nil, xerrors.Errorf("unmarshal reinit response: %w", err)
+ }
+ return &reinitEvent, nil
+ }
+}
diff --git a/codersdk/agentsdk/agentsdk_test.go b/codersdk/agentsdk/agentsdk_test.go
new file mode 100644
index 0000000000000..8ad2d69be0b98
--- /dev/null
+++ b/codersdk/agentsdk/agentsdk_test.go
@@ -0,0 +1,122 @@
+package agentsdk_test
+
+import (
+ "context"
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/google/uuid"
+ "github.com/stretchr/testify/require"
+
+ "cdr.dev/slog/sloggers/slogtest"
+ "github.com/coder/coder/v2/codersdk/agentsdk"
+ "github.com/coder/coder/v2/testutil"
+)
+
+func TestStreamAgentReinitEvents(t *testing.T) {
+ t.Parallel()
+
+ t.Run("transmitted events are received", func(t *testing.T) {
+ t.Parallel()
+
+ eventToSend := agentsdk.ReinitializationEvent{
+ WorkspaceID: uuid.New(),
+ Reason: agentsdk.ReinitializeReasonPrebuildClaimed,
+ }
+
+ events := make(chan agentsdk.ReinitializationEvent, 1)
+ events <- eventToSend
+
+ transmitCtx := testutil.Context(t, testutil.WaitShort)
+ transmitErrCh := make(chan error, 1)
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ transmitter := agentsdk.NewSSEAgentReinitTransmitter(slogtest.Make(t, nil), w, r)
+ transmitErrCh <- transmitter.Transmit(transmitCtx, events)
+ }))
+ defer srv.Close()
+
+ requestCtx := testutil.Context(t, testutil.WaitShort)
+ req, err := http.NewRequestWithContext(requestCtx, "GET", srv.URL, nil)
+ require.NoError(t, err)
+ resp, err := http.DefaultClient.Do(req)
+ require.NoError(t, err)
+ defer resp.Body.Close()
+
+ receiveCtx := testutil.Context(t, testutil.WaitShort)
+ receiver := agentsdk.NewSSEAgentReinitReceiver(resp.Body)
+ sentEvent, receiveErr := receiver.Receive(receiveCtx)
+ require.Nil(t, receiveErr)
+ require.Equal(t, eventToSend, *sentEvent)
+ })
+
+ t.Run("doesn't transmit events if the transmitter context is canceled", func(t *testing.T) {
+ t.Parallel()
+
+ eventToSend := agentsdk.ReinitializationEvent{
+ WorkspaceID: uuid.New(),
+ Reason: agentsdk.ReinitializeReasonPrebuildClaimed,
+ }
+
+ events := make(chan agentsdk.ReinitializationEvent, 1)
+ events <- eventToSend
+
+ transmitCtx, cancelTransmit := context.WithCancel(testutil.Context(t, testutil.WaitShort))
+ cancelTransmit()
+ transmitErrCh := make(chan error, 1)
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ transmitter := agentsdk.NewSSEAgentReinitTransmitter(slogtest.Make(t, nil), w, r)
+ transmitErrCh <- transmitter.Transmit(transmitCtx, events)
+ }))
+
+ defer srv.Close()
+
+ requestCtx := testutil.Context(t, testutil.WaitShort)
+ req, err := http.NewRequestWithContext(requestCtx, "GET", srv.URL, nil)
+ require.NoError(t, err)
+ resp, err := http.DefaultClient.Do(req)
+ require.NoError(t, err)
+ defer resp.Body.Close()
+
+ receiveCtx := testutil.Context(t, testutil.WaitShort)
+ receiver := agentsdk.NewSSEAgentReinitReceiver(resp.Body)
+ sentEvent, receiveErr := receiver.Receive(receiveCtx)
+ require.Nil(t, sentEvent)
+ require.ErrorIs(t, receiveErr, io.EOF)
+ })
+
+ t.Run("does not receive events if the receiver context is canceled", func(t *testing.T) {
+ t.Parallel()
+
+ eventToSend := agentsdk.ReinitializationEvent{
+ WorkspaceID: uuid.New(),
+ Reason: agentsdk.ReinitializeReasonPrebuildClaimed,
+ }
+
+ events := make(chan agentsdk.ReinitializationEvent, 1)
+ events <- eventToSend
+
+ transmitCtx := testutil.Context(t, testutil.WaitShort)
+ transmitErrCh := make(chan error, 1)
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ transmitter := agentsdk.NewSSEAgentReinitTransmitter(slogtest.Make(t, nil), w, r)
+ transmitErrCh <- transmitter.Transmit(transmitCtx, events)
+ }))
+ defer srv.Close()
+
+ requestCtx := testutil.Context(t, testutil.WaitShort)
+ req, err := http.NewRequestWithContext(requestCtx, "GET", srv.URL, nil)
+ require.NoError(t, err)
+ resp, err := http.DefaultClient.Do(req)
+ require.NoError(t, err)
+ defer resp.Body.Close()
+
+ receiveCtx, cancelReceive := context.WithCancel(context.Background())
+ cancelReceive()
+ receiver := agentsdk.NewSSEAgentReinitReceiver(resp.Body)
+ sentEvent, receiveErr := receiver.Receive(receiveCtx)
+ require.Nil(t, sentEvent)
+ require.ErrorIs(t, receiveErr, context.Canceled)
+ })
+}
diff --git a/codersdk/client.go b/codersdk/client.go
index 8ab5a289b2cf5..4492066785d6f 100644
--- a/codersdk/client.go
+++ b/codersdk/client.go
@@ -631,7 +631,7 @@ func (h *HeaderTransport) RoundTrip(req *http.Request) (*http.Response, error) {
}
}
if h.Transport == nil {
- h.Transport = http.DefaultTransport
+ return http.DefaultTransport.RoundTrip(req)
}
return h.Transport.RoundTrip(req)
}
diff --git a/docs/admin/templates/extending-templates/prebuilt-workspaces.md b/docs/admin/templates/extending-templates/prebuilt-workspaces.md
new file mode 100644
index 0000000000000..894a2a219a9cf
--- /dev/null
+++ b/docs/admin/templates/extending-templates/prebuilt-workspaces.md
@@ -0,0 +1,209 @@
+# Prebuilt workspaces
+
+Prebuilt workspaces allow template administrators to improve the developer experience by reducing workspace
+creation time with an automatically maintained pool of ready-to-use workspaces for specific parameter presets.
+
+The template administrator configures a template to provision prebuilt workspaces in the background, and then when a developer creates
+a new workspace that matches the preset, Coder assigns them an existing prebuilt instance.
+Prebuilt workspaces significantly reduce wait times, especially for templates with complex provisioning or lengthy startup procedures.
+
+Prebuilt workspaces are:
+
+- Created and maintained automatically by Coder to match your specified preset configurations.
+- Claimed transparently when developers create workspaces.
+- Monitored and replaced automatically to maintain your desired pool size.
+
+## Relationship to workspace presets
+
+Prebuilt workspaces are tightly integrated with [workspace presets](./parameters.md#workspace-presets-beta):
+
+1. Each prebuilt workspace is associated with a specific template preset.
+1. The preset must define all required parameters needed to build the workspace.
+1. The preset parameters define the base configuration and are immutable once a prebuilt workspace is provisioned.
+1. Parameters that are not defined in the preset can still be customized by users when they claim a workspace.
+
+## Prerequisites
+
+- [**Premium license**](../../licensing/index.md)
+- **Compatible Terraform provider**: Use `coder/coder` Terraform provider `>= 2.4.0`.
+- **Feature flag**: Enable the `workspace-prebuilds` [experiment](../../../reference/cli/server.md#--experiments).
+
+## Enable prebuilt workspaces for template presets
+
+In your template, add a `prebuilds` block within a `coder_workspace_preset` definition to identify the number of prebuilt
+instances your Coder deployment should maintain:
+
+ ```hcl
+ data "coder_workspace_preset" "goland" {
+ name = "GoLand: Large"
+ parameters = {
+ jetbrains_ide = "GO"
+ cpus = 8
+ memory = 16
+ }
+ prebuilds {
+ instances = 3 # Number of prebuilt workspaces to maintain
+ }
+ }
+ ```
+
+After you publish a new template version, Coder will automatically provision and maintain prebuilt workspaces through an
+internal reconciliation loop (similar to Kubernetes) to ensure the defined `instances` count are running.
+
+## Prebuilt workspace lifecycle
+
+Prebuilt workspaces follow a specific lifecycle from creation through eligibility to claiming.
+
+1. After you configure a preset with prebuilds and publish the template, Coder provisions the prebuilt workspace(s).
+
+ 1. Coder automatically creates the defined `instances` count of prebuilt workspaces.
+ 1. Each new prebuilt workspace is initially owned by an unprivileged system pseudo-user named `prebuilds`.
+ - The `prebuilds` user belongs to the `Everyone` group (you can add it to additional groups if needed).
+ 1. Each prebuilt workspace receives a randomly generated name for identification.
+ 1. The workspace is provisioned like a regular workspace; only its ownership distinguishes it as a prebuilt workspace.
+
+1. Prebuilt workspaces start up and become eligible to be claimed by a developer.
+
+ Before a prebuilt workspace is available to users:
+
+ 1. The workspace is provisioned.
+ 1. The agent starts up and connects to coderd.
+ 1. The agent starts its bootstrap procedures and completes its startup scripts.
+ 1. The agent reports `ready` status.
+
+ After the agent reports `ready`, the prebuilt workspace considered eligible to be claimed.
+
+ Prebuilt workspaces that fail during provisioning are retried with a backoff to prevent transient failures.
+
+1. When a developer requests a new workspace, the claiming process occurs:
+
+ 1. Developer selects a template and preset that has prebuilt workspaces configured.
+ 1. If an eligible prebuilt workspace exists, ownership transfers from the `prebuilds` user to the requesting user.
+ 1. The workspace name changes to the user's requested name.
+ 1. `terraform apply` is executed using the new ownership details, which may affect the [`coder_workspace`](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/workspace) and
+ [`coder_workspace_owner`](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/workspace_owner)
+ datasources (see [Preventing resource replacement](#preventing-resource-replacement) for further considerations).
+
+ The developer doesn't see the claiming process — the workspace will just be ready faster than usual.
+
+You can view available prebuilt workspaces in the **Workspaces** view in the Coder dashboard:
+
+
+_Note the search term `owner:prebuilds`._
+
+### Template updates and the prebuilt workspace lifecycle
+
+Prebuilt workspaces are not updated after they are provisioned.
+
+When a template's active version is updated:
+
+1. Prebuilt workspaces for old versions are automatically deleted.
+1. New prebuilt workspaces are created for the active template version.
+1. If dependencies change (e.g., an [AMI](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html) update) without a template version change:
+ - You may delete the existing prebuilt workspaces manually.
+ - Coder will automatically create new prebuilt workspaces with the updated dependencies.
+
+The system always maintains the desired number of prebuilt workspaces for the active template version.
+
+## Administration and troubleshooting
+
+### Managing resource quotas
+
+Prebuilt workspaces can be used in conjunction with [resource quotas](../../users/quotas.md).
+Because unclaimed prebuilt workspaces are owned by the `prebuilds` user, you can:
+
+1. Configure quotas for any group that includes this user.
+1. Set appropriate limits to balance prebuilt workspace availability with resource constraints.
+
+If a quota is exceeded, the prebuilt workspace will fail provisioning the same way other workspaces do.
+
+### Template configuration best practices
+
+#### Preventing resource replacement
+
+When a prebuilt workspace is claimed, another `terraform apply` run occurs with new values for the workspace owner and name.
+
+This can cause issues in the following scenario:
+
+1. The workspace is initially created with values from the `prebuilds` user and a random name.
+1. After claiming, various workspace properties change (ownership, name, and potentially other values), which Terraform sees as configuration drift.
+1. If these values are used in immutable fields, Terraform will destroy and recreate the resource, eliminating the benefit of prebuilds.
+
+For example, when these values are used in immutable fields like the AWS instance `user_data`, you'll see resource replacement during claiming:
+
+
+
+To prevent this, add a `lifecycle` block with `ignore_changes`:
+
+```hcl
+resource "docker_container" "workspace" {
+ lifecycle {
+ ignore_changes = all
+ }
+
+ count = data.coder_workspace.me.start_count
+ name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
+ ...
+}
+```
+
+For more targeted control, specify which attributes to ignore:
+
+```hcl
+resource "docker_container" "workspace" {
+ lifecycle {
+ ignore_changes = [name]
+ }
+
+ count = data.coder_workspace.me.start_count
+ name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
+ ...
+}
+```
+
+Learn more about `ignore_changes` in the [Terraform documentation](https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle#ignore_changes).
+
+_A note on "immutable" attributes: Terraform providers may specify `ForceNew` on their resources' attributes. Any change
+to these attributes require the replacement (destruction and recreation) of the managed resource instance, rather than an in-place update.
+For example, the [`ami`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance#ami-1) attribute on the `aws_instance` resource
+has [`ForceNew`](https://github.com/hashicorp/terraform-provider-aws/blob/main/internal/service/ec2/ec2_instance.go#L75-L81) set,
+since the AMI cannot be changed in-place._
+
+### Current limitations
+
+The prebuilt workspaces feature has these current limitations:
+
+- **Organizations**
+
+ Prebuilt workspaces can only be used with the default organization.
+
+ [coder/internal#364](https://github.com/coder/internal/issues/364)
+
+- **Autoscaling**
+
+ Prebuilt workspaces remain running until claimed. There's no automated mechanism to reduce instances during off-hours.
+
+ [coder/internal#312](https://github.com/coder/internal/issues/312)
+
+### Monitoring and observability
+
+#### Available metrics
+
+Coder provides several metrics to monitor your prebuilt workspaces:
+
+- `coderd_prebuilt_workspaces_created_total` (counter): Total number of prebuilt workspaces created to meet the desired instance count.
+- `coderd_prebuilt_workspaces_failed_total` (counter): Total number of prebuilt workspaces that failed to build.
+- `coderd_prebuilt_workspaces_claimed_total` (counter): Total number of prebuilt workspaces claimed by users.
+- `coderd_prebuilt_workspaces_desired` (gauge): Target number of prebuilt workspaces that should be available.
+- `coderd_prebuilt_workspaces_running` (gauge): Current number of prebuilt workspaces in a `running` state.
+- `coderd_prebuilt_workspaces_eligible` (gauge): Current number of prebuilt workspaces eligible to be claimed.
+
+#### Logs
+
+Search for `coderd.prebuilds:` in your logs to track the reconciliation loop's behavior.
+
+These logs provide information about:
+
+1. Creation and deletion attempts for prebuilt workspaces.
+1. Backoff events after failed builds.
+1. Claiming operations.
diff --git a/docs/images/admin/templates/extend-templates/prebuilt/prebuilt-workspaces.png b/docs/images/admin/templates/extend-templates/prebuilt/prebuilt-workspaces.png
new file mode 100644
index 0000000000000..59d11d6ed7622
Binary files /dev/null and b/docs/images/admin/templates/extend-templates/prebuilt/prebuilt-workspaces.png differ
diff --git a/docs/images/admin/templates/extend-templates/prebuilt/replacement-notification.png b/docs/images/admin/templates/extend-templates/prebuilt/replacement-notification.png
new file mode 100644
index 0000000000000..899c8eaf5a5ea
Binary files /dev/null and b/docs/images/admin/templates/extend-templates/prebuilt/replacement-notification.png differ
diff --git a/docs/manifest.json b/docs/manifest.json
index ea1d19561593f..d3a8c64ad2562 100644
--- a/docs/manifest.json
+++ b/docs/manifest.json
@@ -416,6 +416,12 @@
"description": "Use parameters to customize workspaces at build",
"path": "./admin/templates/extending-templates/parameters.md"
},
+ {
+ "title": "Prebuilt workspaces",
+ "description": "Pre-provision a ready-to-deploy workspace with a defined set of parameters",
+ "path": "./admin/templates/extending-templates/prebuilt-workspaces.md",
+ "state": ["premium", "beta"]
+ },
{
"title": "Icons",
"description": "Customize your template with built-in icons",
diff --git a/docs/reference/api/agents.md b/docs/reference/api/agents.md
index 853cb67e38bfd..81dd9cf2eb88a 100644
--- a/docs/reference/api/agents.md
+++ b/docs/reference/api/agents.md
@@ -470,6 +470,38 @@ curl -X PATCH http://coder-server:8080/api/v2/workspaceagents/me/logs \
To perform this operation, you must be authenticated. [Learn more](authentication.md).
+## Get workspace agent reinitialization
+
+### Code samples
+
+```shell
+# Example request using curl
+curl -X GET http://coder-server:8080/api/v2/workspaceagents/me/reinit \
+ -H 'Accept: application/json' \
+ -H 'Coder-Session-Token: API_KEY'
+```
+
+`GET /workspaceagents/me/reinit`
+
+### Example responses
+
+> 200 Response
+
+```json
+{
+ "reason": "prebuild_claimed",
+ "workspaceID": "string"
+}
+```
+
+### Responses
+
+| Status | Meaning | Description | Schema |
+|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------------------|
+| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [agentsdk.ReinitializationEvent](schemas.md#agentsdkreinitializationevent) |
+
+To perform this operation, you must be authenticated. [Learn more](authentication.md).
+
## Get workspace agent by ID
### Code samples
diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md
index e2ba1373aa613..6caf9e3384546 100644
--- a/docs/reference/api/schemas.md
+++ b/docs/reference/api/schemas.md
@@ -182,6 +182,36 @@
| `icon` | string | false | | |
| `id` | string | false | | ID is a unique identifier for the log source. It is scoped to a workspace agent, and can be statically defined inside code to prevent duplicate sources from being created for the same agent. |
+## agentsdk.ReinitializationEvent
+
+```json
+{
+ "reason": "prebuild_claimed",
+ "workspaceID": "string"
+}
+```
+
+### Properties
+
+| Name | Type | Required | Restrictions | Description |
+|---------------|--------------------------------------------------------------------|----------|--------------|-------------|
+| `reason` | [agentsdk.ReinitializationReason](#agentsdkreinitializationreason) | false | | |
+| `workspaceID` | string | false | | |
+
+## agentsdk.ReinitializationReason
+
+```json
+"prebuild_claimed"
+```
+
+### Properties
+
+#### Enumerated Values
+
+| Value |
+|--------------------|
+| `prebuild_claimed` |
+
## coderd.SCIMUser
```json
diff --git a/enterprise/coderd/coderd.go b/enterprise/coderd/coderd.go
index 8b473e8168ffa..f46848812a69e 100644
--- a/enterprise/coderd/coderd.go
+++ b/enterprise/coderd/coderd.go
@@ -1165,6 +1165,6 @@ func (api *API) setupPrebuilds(featureEnabled bool) (agplprebuilds.Reconciliatio
}
reconciler := prebuilds.NewStoreReconciler(api.Database, api.Pubsub, api.DeploymentValues.Prebuilds,
- api.Logger.Named("prebuilds"), quartz.NewReal(), api.PrometheusRegistry)
+ api.Logger.Named("prebuilds"), quartz.NewReal(), api.PrometheusRegistry, api.NotificationsEnqueuer)
return reconciler, prebuilds.NewEnterpriseClaimer(api.Database)
}
diff --git a/enterprise/coderd/prebuilds/claim_test.go b/enterprise/coderd/prebuilds/claim_test.go
index ad31d2b4eff1b..5a18600a84602 100644
--- a/enterprise/coderd/prebuilds/claim_test.go
+++ b/enterprise/coderd/prebuilds/claim_test.go
@@ -147,7 +147,7 @@ func TestClaimPrebuild(t *testing.T) {
EntitlementsUpdateInterval: time.Second,
})
- reconciler := prebuilds.NewStoreReconciler(spy, pubsub, codersdk.PrebuildsConfig{}, logger, quartz.NewMock(t), prometheus.NewRegistry())
+ reconciler := prebuilds.NewStoreReconciler(spy, pubsub, codersdk.PrebuildsConfig{}, logger, quartz.NewMock(t), prometheus.NewRegistry(), newNoopEnqueuer())
var claimer agplprebuilds.Claimer = prebuilds.NewEnterpriseClaimer(spy)
api.AGPL.PrebuildsClaimer.Store(&claimer)
diff --git a/enterprise/coderd/prebuilds/metricscollector.go b/enterprise/coderd/prebuilds/metricscollector.go
index 76089c025243d..7a7734b6f8093 100644
--- a/enterprise/coderd/prebuilds/metricscollector.go
+++ b/enterprise/coderd/prebuilds/metricscollector.go
@@ -3,6 +3,7 @@ package prebuilds
import (
"context"
"fmt"
+ "sync"
"sync/atomic"
"time"
@@ -16,50 +17,73 @@ import (
"github.com/coder/coder/v2/coderd/prebuilds"
)
+const (
+ namespace = "coderd_prebuilt_workspaces_"
+
+ MetricCreatedCount = namespace + "created_total"
+ MetricFailedCount = namespace + "failed_total"
+ MetricClaimedCount = namespace + "claimed_total"
+ MetricResourceReplacementsCount = namespace + "resource_replacements_total"
+ MetricDesiredGauge = namespace + "desired"
+ MetricRunningGauge = namespace + "running"
+ MetricEligibleGauge = namespace + "eligible"
+ MetricLastUpdatedGauge = namespace + "metrics_last_updated"
+)
+
var (
labels = []string{"template_name", "preset_name", "organization_name"}
createdPrebuildsDesc = prometheus.NewDesc(
- "coderd_prebuilt_workspaces_created_total",
+ MetricCreatedCount,
"Total number of prebuilt workspaces that have been created to meet the desired instance count of each "+
"template preset.",
labels,
nil,
)
failedPrebuildsDesc = prometheus.NewDesc(
- "coderd_prebuilt_workspaces_failed_total",
+ MetricFailedCount,
"Total number of prebuilt workspaces that failed to build.",
labels,
nil,
)
claimedPrebuildsDesc = prometheus.NewDesc(
- "coderd_prebuilt_workspaces_claimed_total",
+ MetricClaimedCount,
"Total number of prebuilt workspaces which were claimed by users. Claiming refers to creating a workspace "+
"with a preset selected for which eligible prebuilt workspaces are available and one is reassigned to a user.",
labels,
nil,
)
+ resourceReplacementsDesc = prometheus.NewDesc(
+ MetricResourceReplacementsCount,
+ "Total number of prebuilt workspaces whose resource(s) got replaced upon being claimed. "+
+ "In Terraform, drift on immutable attributes results in resource replacement. "+
+ "This represents a worst-case scenario for prebuilt workspaces because the pre-provisioned resource "+
+ "would have been recreated when claiming, thus obviating the point of pre-provisioning. "+
+ "See https://coder.com/docs/admin/templates/extending-templates/prebuilt-workspaces#preventing-resource-replacement",
+ labels,
+ nil,
+ )
desiredPrebuildsDesc = prometheus.NewDesc(
- "coderd_prebuilt_workspaces_desired",
+ MetricDesiredGauge,
"Target number of prebuilt workspaces that should be available for each template preset.",
labels,
nil,
)
runningPrebuildsDesc = prometheus.NewDesc(
- "coderd_prebuilt_workspaces_running",
+ MetricRunningGauge,
"Current number of prebuilt workspaces that are in a running state. These workspaces have started "+
"successfully but may not yet be claimable by users (see coderd_prebuilt_workspaces_eligible).",
labels,
nil,
)
eligiblePrebuildsDesc = prometheus.NewDesc(
- "coderd_prebuilt_workspaces_eligible",
+ MetricEligibleGauge,
"Current number of prebuilt workspaces that are eligible to be claimed by users. These are workspaces that "+
"have completed their build process with their agent reporting 'ready' status.",
labels,
nil,
)
lastUpdateDesc = prometheus.NewDesc(
- "coderd_prebuilt_workspaces_metrics_last_updated",
+ MetricLastUpdatedGauge,
"The unix timestamp when the metrics related to prebuilt workspaces were last updated; these metrics are cached.",
[]string{},
nil,
@@ -77,6 +101,9 @@ type MetricsCollector struct {
snapshotter prebuilds.StateSnapshotter
latestState atomic.Pointer[metricsState]
+
+ replacementsCounter map[replacementKey]float64
+ replacementsCounterMu sync.Mutex
}
var _ prometheus.Collector = new(MetricsCollector)
@@ -84,9 +111,10 @@ var _ prometheus.Collector = new(MetricsCollector)
func NewMetricsCollector(db database.Store, logger slog.Logger, snapshotter prebuilds.StateSnapshotter) *MetricsCollector {
log := logger.Named("prebuilds_metrics_collector")
return &MetricsCollector{
- database: db,
- logger: log,
- snapshotter: snapshotter,
+ database: db,
+ logger: log,
+ snapshotter: snapshotter,
+ replacementsCounter: make(map[replacementKey]float64),
}
}
@@ -94,6 +122,7 @@ func (*MetricsCollector) Describe(descCh chan<- *prometheus.Desc) {
descCh <- createdPrebuildsDesc
descCh <- failedPrebuildsDesc
descCh <- claimedPrebuildsDesc
+ descCh <- resourceReplacementsDesc
descCh <- desiredPrebuildsDesc
descCh <- runningPrebuildsDesc
descCh <- eligiblePrebuildsDesc
@@ -117,11 +146,21 @@ func (mc *MetricsCollector) Collect(metricsCh chan<- prometheus.Metric) {
metricsCh <- prometheus.MustNewConstMetric(claimedPrebuildsDesc, prometheus.CounterValue, float64(metric.ClaimedCount), metric.TemplateName, metric.PresetName, metric.OrganizationName)
}
+ mc.replacementsCounterMu.Lock()
+ for key, val := range mc.replacementsCounter {
+ metricsCh <- prometheus.MustNewConstMetric(resourceReplacementsDesc, prometheus.CounterValue, val, key.templateName, key.presetName, key.orgName)
+ }
+ mc.replacementsCounterMu.Unlock()
+
for _, preset := range currentState.snapshot.Presets {
if !preset.UsingActiveVersion {
continue
}
+ if preset.Deleted {
+ continue
+ }
+
presetSnapshot, err := currentState.snapshot.FilterByPreset(preset.ID)
if err != nil {
mc.logger.Error(context.Background(), "failed to filter by preset", slog.Error(err))
@@ -187,3 +226,24 @@ func (mc *MetricsCollector) UpdateState(ctx context.Context, timeout time.Durati
})
return nil
}
+
+type replacementKey struct {
+ orgName, templateName, presetName string
+}
+
+func (k replacementKey) String() string {
+ return fmt.Sprintf("%s:%s:%s", k.orgName, k.templateName, k.presetName)
+}
+
+func (mc *MetricsCollector) trackResourceReplacement(orgName, templateName, presetName string) {
+ mc.replacementsCounterMu.Lock()
+ defer mc.replacementsCounterMu.Unlock()
+
+ key := replacementKey{orgName: orgName, templateName: templateName, presetName: presetName}
+
+ // We only track _that_ a resource replacement occurred, not how many.
+ // Just one is enough to ruin a prebuild, but we can't know apriori which replacement would cause this.
+ // For example, say we have 2 replacements: a docker_container and a null_resource; we don't know which one might
+ // cause an issue (or indeed if either would), so we just track the replacement.
+ mc.replacementsCounter[key]++
+}
diff --git a/enterprise/coderd/prebuilds/metricscollector_test.go b/enterprise/coderd/prebuilds/metricscollector_test.go
index de3f5d017f715..dce9e07dd110f 100644
--- a/enterprise/coderd/prebuilds/metricscollector_test.go
+++ b/enterprise/coderd/prebuilds/metricscollector_test.go
@@ -19,6 +19,7 @@ import (
"github.com/coder/coder/v2/coderd/database/dbauthz"
"github.com/coder/coder/v2/coderd/database/dbgen"
"github.com/coder/coder/v2/coderd/database/dbtestutil"
+ "github.com/coder/coder/v2/coderd/database/dbtime"
agplprebuilds "github.com/coder/coder/v2/coderd/prebuilds"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/enterprise/coderd/prebuilds"
@@ -57,12 +58,12 @@ func TestMetricsCollector(t *testing.T) {
initiatorIDs: []uuid.UUID{agplprebuilds.SystemUserID},
ownerIDs: []uuid.UUID{agplprebuilds.SystemUserID},
metrics: []metricCheck{
- {"coderd_prebuilt_workspaces_created_total", ptr.To(1.0), true},
- {"coderd_prebuilt_workspaces_claimed_total", ptr.To(0.0), true},
- {"coderd_prebuilt_workspaces_failed_total", ptr.To(0.0), true},
- {"coderd_prebuilt_workspaces_desired", ptr.To(1.0), false},
- {"coderd_prebuilt_workspaces_running", ptr.To(0.0), false},
- {"coderd_prebuilt_workspaces_eligible", ptr.To(0.0), false},
+ {prebuilds.MetricCreatedCount, ptr.To(1.0), true},
+ {prebuilds.MetricClaimedCount, ptr.To(0.0), true},
+ {prebuilds.MetricFailedCount, ptr.To(0.0), true},
+ {prebuilds.MetricDesiredGauge, ptr.To(1.0), false},
+ {prebuilds.MetricRunningGauge, ptr.To(0.0), false},
+ {prebuilds.MetricEligibleGauge, ptr.To(0.0), false},
},
templateDeleted: []bool{false},
eligible: []bool{false},
@@ -74,12 +75,12 @@ func TestMetricsCollector(t *testing.T) {
initiatorIDs: []uuid.UUID{agplprebuilds.SystemUserID},
ownerIDs: []uuid.UUID{agplprebuilds.SystemUserID},
metrics: []metricCheck{
- {"coderd_prebuilt_workspaces_created_total", ptr.To(1.0), true},
- {"coderd_prebuilt_workspaces_claimed_total", ptr.To(0.0), true},
- {"coderd_prebuilt_workspaces_failed_total", ptr.To(0.0), true},
- {"coderd_prebuilt_workspaces_desired", ptr.To(1.0), false},
- {"coderd_prebuilt_workspaces_running", ptr.To(1.0), false},
- {"coderd_prebuilt_workspaces_eligible", ptr.To(0.0), false},
+ {prebuilds.MetricCreatedCount, ptr.To(1.0), true},
+ {prebuilds.MetricClaimedCount, ptr.To(0.0), true},
+ {prebuilds.MetricFailedCount, ptr.To(0.0), true},
+ {prebuilds.MetricDesiredGauge, ptr.To(1.0), false},
+ {prebuilds.MetricRunningGauge, ptr.To(1.0), false},
+ {prebuilds.MetricEligibleGauge, ptr.To(0.0), false},
},
templateDeleted: []bool{false},
eligible: []bool{false},
@@ -91,11 +92,11 @@ func TestMetricsCollector(t *testing.T) {
initiatorIDs: []uuid.UUID{agplprebuilds.SystemUserID},
ownerIDs: []uuid.UUID{agplprebuilds.SystemUserID, uuid.New()},
metrics: []metricCheck{
- {"coderd_prebuilt_workspaces_created_total", ptr.To(1.0), true},
- {"coderd_prebuilt_workspaces_failed_total", ptr.To(1.0), true},
- {"coderd_prebuilt_workspaces_desired", ptr.To(1.0), false},
- {"coderd_prebuilt_workspaces_running", ptr.To(0.0), false},
- {"coderd_prebuilt_workspaces_eligible", ptr.To(0.0), false},
+ {prebuilds.MetricCreatedCount, ptr.To(1.0), true},
+ {prebuilds.MetricFailedCount, ptr.To(1.0), true},
+ {prebuilds.MetricDesiredGauge, ptr.To(1.0), false},
+ {prebuilds.MetricRunningGauge, ptr.To(0.0), false},
+ {prebuilds.MetricEligibleGauge, ptr.To(0.0), false},
},
templateDeleted: []bool{false},
eligible: []bool{false},
@@ -107,12 +108,12 @@ func TestMetricsCollector(t *testing.T) {
initiatorIDs: []uuid.UUID{agplprebuilds.SystemUserID},
ownerIDs: []uuid.UUID{agplprebuilds.SystemUserID},
metrics: []metricCheck{
- {"coderd_prebuilt_workspaces_created_total", ptr.To(1.0), true},
- {"coderd_prebuilt_workspaces_claimed_total", ptr.To(0.0), true},
- {"coderd_prebuilt_workspaces_failed_total", ptr.To(0.0), true},
- {"coderd_prebuilt_workspaces_desired", ptr.To(1.0), false},
- {"coderd_prebuilt_workspaces_running", ptr.To(1.0), false},
- {"coderd_prebuilt_workspaces_eligible", ptr.To(1.0), false},
+ {prebuilds.MetricCreatedCount, ptr.To(1.0), true},
+ {prebuilds.MetricClaimedCount, ptr.To(0.0), true},
+ {prebuilds.MetricFailedCount, ptr.To(0.0), true},
+ {prebuilds.MetricDesiredGauge, ptr.To(1.0), false},
+ {prebuilds.MetricRunningGauge, ptr.To(1.0), false},
+ {prebuilds.MetricEligibleGauge, ptr.To(1.0), false},
},
templateDeleted: []bool{false},
eligible: []bool{true},
@@ -124,12 +125,12 @@ func TestMetricsCollector(t *testing.T) {
initiatorIDs: []uuid.UUID{agplprebuilds.SystemUserID},
ownerIDs: []uuid.UUID{agplprebuilds.SystemUserID},
metrics: []metricCheck{
- {"coderd_prebuilt_workspaces_created_total", ptr.To(1.0), true},
- {"coderd_prebuilt_workspaces_claimed_total", ptr.To(0.0), true},
- {"coderd_prebuilt_workspaces_failed_total", ptr.To(0.0), true},
- {"coderd_prebuilt_workspaces_desired", ptr.To(1.0), false},
- {"coderd_prebuilt_workspaces_running", ptr.To(1.0), false},
- {"coderd_prebuilt_workspaces_eligible", ptr.To(0.0), false},
+ {prebuilds.MetricCreatedCount, ptr.To(1.0), true},
+ {prebuilds.MetricClaimedCount, ptr.To(0.0), true},
+ {prebuilds.MetricFailedCount, ptr.To(0.0), true},
+ {prebuilds.MetricDesiredGauge, ptr.To(1.0), false},
+ {prebuilds.MetricRunningGauge, ptr.To(1.0), false},
+ {prebuilds.MetricEligibleGauge, ptr.To(0.0), false},
},
templateDeleted: []bool{false},
eligible: []bool{false},
@@ -141,11 +142,11 @@ func TestMetricsCollector(t *testing.T) {
initiatorIDs: []uuid.UUID{agplprebuilds.SystemUserID},
ownerIDs: []uuid.UUID{uuid.New()},
metrics: []metricCheck{
- {"coderd_prebuilt_workspaces_created_total", ptr.To(1.0), true},
- {"coderd_prebuilt_workspaces_claimed_total", ptr.To(1.0), true},
- {"coderd_prebuilt_workspaces_desired", ptr.To(1.0), false},
- {"coderd_prebuilt_workspaces_running", ptr.To(0.0), false},
- {"coderd_prebuilt_workspaces_eligible", ptr.To(0.0), false},
+ {prebuilds.MetricCreatedCount, ptr.To(1.0), true},
+ {prebuilds.MetricClaimedCount, ptr.To(1.0), true},
+ {prebuilds.MetricDesiredGauge, ptr.To(1.0), false},
+ {prebuilds.MetricRunningGauge, ptr.To(0.0), false},
+ {prebuilds.MetricEligibleGauge, ptr.To(0.0), false},
},
templateDeleted: []bool{false},
eligible: []bool{false},
@@ -157,35 +158,20 @@ func TestMetricsCollector(t *testing.T) {
initiatorIDs: []uuid.UUID{uuid.New()},
ownerIDs: []uuid.UUID{uuid.New()},
metrics: []metricCheck{
- {"coderd_prebuilt_workspaces_desired", ptr.To(1.0), false},
- {"coderd_prebuilt_workspaces_running", ptr.To(0.0), false},
- {"coderd_prebuilt_workspaces_eligible", ptr.To(0.0), false},
+ {prebuilds.MetricDesiredGauge, ptr.To(1.0), false},
+ {prebuilds.MetricRunningGauge, ptr.To(0.0), false},
+ {prebuilds.MetricEligibleGauge, ptr.To(0.0), false},
},
templateDeleted: []bool{false},
eligible: []bool{false},
},
{
- name: "deleted templates never desire prebuilds",
- transitions: allTransitions,
- jobStatuses: allJobStatuses,
- initiatorIDs: []uuid.UUID{agplprebuilds.SystemUserID},
- ownerIDs: []uuid.UUID{agplprebuilds.SystemUserID, uuid.New()},
- metrics: []metricCheck{
- {"coderd_prebuilt_workspaces_desired", ptr.To(0.0), false},
- },
- templateDeleted: []bool{true},
- eligible: []bool{false},
- },
- {
- name: "running prebuilds for deleted templates are still counted, so that they can be deleted",
- transitions: []database.WorkspaceTransition{database.WorkspaceTransitionStart},
- jobStatuses: []database.ProvisionerJobStatus{database.ProvisionerJobStatusSucceeded},
- initiatorIDs: []uuid.UUID{agplprebuilds.SystemUserID},
- ownerIDs: []uuid.UUID{agplprebuilds.SystemUserID},
- metrics: []metricCheck{
- {"coderd_prebuilt_workspaces_running", ptr.To(1.0), false},
- {"coderd_prebuilt_workspaces_eligible", ptr.To(0.0), false},
- },
+ name: "deleted templates should not be included in exported metrics",
+ transitions: allTransitions,
+ jobStatuses: allJobStatuses,
+ initiatorIDs: []uuid.UUID{agplprebuilds.SystemUserID},
+ ownerIDs: []uuid.UUID{agplprebuilds.SystemUserID, uuid.New()},
+ metrics: nil,
templateDeleted: []bool{true},
eligible: []bool{false},
},
@@ -220,7 +206,7 @@ func TestMetricsCollector(t *testing.T) {
})
clock := quartz.NewMock(t)
db, pubsub := dbtestutil.NewDB(t)
- reconciler := prebuilds.NewStoreReconciler(db, pubsub, codersdk.PrebuildsConfig{}, logger, quartz.NewMock(t), prometheus.NewRegistry())
+ reconciler := prebuilds.NewStoreReconciler(db, pubsub, codersdk.PrebuildsConfig{}, logger, quartz.NewMock(t), prometheus.NewRegistry(), newNoopEnqueuer())
ctx := testutil.Context(t, testutil.WaitLong)
createdUsers := []uuid.UUID{agplprebuilds.SystemUserID}
@@ -242,7 +228,7 @@ func TestMetricsCollector(t *testing.T) {
org, template := setupTestDBTemplate(t, db, ownerID, templateDeleted)
templateVersionID := setupTestDBTemplateVersion(ctx, t, clock, db, pubsub, org.ID, ownerID, template.ID)
preset := setupTestDBPreset(t, db, templateVersionID, 1, uuid.New().String())
- workspace := setupTestDBWorkspace(
+ workspace, _ := setupTestDBWorkspace(
t, clock, db, pubsub,
transition, jobStatus, org.ID, preset, template.ID, templateVersionID, initiatorID, ownerID,
)
@@ -281,6 +267,12 @@ func TestMetricsCollector(t *testing.T) {
"organization_name": org.Name,
}
+ // If no expected metrics have been defined, ensure we don't find any metric series (i.e. metrics with given labels).
+ if test.metrics == nil {
+ series := findAllMetricSeries(metricsFamilies, labels)
+ require.Empty(t, series)
+ }
+
for _, check := range test.metrics {
metric := findMetric(metricsFamilies, check.name, labels)
if check.value == nil {
@@ -307,6 +299,131 @@ func TestMetricsCollector(t *testing.T) {
}
}
+// TestMetricsCollector_DuplicateTemplateNames validates a bug that we saw previously which caused duplicate metric series
+// registration when a template was deleted and a new one created with the same name (and preset name).
+// We are now excluding deleted templates from our metric collection.
+func TestMetricsCollector_DuplicateTemplateNames(t *testing.T) {
+ t.Parallel()
+
+ if !dbtestutil.WillUsePostgres() {
+ t.Skip("this test requires postgres")
+ }
+
+ type metricCheck struct {
+ name string
+ value *float64
+ isCounter bool
+ }
+
+ type testCase struct {
+ transition database.WorkspaceTransition
+ jobStatus database.ProvisionerJobStatus
+ initiatorID uuid.UUID
+ ownerID uuid.UUID
+ metrics []metricCheck
+ eligible bool
+ }
+
+ test := testCase{
+ transition: database.WorkspaceTransitionStart,
+ jobStatus: database.ProvisionerJobStatusSucceeded,
+ initiatorID: agplprebuilds.SystemUserID,
+ ownerID: agplprebuilds.SystemUserID,
+ metrics: []metricCheck{
+ {prebuilds.MetricCreatedCount, ptr.To(1.0), true},
+ {prebuilds.MetricClaimedCount, ptr.To(0.0), true},
+ {prebuilds.MetricFailedCount, ptr.To(0.0), true},
+ {prebuilds.MetricDesiredGauge, ptr.To(1.0), false},
+ {prebuilds.MetricRunningGauge, ptr.To(1.0), false},
+ {prebuilds.MetricEligibleGauge, ptr.To(1.0), false},
+ },
+ eligible: true,
+ }
+
+ logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
+ clock := quartz.NewMock(t)
+ db, pubsub := dbtestutil.NewDB(t)
+ reconciler := prebuilds.NewStoreReconciler(db, pubsub, codersdk.PrebuildsConfig{}, logger, quartz.NewMock(t), prometheus.NewRegistry(), newNoopEnqueuer())
+ ctx := testutil.Context(t, testutil.WaitLong)
+
+ collector := prebuilds.NewMetricsCollector(db, logger, reconciler)
+ registry := prometheus.NewPedanticRegistry()
+ registry.Register(collector)
+
+ presetName := "default-preset"
+ defaultOrg := dbgen.Organization(t, db, database.Organization{})
+ setupTemplateWithDeps := func() database.Template {
+ template := setupTestDBTemplateWithinOrg(t, db, test.ownerID, false, "default-template", defaultOrg)
+ templateVersionID := setupTestDBTemplateVersion(ctx, t, clock, db, pubsub, defaultOrg.ID, test.ownerID, template.ID)
+ preset := setupTestDBPreset(t, db, templateVersionID, 1, "default-preset")
+ workspace, _ := setupTestDBWorkspace(
+ t, clock, db, pubsub,
+ test.transition, test.jobStatus, defaultOrg.ID, preset, template.ID, templateVersionID, test.initiatorID, test.ownerID,
+ )
+ setupTestDBWorkspaceAgent(t, db, workspace.ID, test.eligible)
+ return template
+ }
+
+ // When: starting with a regular template.
+ template := setupTemplateWithDeps()
+ labels := map[string]string{
+ "template_name": template.Name,
+ "preset_name": presetName,
+ "organization_name": defaultOrg.Name,
+ }
+
+ // nolint:gocritic // Authz context needed to retrieve state.
+ ctx = dbauthz.AsPrebuildsOrchestrator(ctx)
+
+ // Then: metrics collect successfully.
+ require.NoError(t, collector.UpdateState(ctx, testutil.WaitLong))
+ metricsFamilies, err := registry.Gather()
+ require.NoError(t, err)
+ require.NotEmpty(t, findAllMetricSeries(metricsFamilies, labels))
+
+ // When: the template is deleted.
+ require.NoError(t, db.UpdateTemplateDeletedByID(ctx, database.UpdateTemplateDeletedByIDParams{
+ ID: template.ID,
+ Deleted: true,
+ UpdatedAt: dbtime.Now(),
+ }))
+
+ // Then: metrics collect successfully but are empty because the template is deleted.
+ require.NoError(t, collector.UpdateState(ctx, testutil.WaitLong))
+ metricsFamilies, err = registry.Gather()
+ require.NoError(t, err)
+ require.Empty(t, findAllMetricSeries(metricsFamilies, labels))
+
+ // When: a new template is created with the same name as the deleted template.
+ newTemplate := setupTemplateWithDeps()
+
+ // Ensure the database has both the new and old (delete) template.
+ {
+ deleted, err := db.GetTemplateByOrganizationAndName(ctx, database.GetTemplateByOrganizationAndNameParams{
+ OrganizationID: template.OrganizationID,
+ Deleted: true,
+ Name: template.Name,
+ })
+ require.NoError(t, err)
+ require.Equal(t, template.ID, deleted.ID)
+
+ current, err := db.GetTemplateByOrganizationAndName(ctx, database.GetTemplateByOrganizationAndNameParams{
+ // Use details from deleted template to ensure they're aligned.
+ OrganizationID: template.OrganizationID,
+ Deleted: false,
+ Name: template.Name,
+ })
+ require.NoError(t, err)
+ require.Equal(t, newTemplate.ID, current.ID)
+ }
+
+ // Then: metrics collect successfully.
+ require.NoError(t, collector.UpdateState(ctx, testutil.WaitLong))
+ metricsFamilies, err = registry.Gather()
+ require.NoError(t, err)
+ require.NotEmpty(t, findAllMetricSeries(metricsFamilies, labels))
+}
+
func findMetric(metricsFamilies []*prometheus_client.MetricFamily, name string, labels map[string]string) *prometheus_client.Metric {
for _, metricFamily := range metricsFamilies {
if metricFamily.GetName() != name {
@@ -334,3 +451,33 @@ func findMetric(metricsFamilies []*prometheus_client.MetricFamily, name string,
}
return nil
}
+
+// findAllMetricSeries finds all metrics with a given set of labels.
+func findAllMetricSeries(metricsFamilies []*prometheus_client.MetricFamily, labels map[string]string) map[string]*prometheus_client.Metric {
+ series := make(map[string]*prometheus_client.Metric)
+ for _, metricFamily := range metricsFamilies {
+ for _, metric := range metricFamily.GetMetric() {
+ labelPairs := metric.GetLabel()
+
+ if len(labelPairs) != len(labels) {
+ continue
+ }
+
+ // Convert label pairs to map for easier lookup
+ metricLabels := make(map[string]string, len(labelPairs))
+ for _, label := range labelPairs {
+ metricLabels[label.GetName()] = label.GetValue()
+ }
+
+ // Check if all requested labels match
+ for wantName, wantValue := range labels {
+ if metricLabels[wantName] != wantValue {
+ continue
+ }
+ }
+
+ series[metricFamily.GetName()] = metric
+ }
+ }
+ return series
+}
diff --git a/enterprise/coderd/prebuilds/reconcile.go b/enterprise/coderd/prebuilds/reconcile.go
index 79a8baa337e72..f9588a5d7cacb 100644
--- a/enterprise/coderd/prebuilds/reconcile.go
+++ b/enterprise/coderd/prebuilds/reconcile.go
@@ -3,8 +3,10 @@ package prebuilds
import (
"context"
"database/sql"
+ "errors"
"fmt"
"math"
+ "strings"
"sync"
"sync/atomic"
"time"
@@ -19,11 +21,13 @@ import (
"github.com/coder/coder/v2/coderd/database/dbauthz"
"github.com/coder/coder/v2/coderd/database/provisionerjobs"
"github.com/coder/coder/v2/coderd/database/pubsub"
+ "github.com/coder/coder/v2/coderd/notifications"
"github.com/coder/coder/v2/coderd/prebuilds"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/rbac/policy"
"github.com/coder/coder/v2/coderd/wsbuilder"
"github.com/coder/coder/v2/codersdk"
+ sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
"cdr.dev/slog"
@@ -40,6 +44,7 @@ type StoreReconciler struct {
clock quartz.Clock
registerer prometheus.Registerer
metrics *MetricsCollector
+ notifEnq notifications.Enqueuer
cancelFn context.CancelCauseFunc
running atomic.Bool
@@ -56,6 +61,7 @@ func NewStoreReconciler(store database.Store,
logger slog.Logger,
clock quartz.Clock,
registerer prometheus.Registerer,
+ notifEnq notifications.Enqueuer,
) *StoreReconciler {
reconciler := &StoreReconciler{
store: store,
@@ -64,6 +70,7 @@ func NewStoreReconciler(store database.Store,
cfg: cfg,
clock: clock,
registerer: registerer,
+ notifEnq: notifEnq,
done: make(chan struct{}, 1),
provisionNotifyCh: make(chan database.ProvisionerJob, 10),
}
@@ -633,3 +640,124 @@ func (c *StoreReconciler) provision(
return nil
}
+
+// ForceMetricsUpdate forces the metrics collector, if defined, to update its state (we cache the metrics state to
+// reduce load on the database).
+func (c *StoreReconciler) ForceMetricsUpdate(ctx context.Context) error {
+ if c.metrics == nil {
+ return nil
+ }
+
+ return c.metrics.UpdateState(ctx, time.Second*10)
+}
+
+func (c *StoreReconciler) TrackResourceReplacement(ctx context.Context, workspaceID, buildID uuid.UUID, replacements []*sdkproto.ResourceReplacement) {
+ // nolint:gocritic // Necessary to query all the required data.
+ ctx = dbauthz.AsSystemRestricted(ctx)
+ // Since this may be called in a fire-and-forget fashion, we need to give up at some point.
+ trackCtx, trackCancel := context.WithTimeout(ctx, time.Minute)
+ defer trackCancel()
+
+ if err := c.trackResourceReplacement(trackCtx, workspaceID, buildID, replacements); err != nil {
+ c.logger.Error(ctx, "failed to track resource replacement", slog.Error(err))
+ }
+}
+
+// nolint:revive // Shut up it's fine.
+func (c *StoreReconciler) trackResourceReplacement(ctx context.Context, workspaceID, buildID uuid.UUID, replacements []*sdkproto.ResourceReplacement) error {
+ if err := ctx.Err(); err != nil {
+ return err
+ }
+
+ workspace, err := c.store.GetWorkspaceByID(ctx, workspaceID)
+ if err != nil {
+ return xerrors.Errorf("fetch workspace %q: %w", workspaceID.String(), err)
+ }
+
+ build, err := c.store.GetWorkspaceBuildByID(ctx, buildID)
+ if err != nil {
+ return xerrors.Errorf("fetch workspace build %q: %w", buildID.String(), err)
+ }
+
+ // The first build will always be the prebuild.
+ prebuild, err := c.store.GetWorkspaceBuildByWorkspaceIDAndBuildNumber(ctx, database.GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams{
+ WorkspaceID: workspaceID, BuildNumber: 1,
+ })
+ if err != nil {
+ return xerrors.Errorf("fetch prebuild: %w", err)
+ }
+
+ // This should not be possible, but defend against it.
+ if !prebuild.TemplateVersionPresetID.Valid || prebuild.TemplateVersionPresetID.UUID == uuid.Nil {
+ return xerrors.Errorf("no preset used in prebuild for workspace %q", workspaceID.String())
+ }
+
+ prebuildPreset, err := c.store.GetPresetByID(ctx, prebuild.TemplateVersionPresetID.UUID)
+ if err != nil {
+ return xerrors.Errorf("fetch template preset for template version ID %q: %w", prebuild.TemplateVersionID.String(), err)
+ }
+
+ claimant, err := c.store.GetUserByID(ctx, workspace.OwnerID) // At this point, the workspace is owned by the new owner.
+ if err != nil {
+ return xerrors.Errorf("fetch claimant %q: %w", workspace.OwnerID.String(), err)
+ }
+
+ // Use the claiming build here (not prebuild) because both should be equivalent, and we might as well spot inconsistencies now.
+ templateVersion, err := c.store.GetTemplateVersionByID(ctx, build.TemplateVersionID)
+ if err != nil {
+ return xerrors.Errorf("fetch template version %q: %w", build.TemplateVersionID.String(), err)
+ }
+
+ org, err := c.store.GetOrganizationByID(ctx, workspace.OrganizationID)
+ if err != nil {
+ return xerrors.Errorf("fetch org %q: %w", workspace.OrganizationID.String(), err)
+ }
+
+ // Track resource replacement in Prometheus metric.
+ if c.metrics != nil {
+ c.metrics.trackResourceReplacement(org.Name, workspace.TemplateName, prebuildPreset.Name)
+ }
+
+ // Send notification to template admins.
+ if c.notifEnq == nil {
+ c.logger.Warn(ctx, "notification enqueuer not set, cannot send resource replacement notification(s)")
+ return nil
+ }
+
+ repls := make(map[string]string, len(replacements))
+ for _, repl := range replacements {
+ repls[repl.GetResource()] = strings.Join(repl.GetPaths(), ", ")
+ }
+
+ templateAdmins, err := c.store.GetUsers(ctx, database.GetUsersParams{
+ RbacRole: []string{codersdk.RoleTemplateAdmin},
+ })
+ if err != nil {
+ return xerrors.Errorf("fetch template admins: %w", err)
+ }
+
+ var notifErr error
+ for _, templateAdmin := range templateAdmins {
+ if _, err := c.notifEnq.EnqueueWithData(ctx, templateAdmin.ID, notifications.TemplateWorkspaceResourceReplaced,
+ map[string]string{
+ "org": org.Name,
+ "workspace": workspace.Name,
+ "template": workspace.TemplateName,
+ "template_version": templateVersion.Name,
+ "preset": prebuildPreset.Name,
+ "workspace_build_num": fmt.Sprintf("%d", build.BuildNumber),
+ "claimant": claimant.Username,
+ },
+ map[string]any{
+ "replacements": repls,
+ }, "prebuilds_reconciler",
+ // Associate this notification with all the related entities.
+ workspace.ID, workspace.OwnerID, workspace.TemplateID, templateVersion.ID, prebuildPreset.ID, workspace.OrganizationID,
+ ); err != nil {
+ notifErr = errors.Join(xerrors.Errorf("send notification to %q: %w", templateAdmin.ID.String(), err))
+ continue
+ }
+ }
+
+ return notifErr
+}
diff --git a/enterprise/coderd/prebuilds/reconcile_test.go b/enterprise/coderd/prebuilds/reconcile_test.go
index a1666134a7965..660b1733e6cc9 100644
--- a/enterprise/coderd/prebuilds/reconcile_test.go
+++ b/enterprise/coderd/prebuilds/reconcile_test.go
@@ -9,10 +9,14 @@ import (
"time"
"github.com/prometheus/client_golang/prometheus"
+ "github.com/stretchr/testify/assert"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/coderd/database/dbtime"
+ "github.com/coder/coder/v2/coderd/notifications"
+ "github.com/coder/coder/v2/coderd/notifications/notificationstest"
"github.com/coder/coder/v2/coderd/util/slice"
+ sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
@@ -49,7 +53,7 @@ func TestNoReconciliationActionsIfNoPresets(t *testing.T) {
ReconciliationInterval: serpent.Duration(testutil.WaitLong),
}
logger := testutil.Logger(t)
- controller := prebuilds.NewStoreReconciler(db, ps, cfg, logger, quartz.NewMock(t), prometheus.NewRegistry())
+ controller := prebuilds.NewStoreReconciler(db, ps, cfg, logger, quartz.NewMock(t), prometheus.NewRegistry(), newNoopEnqueuer())
// given a template version with no presets
org := dbgen.Organization(t, db, database.Organization{})
@@ -94,7 +98,7 @@ func TestNoReconciliationActionsIfNoPrebuilds(t *testing.T) {
ReconciliationInterval: serpent.Duration(testutil.WaitLong),
}
logger := testutil.Logger(t)
- controller := prebuilds.NewStoreReconciler(db, ps, cfg, logger, quartz.NewMock(t), prometheus.NewRegistry())
+ controller := prebuilds.NewStoreReconciler(db, ps, cfg, logger, quartz.NewMock(t), prometheus.NewRegistry(), newNoopEnqueuer())
// given there are presets, but no prebuilds
org := dbgen.Organization(t, db, database.Organization{})
@@ -290,10 +294,15 @@ func TestPrebuildReconciliation(t *testing.T) {
templateDeleted: []bool{false},
},
{
- name: "delete prebuilds for deleted templates",
+ // Templates can be soft-deleted (`deleted=true`) or hard-deleted (row is removed).
+ // On the former there is *no* DB constraint to prevent soft deletion, so we have to ensure that if somehow
+ // the template was soft-deleted any running prebuilds will be removed.
+ // On the latter there is a DB constraint to prevent row deletion if any workspaces reference the deleting template.
+ name: "soft-deleted templates MAY have prebuilds",
prebuildLatestTransitions: []database.WorkspaceTransition{database.WorkspaceTransitionStart},
prebuildJobStatuses: []database.ProvisionerJobStatus{database.ProvisionerJobStatusSucceeded},
templateVersionActive: []bool{true, false},
+ shouldCreateNewPrebuild: ptr.To(false),
shouldDeleteOldPrebuild: ptr.To(true),
templateDeleted: []bool{true},
},
@@ -345,7 +354,7 @@ func TestPrebuildReconciliation(t *testing.T) {
1,
uuid.New().String(),
)
- prebuild := setupTestDBPrebuild(
+ prebuild, _ := setupTestDBPrebuild(
t,
clock,
db,
@@ -367,7 +376,7 @@ func TestPrebuildReconciliation(t *testing.T) {
if useBrokenPubsub {
pubSub = &brokenPublisher{Pubsub: pubSub}
}
- controller := prebuilds.NewStoreReconciler(db, pubSub, cfg, logger, quartz.NewMock(t), prometheus.NewRegistry())
+ controller := prebuilds.NewStoreReconciler(db, pubSub, cfg, logger, quartz.NewMock(t), prometheus.NewRegistry(), newNoopEnqueuer())
// Run the reconciliation multiple times to ensure idempotency
// 8 was arbitrary, but large enough to reasonably trust the result
@@ -444,7 +453,7 @@ func TestMultiplePresetsPerTemplateVersion(t *testing.T) {
t, &slogtest.Options{IgnoreErrors: true},
).Leveled(slog.LevelDebug)
db, pubSub := dbtestutil.NewDB(t)
- controller := prebuilds.NewStoreReconciler(db, pubSub, cfg, logger, quartz.NewMock(t), prometheus.NewRegistry())
+ controller := prebuilds.NewStoreReconciler(db, pubSub, cfg, logger, quartz.NewMock(t), prometheus.NewRegistry(), newNoopEnqueuer())
ownerID := uuid.New()
dbgen.User(t, db, database.User{
@@ -477,7 +486,7 @@ func TestMultiplePresetsPerTemplateVersion(t *testing.T) {
)
prebuildIDs := make([]uuid.UUID, 0)
for i := 0; i < int(preset.DesiredInstances.Int32); i++ {
- prebuild := setupTestDBPrebuild(
+ prebuild, _ := setupTestDBPrebuild(
t,
clock,
db,
@@ -528,7 +537,7 @@ func TestInvalidPreset(t *testing.T) {
t, &slogtest.Options{IgnoreErrors: true},
).Leveled(slog.LevelDebug)
db, pubSub := dbtestutil.NewDB(t)
- controller := prebuilds.NewStoreReconciler(db, pubSub, cfg, logger, quartz.NewMock(t), prometheus.NewRegistry())
+ controller := prebuilds.NewStoreReconciler(db, pubSub, cfg, logger, quartz.NewMock(t), prometheus.NewRegistry(), newNoopEnqueuer())
ownerID := uuid.New()
dbgen.User(t, db, database.User{
@@ -592,7 +601,7 @@ func TestDeletionOfPrebuiltWorkspaceWithInvalidPreset(t *testing.T) {
t, &slogtest.Options{IgnoreErrors: true},
).Leveled(slog.LevelDebug)
db, pubSub := dbtestutil.NewDB(t)
- controller := prebuilds.NewStoreReconciler(db, pubSub, cfg, logger, quartz.NewMock(t), prometheus.NewRegistry())
+ controller := prebuilds.NewStoreReconciler(db, pubSub, cfg, logger, quartz.NewMock(t), prometheus.NewRegistry(), newNoopEnqueuer())
ownerID := uuid.New()
dbgen.User(t, db, database.User{
@@ -601,7 +610,7 @@ func TestDeletionOfPrebuiltWorkspaceWithInvalidPreset(t *testing.T) {
org, template := setupTestDBTemplate(t, db, ownerID, templateDeleted)
templateVersionID := setupTestDBTemplateVersion(ctx, t, clock, db, pubSub, org.ID, ownerID, template.ID)
preset := setupTestDBPreset(t, db, templateVersionID, 1, uuid.New().String())
- prebuiltWorkspace := setupTestDBPrebuild(
+ prebuiltWorkspace, _ := setupTestDBPrebuild(
t,
clock,
db,
@@ -669,7 +678,7 @@ func TestRunLoop(t *testing.T) {
t, &slogtest.Options{IgnoreErrors: true},
).Leveled(slog.LevelDebug)
db, pubSub := dbtestutil.NewDB(t)
- reconciler := prebuilds.NewStoreReconciler(db, pubSub, cfg, logger, clock, prometheus.NewRegistry())
+ reconciler := prebuilds.NewStoreReconciler(db, pubSub, cfg, logger, clock, prometheus.NewRegistry(), newNoopEnqueuer())
ownerID := uuid.New()
dbgen.User(t, db, database.User{
@@ -702,7 +711,7 @@ func TestRunLoop(t *testing.T) {
)
prebuildIDs := make([]uuid.UUID, 0)
for i := 0; i < int(preset.DesiredInstances.Int32); i++ {
- prebuild := setupTestDBPrebuild(
+ prebuild, _ := setupTestDBPrebuild(
t,
clock,
db,
@@ -799,7 +808,7 @@ func TestFailedBuildBackoff(t *testing.T) {
t, &slogtest.Options{IgnoreErrors: true},
).Leveled(slog.LevelDebug)
db, ps := dbtestutil.NewDB(t)
- reconciler := prebuilds.NewStoreReconciler(db, ps, cfg, logger, clock, prometheus.NewRegistry())
+ reconciler := prebuilds.NewStoreReconciler(db, ps, cfg, logger, clock, prometheus.NewRegistry(), newNoopEnqueuer())
// Given: an active template version with presets and prebuilds configured.
const desiredInstances = 2
@@ -812,7 +821,7 @@ func TestFailedBuildBackoff(t *testing.T) {
preset := setupTestDBPreset(t, db, templateVersionID, desiredInstances, "test")
for range desiredInstances {
- _ = setupTestDBPrebuild(t, clock, db, ps, database.WorkspaceTransitionStart, database.ProvisionerJobStatusFailed, org.ID, preset, template.ID, templateVersionID)
+ _, _ = setupTestDBPrebuild(t, clock, db, ps, database.WorkspaceTransitionStart, database.ProvisionerJobStatusFailed, org.ID, preset, template.ID, templateVersionID)
}
// When: determining what actions to take next, backoff is calculated because the prebuild is in a failed state.
@@ -873,7 +882,7 @@ func TestFailedBuildBackoff(t *testing.T) {
if i == 1 {
status = database.ProvisionerJobStatusSucceeded
}
- _ = setupTestDBPrebuild(t, clock, db, ps, database.WorkspaceTransitionStart, status, org.ID, preset, template.ID, templateVersionID)
+ _, _ = setupTestDBPrebuild(t, clock, db, ps, database.WorkspaceTransitionStart, status, org.ID, preset, template.ID, templateVersionID)
}
// Then: the backoff time is roughly equal to two backoff intervals, since another build has failed.
@@ -914,7 +923,8 @@ func TestReconciliationLock(t *testing.T) {
codersdk.PrebuildsConfig{},
slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug),
quartz.NewMock(t),
- prometheus.NewRegistry())
+ prometheus.NewRegistry(),
+ newNoopEnqueuer())
reconciler.WithReconciliationLock(ctx, logger, func(_ context.Context, _ database.Store) error {
lockObtained := mutex.TryLock()
// As long as the postgres lock is held, this mutex should always be unlocked when we get here.
@@ -931,6 +941,102 @@ func TestReconciliationLock(t *testing.T) {
wg.Wait()
}
+func TestTrackResourceReplacement(t *testing.T) {
+ t.Parallel()
+
+ if !dbtestutil.WillUsePostgres() {
+ t.Skip("This test requires postgres")
+ }
+
+ ctx := testutil.Context(t, testutil.WaitSuperLong)
+
+ // Setup.
+ clock := quartz.NewMock(t)
+ logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: false}).Leveled(slog.LevelDebug)
+ db, ps := dbtestutil.NewDB(t)
+
+ fakeEnqueuer := newFakeEnqueuer()
+ registry := prometheus.NewRegistry()
+ reconciler := prebuilds.NewStoreReconciler(db, ps, codersdk.PrebuildsConfig{}, logger, clock, registry, fakeEnqueuer)
+
+ // Given: a template admin to receive a notification.
+ templateAdmin := dbgen.User(t, db, database.User{
+ RBACRoles: []string{codersdk.RoleTemplateAdmin},
+ })
+
+ // Given: a prebuilt workspace.
+ userID := uuid.New()
+ dbgen.User(t, db, database.User{ID: userID})
+ org, template := setupTestDBTemplate(t, db, userID, false)
+ templateVersionID := setupTestDBTemplateVersion(ctx, t, clock, db, ps, org.ID, userID, template.ID)
+ preset := setupTestDBPreset(t, db, templateVersionID, 1, "b0rked")
+ prebuiltWorkspace, prebuild := setupTestDBPrebuild(t, clock, db, ps, database.WorkspaceTransitionStart, database.ProvisionerJobStatusSucceeded, org.ID, preset, template.ID, templateVersionID)
+
+ // Given: no replacement has been tracked yet, we should not see a metric for it yet.
+ require.NoError(t, reconciler.ForceMetricsUpdate(ctx))
+ mf, err := registry.Gather()
+ require.NoError(t, err)
+ require.Nil(t, findMetric(mf, prebuilds.MetricResourceReplacementsCount, map[string]string{
+ "template_name": template.Name,
+ "preset_name": preset.Name,
+ "org_name": org.Name,
+ }))
+
+ // When: a claim occurred and resource replacements are detected (_how_ is out of scope of this test).
+ reconciler.TrackResourceReplacement(ctx, prebuiltWorkspace.ID, prebuild.ID, []*sdkproto.ResourceReplacement{
+ {
+ Resource: "docker_container[0]",
+ Paths: []string{"env", "image"},
+ },
+ {
+ Resource: "docker_volume[0]",
+ Paths: []string{"name"},
+ },
+ })
+
+ // Then: a notification will be sent detailing the replacement(s).
+ matching := fakeEnqueuer.Sent(func(notification *notificationstest.FakeNotification) bool {
+ // This is not an exhaustive check of the expected labels/data in the notification. This would tie the implementations
+ // too tightly together.
+ // All we need to validate is that a template of the right kind was sent, to the expected user, with some replacements.
+
+ if !assert.Equal(t, notification.TemplateID, notifications.TemplateWorkspaceResourceReplaced, "unexpected template") {
+ return false
+ }
+
+ if !assert.Equal(t, templateAdmin.ID, notification.UserID, "unexpected receiver") {
+ return false
+ }
+
+ if !assert.Len(t, notification.Data["replacements"], 2, "unexpected replacements count") {
+ return false
+ }
+
+ return true
+ })
+ require.Len(t, matching, 1)
+
+ // Then: the metric will be incremented.
+ mf, err = registry.Gather()
+ require.NoError(t, err)
+ metric := findMetric(mf, prebuilds.MetricResourceReplacementsCount, map[string]string{
+ "template_name": template.Name,
+ "preset_name": preset.Name,
+ "org_name": org.Name,
+ })
+ require.NotNil(t, metric)
+ require.NotNil(t, metric.GetCounter())
+ require.EqualValues(t, 1, metric.GetCounter().GetValue())
+}
+
+func newNoopEnqueuer() *notifications.NoopEnqueuer {
+ return notifications.NewNoopEnqueuer()
+}
+
+func newFakeEnqueuer() *notificationstest.FakeEnqueuer {
+ return notificationstest.NewFakeEnqueuer()
+}
+
// nolint:revive // It's a control flag, but this is a test.
func setupTestDBTemplate(
t *testing.T,
@@ -959,6 +1065,33 @@ func setupTestDBTemplate(
return org, template
}
+// nolint:revive // It's a control flag, but this is a test.
+func setupTestDBTemplateWithinOrg(
+ t *testing.T,
+ db database.Store,
+ userID uuid.UUID,
+ templateDeleted bool,
+ templateName string,
+ org database.Organization,
+) database.Template {
+ t.Helper()
+
+ template := dbgen.Template(t, db, database.Template{
+ Name: templateName,
+ CreatedBy: userID,
+ OrganizationID: org.ID,
+ CreatedAt: time.Now().Add(muchEarlier),
+ })
+ if templateDeleted {
+ ctx := testutil.Context(t, testutil.WaitShort)
+ require.NoError(t, db.UpdateTemplateDeletedByID(ctx, database.UpdateTemplateDeletedByIDParams{
+ ID: template.ID,
+ Deleted: true,
+ }))
+ }
+ return template
+}
+
const (
earlier = -time.Hour
muchEarlier = -time.Hour * 2
@@ -1040,7 +1173,7 @@ func setupTestDBPrebuild(
preset database.TemplateVersionPreset,
templateID uuid.UUID,
templateVersionID uuid.UUID,
-) database.WorkspaceTable {
+) (database.WorkspaceTable, database.WorkspaceBuild) {
t.Helper()
return setupTestDBWorkspace(t, clock, db, ps, transition, prebuildStatus, orgID, preset, templateID, templateVersionID, agplprebuilds.SystemUserID, agplprebuilds.SystemUserID)
}
@@ -1058,7 +1191,7 @@ func setupTestDBWorkspace(
templateVersionID uuid.UUID,
initiatorID uuid.UUID,
ownerID uuid.UUID,
-) database.WorkspaceTable {
+) (database.WorkspaceTable, database.WorkspaceBuild) {
t.Helper()
cancelledAt := sql.NullTime{}
completedAt := sql.NullTime{}
@@ -1117,7 +1250,7 @@ func setupTestDBWorkspace(
},
})
- return workspace
+ return workspace, workspaceBuild
}
// nolint:revive // It's a control flag, but this is a test.
diff --git a/enterprise/coderd/provisionerdaemons.go b/enterprise/coderd/provisionerdaemons.go
index 6ffa15851214d..71a32d7c976ba 100644
--- a/enterprise/coderd/provisionerdaemons.go
+++ b/enterprise/coderd/provisionerdaemons.go
@@ -19,6 +19,8 @@ import (
"storj.io/drpc/drpcserver"
"cdr.dev/slog"
+ "github.com/coder/websocket"
+
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbauthz"
"github.com/coder/coder/v2/coderd/database/dbtime"
@@ -33,7 +35,6 @@ import (
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/provisionerd/proto"
"github.com/coder/coder/v2/provisionersdk"
- "github.com/coder/websocket"
)
func (api *API) provisionerDaemonsEnabledMW(next http.Handler) http.Handler {
@@ -356,6 +357,7 @@ func (api *API) provisionerDaemonServe(rw http.ResponseWriter, r *http.Request)
Clock: api.Clock,
},
api.NotificationsEnqueuer,
+ &api.AGPL.PrebuildsReconciler,
)
if err != nil {
if !xerrors.Is(err, context.Canceled) {
diff --git a/enterprise/coderd/workspaceagents_test.go b/enterprise/coderd/workspaceagents_test.go
index 4ac374a3c8c8e..44aba69b9ffaa 100644
--- a/enterprise/coderd/workspaceagents_test.go
+++ b/enterprise/coderd/workspaceagents_test.go
@@ -5,12 +5,19 @@ import (
"crypto/tls"
"fmt"
"net/http"
+ "os"
+ "regexp"
"testing"
+ "time"
+
+ "github.com/coder/coder/v2/coderd/database/dbtestutil"
+ "github.com/coder/serpent"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"github.com/coder/coder/v2/agent"
+ "github.com/coder/coder/v2/cli/clitest"
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/agentsdk"
@@ -73,6 +80,168 @@ func TestBlockNonBrowser(t *testing.T) {
})
}
+func TestReinitializeAgent(t *testing.T) {
+ t.Parallel()
+
+ tempAgentLog := testutil.CreateTemp(t, "", "testReinitializeAgent")
+
+ if !dbtestutil.WillUsePostgres() {
+ t.Skip("dbmem cannot currently claim a workspace")
+ }
+
+ db, ps := dbtestutil.NewDB(t)
+ // GIVEN a live enterprise API with the prebuilds feature enabled
+ client, user := coderdenttest.New(t, &coderdenttest.Options{
+ Options: &coderdtest.Options{
+ Database: db,
+ Pubsub: ps,
+ DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) {
+ dv.Prebuilds.ReconciliationInterval = serpent.Duration(time.Second)
+ dv.Experiments.Append(string(codersdk.ExperimentWorkspacePrebuilds))
+ }),
+ IncludeProvisionerDaemon: true,
+ },
+ LicenseOptions: &coderdenttest.LicenseOptions{
+ Features: license.Features{
+ codersdk.FeatureWorkspacePrebuilds: 1,
+ },
+ },
+ })
+
+ // GIVEN a template, template version, preset and a prebuilt workspace that uses them all
+ agentToken := uuid.UUID{3}
+ version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
+ Parse: echo.ParseComplete,
+ ProvisionPlan: []*proto.Response{
+ {
+ Type: &proto.Response_Plan{
+ Plan: &proto.PlanComplete{
+ Presets: []*proto.Preset{
+ {
+ Name: "test-preset",
+ Prebuild: &proto.Prebuild{
+ Instances: 1,
+ },
+ },
+ },
+ Resources: []*proto.Resource{
+ {
+ Agents: []*proto.Agent{
+ {
+ Name: "smith",
+ OperatingSystem: "linux",
+ Architecture: "i386",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ ProvisionApply: []*proto.Response{
+ {
+ Type: &proto.Response_Apply{
+ Apply: &proto.ApplyComplete{
+ Resources: []*proto.Resource{
+ {
+ Type: "compute",
+ Name: "main",
+ Agents: []*proto.Agent{
+ {
+ Name: "smith",
+ OperatingSystem: "linux",
+ Architecture: "i386",
+ Scripts: []*proto.Script{
+ {
+ RunOnStart: true,
+ Script: fmt.Sprintf("printenv >> %s; echo '---\n' >> %s", tempAgentLog.Name(), tempAgentLog.Name()), // Make reinitialization take long enough to assert that it happened
+ },
+ },
+ Auth: &proto.Agent_Token{
+ Token: agentToken.String(),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ })
+ coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
+
+ coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
+
+ // Wait for prebuilds to create a prebuilt workspace
+ ctx := context.Background()
+ // ctx := testutil.Context(t, testutil.WaitLong)
+ var (
+ prebuildID uuid.UUID
+ )
+ require.Eventually(t, func() bool {
+ agentAndBuild, err := db.GetWorkspaceAgentAndLatestBuildByAuthToken(ctx, agentToken)
+ if err != nil {
+ return false
+ }
+ prebuildID = agentAndBuild.WorkspaceBuild.ID
+ return true
+ }, testutil.WaitLong, testutil.IntervalFast)
+
+ prebuild := coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, prebuildID)
+
+ preset, err := db.GetPresetByWorkspaceBuildID(ctx, prebuildID)
+ require.NoError(t, err)
+
+ // GIVEN a running agent
+ logDir := t.TempDir()
+ inv, _ := clitest.New(t,
+ "agent",
+ "--auth", "token",
+ "--agent-token", agentToken.String(),
+ "--agent-url", client.URL.String(),
+ "--log-dir", logDir,
+ )
+ clitest.Start(t, inv)
+
+ // GIVEN the agent is in a happy steady state
+ waiter := coderdtest.NewWorkspaceAgentWaiter(t, client, prebuild.WorkspaceID)
+ waiter.WaitFor(coderdtest.AgentsReady)
+
+ // WHEN a workspace is created that can benefit from prebuilds
+ anotherClient, anotherUser := coderdtest.CreateAnotherUser(t, client, user.OrganizationID)
+ workspace, err := anotherClient.CreateUserWorkspace(ctx, anotherUser.ID.String(), codersdk.CreateWorkspaceRequest{
+ TemplateVersionID: version.ID,
+ TemplateVersionPresetID: preset.ID,
+ Name: "claimed-workspace",
+ })
+ require.NoError(t, err)
+
+ coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
+
+ // THEN reinitialization completes
+ waiter.WaitFor(coderdtest.AgentsReady)
+
+ var matches [][]byte
+ require.Eventually(t, func() bool {
+ // THEN the agent script ran again and reused the same agent token
+ contents, err := os.ReadFile(tempAgentLog.Name())
+ if err != nil {
+ return false
+ }
+ // UUID regex pattern (matches UUID v4-like strings)
+ uuidRegex := regexp.MustCompile(`\bCODER_AGENT_TOKEN=(.+)\b`)
+
+ matches = uuidRegex.FindAll(contents, -1)
+ // When an agent reinitializes, we expect it to run startup scripts again.
+ // As such, we expect to have written the agent environment to the temp file twice.
+ // Once on initial startup and then once on reinitialization.
+ return len(matches) == 2
+ }, testutil.WaitLong, testutil.IntervalMedium)
+ require.Equal(t, matches[0], matches[1])
+}
+
type setupResp struct {
workspace codersdk.Workspace
sdkAgent codersdk.WorkspaceAgent
diff --git a/enterprise/coderd/workspaces_test.go b/enterprise/coderd/workspaces_test.go
index 72859c5460fa7..92a51be33d552 100644
--- a/enterprise/coderd/workspaces_test.go
+++ b/enterprise/coderd/workspaces_test.go
@@ -4,6 +4,7 @@ import (
"bytes"
"context"
"database/sql"
+ "encoding/json"
"fmt"
"net/http"
"os"
@@ -13,6 +14,7 @@ import (
"testing"
"time"
+ "github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -30,6 +32,8 @@ import (
"github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/coderd/httpmw"
"github.com/coder/coder/v2/coderd/notifications"
+ "github.com/coder/coder/v2/coderd/prebuilds"
+ "github.com/coder/coder/v2/coderd/provisionerdserver"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/rbac/policy"
agplschedule "github.com/coder/coder/v2/coderd/schedule"
@@ -43,6 +47,7 @@ import (
"github.com/coder/coder/v2/enterprise/coderd/schedule"
"github.com/coder/coder/v2/provisioner/echo"
"github.com/coder/coder/v2/provisionersdk"
+ "github.com/coder/coder/v2/provisionersdk/proto"
"github.com/coder/coder/v2/testutil"
"github.com/coder/quartz"
)
@@ -453,6 +458,79 @@ func TestCreateUserWorkspace(t *testing.T) {
_, err = client1.CreateUserWorkspace(ctx, user1.ID.String(), req)
require.Error(t, err)
})
+
+ t.Run("ClaimPrebuild", func(t *testing.T) {
+ t.Parallel()
+
+ if !dbtestutil.WillUsePostgres() {
+ t.Skip("dbmem cannot currently claim a workspace")
+ }
+
+ client, db, user := coderdenttest.NewWithDatabase(t, &coderdenttest.Options{
+ Options: &coderdtest.Options{
+ DeploymentValues: coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) {
+ err := dv.Experiments.Append(string(codersdk.ExperimentWorkspacePrebuilds))
+ require.NoError(t, err)
+ }),
+ },
+ LicenseOptions: &coderdenttest.LicenseOptions{
+ Features: license.Features{
+ codersdk.FeatureWorkspacePrebuilds: 1,
+ },
+ },
+ })
+
+ // GIVEN a template, template version, preset and a prebuilt workspace that uses them all
+ presetID := uuid.New()
+ tv := dbfake.TemplateVersion(t, db).Seed(database.TemplateVersion{
+ OrganizationID: user.OrganizationID,
+ CreatedBy: user.UserID,
+ }).Preset(database.TemplateVersionPreset{
+ ID: presetID,
+ }).Do()
+
+ r := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
+ OwnerID: prebuilds.SystemUserID,
+ TemplateID: tv.Template.ID,
+ }).Seed(database.WorkspaceBuild{
+ TemplateVersionID: tv.TemplateVersion.ID,
+ TemplateVersionPresetID: uuid.NullUUID{
+ UUID: presetID,
+ Valid: true,
+ },
+ }).WithAgent(func(a []*proto.Agent) []*proto.Agent {
+ return a
+ }).Do()
+
+ // nolint:gocritic // this is a test
+ ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitLong))
+ agent, err := db.GetWorkspaceAgentAndLatestBuildByAuthToken(ctx, uuid.MustParse(r.AgentToken))
+ require.NoError(t, err)
+
+ err = db.UpdateWorkspaceAgentLifecycleStateByID(ctx, database.UpdateWorkspaceAgentLifecycleStateByIDParams{
+ ID: agent.WorkspaceAgent.ID,
+ LifecycleState: database.WorkspaceAgentLifecycleStateReady,
+ })
+ require.NoError(t, err)
+
+ // WHEN a workspace is created that matches the available prebuilt workspace
+ _, err = client.CreateUserWorkspace(ctx, user.UserID.String(), codersdk.CreateWorkspaceRequest{
+ TemplateVersionID: tv.TemplateVersion.ID,
+ TemplateVersionPresetID: presetID,
+ Name: "claimed-workspace",
+ })
+ require.NoError(t, err)
+
+ // THEN a new build is scheduled with the build stage specified
+ build, err := db.GetLatestWorkspaceBuildByWorkspaceID(ctx, r.Workspace.ID)
+ require.NoError(t, err)
+ require.NotEqual(t, build.ID, r.Build.ID)
+ job, err := db.GetProvisionerJobByID(ctx, build.JobID)
+ require.NoError(t, err)
+ var metadata provisionerdserver.WorkspaceProvisionJob
+ require.NoError(t, json.Unmarshal(job.Input, &metadata))
+ require.Equal(t, metadata.PrebuiltWorkspaceBuildStage, proto.PrebuiltWorkspaceBuildStage_CLAIM)
+ })
}
func TestWorkspaceAutobuild(t *testing.T) {
diff --git a/go.mod b/go.mod
index a2f8bda5c97bb..d35266e6a75a7 100644
--- a/go.mod
+++ b/go.mod
@@ -101,7 +101,7 @@ require (
github.com/coder/quartz v0.1.2
github.com/coder/retry v1.5.1
github.com/coder/serpent v0.10.0
- github.com/coder/terraform-provider-coder/v2 v2.4.0
+ github.com/coder/terraform-provider-coder/v2 v2.4.1
github.com/coder/websocket v1.8.13
github.com/coder/wgtunnel v0.1.13-0.20240522110300-ade90dfb2da0
github.com/coreos/go-oidc/v3 v3.14.1
diff --git a/go.sum b/go.sum
index 4a2c74f8dc69e..05a1d3ea0f2d7 100644
--- a/go.sum
+++ b/go.sum
@@ -921,8 +921,8 @@ github.com/coder/tailscale v1.1.1-0.20250422090654-5090e715905e h1:nope/SZfoLB9M
github.com/coder/tailscale v1.1.1-0.20250422090654-5090e715905e/go.mod h1:1ggFFdHTRjPRu9Yc1yA7nVHBYB50w9Ce7VIXNqcW6Ko=
github.com/coder/terraform-config-inspect v0.0.0-20250107175719-6d06d90c630e h1:JNLPDi2P73laR1oAclY6jWzAbucf70ASAvf5mh2cME0=
github.com/coder/terraform-config-inspect v0.0.0-20250107175719-6d06d90c630e/go.mod h1:Gz/z9Hbn+4KSp8A2FBtNszfLSdT2Tn/uAKGuVqqWmDI=
-github.com/coder/terraform-provider-coder/v2 v2.4.0 h1:uuFmF03IyahAZLXEukOdmvV9hGfUMJSESD8+G5wkTcM=
-github.com/coder/terraform-provider-coder/v2 v2.4.0/go.mod h1:2kaBpn5k9ZWtgKq5k4JbkVZG9DzEqR4mJSmpdshcO+s=
+github.com/coder/terraform-provider-coder/v2 v2.4.1 h1:+HxLJVENJ+kvGhibQ0jbr8Evi6M857d9691ytxNbv90=
+github.com/coder/terraform-provider-coder/v2 v2.4.1/go.mod h1:2kaBpn5k9ZWtgKq5k4JbkVZG9DzEqR4mJSmpdshcO+s=
github.com/coder/trivy v0.0.0-20250409153844-e6b004bc465a h1:yryP7e+IQUAArlycH4hQrjXQ64eRNbxsV5/wuVXHgME=
github.com/coder/trivy v0.0.0-20250409153844-e6b004bc465a/go.mod h1:dDvq9axp3kZsT63gY2Znd1iwzfqDq3kXbQnccIrjRYY=
github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE=
diff --git a/provisioner/terraform/executor.go b/provisioner/terraform/executor.go
index 442ed36074eb2..55b37f242ac05 100644
--- a/provisioner/terraform/executor.go
+++ b/provisioner/terraform/executor.go
@@ -258,7 +258,7 @@ func getStateFilePath(workdir string) string {
}
// revive:disable-next-line:flag-parameter
-func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr logSink, destroy bool) (*proto.PlanComplete, error) {
+func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr logSink, metadata *proto.Metadata) (*proto.PlanComplete, error) {
ctx, span := e.server.startTrace(ctx, tracing.FuncName())
defer span.End()
@@ -274,6 +274,7 @@ func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr l
"-refresh=true",
"-out=" + planfilePath,
}
+ destroy := metadata.GetWorkspaceTransition() == proto.WorkspaceTransition_DESTROY
if destroy {
args = append(args, "-destroy")
}
@@ -302,18 +303,49 @@ func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr l
state, plan, err := e.planResources(ctx, killCtx, planfilePath)
if err != nil {
graphTimings.ingest(createGraphTimingsEvent(timingGraphErrored))
- return nil, err
+ return nil, xerrors.Errorf("plan resources: %w", err)
+ }
+ planJSON, err := json.Marshal(plan)
+ if err != nil {
+ return nil, xerrors.Errorf("marshal plan: %w", err)
}
graphTimings.ingest(createGraphTimingsEvent(timingGraphComplete))
+ // 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 (
+ isPrebuildClaimAttempt = !destroy && metadata.GetPrebuiltWorkspaceBuildStage().IsPrebuiltWorkspaceClaim()
+ resReps []*proto.ResourceReplacement
+ )
+ if repsFromPlan := findResourceReplacements(plan); len(repsFromPlan) > 0 {
+ if isPrebuildClaimAttempt {
+ // TODO(dannyk): we should log drift always (not just during prebuild claim attempts); we're validating that this output
+ // will not be overwhelming for end-users, but it'll certainly be super valuable for template admins
+ // to diagnose this resource replacement issue, at least.
+ // Once prebuilds moves out of beta, consider deleting this condition.
+
+ // Lock held before calling (see top of method).
+ e.logDrift(ctx, killCtx, planfilePath, logr)
+ }
+
+ resReps = make([]*proto.ResourceReplacement, 0, len(repsFromPlan))
+ for n, p := range repsFromPlan {
+ resReps = append(resReps, &proto.ResourceReplacement{
+ Resource: n,
+ Paths: p,
+ })
+ }
+ }
+
return &proto.PlanComplete{
Parameters: state.Parameters,
Resources: state.Resources,
ExternalAuthProviders: state.ExternalAuthProviders,
Timings: append(e.timings.aggregate(), graphTimings.aggregate()...),
Presets: state.Presets,
- Plan: plan,
+ Plan: planJSON,
+ ResourceReplacements: resReps,
}, nil
}
@@ -335,11 +367,11 @@ func onlyDataResources(sm tfjson.StateModule) tfjson.StateModule {
}
// planResources must only be called while the lock is held.
-func (e *executor) planResources(ctx, killCtx context.Context, planfilePath string) (*State, json.RawMessage, error) {
+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.showPlan(ctx, killCtx, planfilePath)
+ plan, err := e.parsePlan(ctx, killCtx, planfilePath)
if err != nil {
return nil, nil, xerrors.Errorf("show terraform plan file: %w", err)
}
@@ -367,16 +399,11 @@ func (e *executor) planResources(ctx, killCtx context.Context, planfilePath stri
return nil, nil, err
}
- planJSON, err := json.Marshal(plan)
- if err != nil {
- return nil, nil, err
- }
-
- return state, planJSON, nil
+ return state, plan, nil
}
-// showPlan must only be called while the lock is held.
-func (e *executor) showPlan(ctx, killCtx context.Context, planfilePath string) (*tfjson.Plan, error) {
+// 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())
defer span.End()
@@ -386,6 +413,64 @@ func (e *executor) showPlan(ctx, killCtx context.Context, planfilePath string) (
return p, err
}
+// logDrift must only be called while the lock is held.
+// It will log the output of `terraform show`, which will show which resources have drifted from the known state.
+func (e *executor) logDrift(ctx, killCtx context.Context, planfilePath string, logr logSink) {
+ stdout, stdoutDone := resourceReplaceLogWriter(logr, e.logger)
+ stderr, stderrDone := logWriter(logr, proto.LogLevel_ERROR)
+ defer func() {
+ _ = stdout.Close()
+ _ = stderr.Close()
+ <-stdoutDone
+ <-stderrDone
+ }()
+
+ err := e.showPlan(ctx, killCtx, stdout, stderr, planfilePath)
+ if err != nil {
+ e.server.logger.Debug(ctx, "failed to log state drift", slog.Error(err))
+ }
+}
+
+// resourceReplaceLogWriter highlights log lines relating to resource replacement by elevating their log level.
+// This will help template admins to visually find problematic resources easier.
+//
+// The WriteCloser must be closed by the caller to end logging, after which the returned channel will be closed to
+// indicate that logging of the written data has finished. Failure to close the WriteCloser will leak a goroutine.
+func resourceReplaceLogWriter(sink logSink, logger slog.Logger) (io.WriteCloser, <-chan struct{}) {
+ r, w := io.Pipe()
+ done := make(chan struct{})
+
+ go func() {
+ defer close(done)
+
+ scanner := bufio.NewScanner(r)
+ for scanner.Scan() {
+ line := scanner.Bytes()
+ level := proto.LogLevel_INFO
+
+ // Terraform indicates that a resource will be deleted and recreated by showing the change along with this substring.
+ if bytes.Contains(line, []byte("# forces replacement")) {
+ level = proto.LogLevel_WARN
+ }
+
+ sink.ProvisionLog(level, string(line))
+ }
+ if err := scanner.Err(); err != nil {
+ logger.Error(context.Background(), "failed to read terraform log", slog.Error(err))
+ }
+ }()
+ return w, done
+}
+
+// showPlan must only be called while the lock is held.
+func (e *executor) showPlan(ctx, killCtx context.Context, stdoutWriter, stderrWriter io.WriteCloser, planfilePath string) error {
+ ctx, span := e.server.startTrace(ctx, tracing.FuncName())
+ defer span.End()
+
+ args := []string{"show", "-no-color", planfilePath}
+ return e.execWriteOutput(ctx, killCtx, args, e.basicEnv(), stdoutWriter, stderrWriter)
+}
+
// graph must only be called while the lock is held.
func (e *executor) graph(ctx, killCtx context.Context) (string, error) {
ctx, span := e.server.startTrace(ctx, tracing.FuncName())
diff --git a/provisioner/terraform/provision.go b/provisioner/terraform/provision.go
index f8f82bbad7b9a..79ed299514bb3 100644
--- a/provisioner/terraform/provision.go
+++ b/provisioner/terraform/provision.go
@@ -163,10 +163,7 @@ func (s *server) Plan(
return provisionersdk.PlanErrorf("plan vars: %s", err)
}
- resp, err := e.plan(
- ctx, killCtx, env, vars, sess,
- request.Metadata.GetWorkspaceTransition() == proto.WorkspaceTransition_DESTROY,
- )
+ resp, err := e.plan(ctx, killCtx, env, vars, sess, request.Metadata)
if err != nil {
return provisionersdk.PlanErrorf("%s", err.Error())
}
@@ -270,9 +267,23 @@ func provisionEnv(
"CODER_WORKSPACE_TEMPLATE_VERSION="+metadata.GetTemplateVersion(),
"CODER_WORKSPACE_BUILD_ID="+metadata.GetWorkspaceBuildId(),
)
- if metadata.GetIsPrebuild() {
+ if metadata.GetPrebuiltWorkspaceBuildStage().IsPrebuild() {
env = append(env, provider.IsPrebuildEnvironmentVariable()+"=true")
}
+ tokens := metadata.GetRunningAgentAuthTokens()
+ if len(tokens) == 1 {
+ env = append(env, provider.RunningAgentTokenEnvironmentVariable("")+"="+tokens[0].Token)
+ } else {
+ // Not currently supported, but added for forward-compatibility
+ for _, t := range tokens {
+ // If there are multiple agents, provide all the tokens to terraform so that it can
+ // choose the correct one for each agent ID.
+ env = append(env, provider.RunningAgentTokenEnvironmentVariable(t.AgentId)+"="+t.Token)
+ }
+ }
+ if metadata.GetPrebuiltWorkspaceBuildStage().IsPrebuiltWorkspaceClaim() {
+ env = append(env, provider.IsPrebuildClaimEnvironmentVariable()+"=true")
+ }
for key, value := range provisionersdk.AgentScriptEnv() {
env = append(env, key+"="+value)
diff --git a/provisioner/terraform/provision_test.go b/provisioner/terraform/provision_test.go
index 96514cc4b59ad..ecff965b72984 100644
--- a/provisioner/terraform/provision_test.go
+++ b/provisioner/terraform/provision_test.go
@@ -25,6 +25,7 @@ import (
"cdr.dev/slog"
"cdr.dev/slog/sloggers/slogtest"
+
"github.com/coder/coder/v2/codersdk/drpc"
"github.com/coder/coder/v2/provisioner/terraform"
"github.com/coder/coder/v2/provisionersdk"
@@ -977,7 +978,7 @@ func TestProvision(t *testing.T) {
required_providers {
coder = {
source = "coder/coder"
- version = "2.3.0-pre2"
+ version = ">= 2.4.1"
}
}
}
@@ -994,7 +995,7 @@ func TestProvision(t *testing.T) {
},
Request: &proto.PlanRequest{
Metadata: &proto.Metadata{
- IsPrebuild: true,
+ PrebuiltWorkspaceBuildStage: proto.PrebuiltWorkspaceBuildStage_CREATE,
},
},
Response: &proto.PlanComplete{
@@ -1008,6 +1009,44 @@ func TestProvision(t *testing.T) {
}},
},
},
+ {
+ Name: "is-prebuild-claim",
+ Files: map[string]string{
+ "main.tf": `terraform {
+ required_providers {
+ coder = {
+ source = "coder/coder"
+ version = ">= 2.4.1"
+ }
+ }
+ }
+ data "coder_workspace" "me" {}
+ resource "null_resource" "example" {}
+ resource "coder_metadata" "example" {
+ resource_id = null_resource.example.id
+ item {
+ key = "is_prebuild_claim"
+ value = data.coder_workspace.me.is_prebuild_claim
+ }
+ }
+ `,
+ },
+ Request: &proto.PlanRequest{
+ Metadata: &proto.Metadata{
+ PrebuiltWorkspaceBuildStage: proto.PrebuiltWorkspaceBuildStage_CLAIM,
+ },
+ },
+ Response: &proto.PlanComplete{
+ Resources: []*proto.Resource{{
+ Name: "example",
+ Type: "null_resource",
+ Metadata: []*proto.Resource_Metadata{{
+ Key: "is_prebuild_claim",
+ Value: "true",
+ }},
+ }},
+ },
+ },
}
// Remove unused cache dirs before running tests.
diff --git a/provisioner/terraform/resource_replacements.go b/provisioner/terraform/resource_replacements.go
new file mode 100644
index 0000000000000..a2bbbb1802883
--- /dev/null
+++ b/provisioner/terraform/resource_replacements.go
@@ -0,0 +1,86 @@
+package terraform
+
+import (
+ "fmt"
+ "strings"
+
+ tfjson "github.com/hashicorp/terraform-json"
+)
+
+type resourceReplacements map[string][]string
+
+// resourceReplacements finds all resources which would be replaced by the current plan, and the attribute paths which
+// caused the replacement.
+//
+// NOTE: "replacement" in terraform terms means that a resource will have to be destroyed and replaced with a new resource
+// since one of its immutable attributes was modified, which cannot be updated in-place.
+func findResourceReplacements(plan *tfjson.Plan) resourceReplacements {
+ if plan == nil {
+ return nil
+ }
+
+ // No changes, no problem!
+ if len(plan.ResourceChanges) == 0 {
+ return nil
+ }
+
+ replacements := make(resourceReplacements, len(plan.ResourceChanges))
+
+ for _, ch := range plan.ResourceChanges {
+ // No change, no problem!
+ if ch.Change == nil {
+ continue
+ }
+
+ // No-op change, no problem!
+ if ch.Change.Actions.NoOp() {
+ continue
+ }
+
+ // No replacements, no problem!
+ if len(ch.Change.ReplacePaths) == 0 {
+ continue
+ }
+
+ // Replacing our resources: could be a problem - but we ignore since they're "virtual" resources. If any of these
+ // resources' attributes are referenced by non-coder resources, those will show up as transitive changes there.
+ // i.e. if the coder_agent.id attribute is used in docker_container.env
+ //
+ // Replacing our resources is not strictly a problem in and of itself.
+ //
+ // NOTE:
+ // We may need to special-case coder_agent in the future. Currently, coder_agent is replaced on every build
+ // because it only supports Create but not Update: https://github.com/coder/terraform-provider-coder/blob/5648efb/provider/agent.go#L28
+ // When we can modify an agent's attributes, some of which may be immutable (like "arch") and some may not (like "env"),
+ // then we'll have to handle this specifically.
+ // This will only become relevant once we support multiple agents: https://github.com/coder/coder/issues/17388
+ if strings.Index(ch.Type, "coder_") == 0 {
+ continue
+ }
+
+ // Replacements found, problem!
+ for _, val := range ch.Change.ReplacePaths {
+ var pathStr string
+ // Each path needs to be coerced into a string. All types except []interface{} can be coerced using fmt.Sprintf.
+ switch path := val.(type) {
+ case []interface{}:
+ // Found a slice of paths; coerce to string and join by ".".
+ segments := make([]string, 0, len(path))
+ for _, seg := range path {
+ segments = append(segments, fmt.Sprintf("%v", seg))
+ }
+ pathStr = strings.Join(segments, ".")
+ default:
+ pathStr = fmt.Sprintf("%v", path)
+ }
+
+ replacements[ch.Address] = append(replacements[ch.Address], pathStr)
+ }
+ }
+
+ if len(replacements) == 0 {
+ return nil
+ }
+
+ return replacements
+}
diff --git a/provisioner/terraform/resource_replacements_internal_test.go b/provisioner/terraform/resource_replacements_internal_test.go
new file mode 100644
index 0000000000000..4cca4ed396a43
--- /dev/null
+++ b/provisioner/terraform/resource_replacements_internal_test.go
@@ -0,0 +1,176 @@
+package terraform
+
+import (
+ "testing"
+
+ tfjson "github.com/hashicorp/terraform-json"
+ "github.com/stretchr/testify/require"
+)
+
+func TestFindResourceReplacements(t *testing.T) {
+ t.Parallel()
+
+ cases := []struct {
+ name string
+ plan *tfjson.Plan
+ expected resourceReplacements
+ }{
+ {
+ name: "nil plan",
+ },
+ {
+ name: "no resource changes",
+ plan: &tfjson.Plan{},
+ },
+ {
+ name: "resource change with nil change",
+ plan: &tfjson.Plan{
+ ResourceChanges: []*tfjson.ResourceChange{
+ {
+ Address: "resource1",
+ },
+ },
+ },
+ },
+ {
+ name: "no-op action",
+ plan: &tfjson.Plan{
+ ResourceChanges: []*tfjson.ResourceChange{
+ {
+ Address: "resource1",
+ Change: &tfjson.Change{
+ Actions: tfjson.Actions{tfjson.ActionNoop},
+ },
+ },
+ },
+ },
+ },
+ {
+ name: "empty replace paths",
+ plan: &tfjson.Plan{
+ ResourceChanges: []*tfjson.ResourceChange{
+ {
+ Address: "resource1",
+ Change: &tfjson.Change{
+ Actions: tfjson.Actions{tfjson.ActionDelete, tfjson.ActionCreate},
+ },
+ },
+ },
+ },
+ },
+ {
+ name: "coder_* types are ignored",
+ plan: &tfjson.Plan{
+ ResourceChanges: []*tfjson.ResourceChange{
+ {
+ Address: "resource1",
+ Type: "coder_resource",
+ Change: &tfjson.Change{
+ Actions: tfjson.Actions{tfjson.ActionDelete, tfjson.ActionCreate},
+ ReplacePaths: []interface{}{"path1"},
+ },
+ },
+ },
+ },
+ },
+ {
+ name: "valid replacements - single path",
+ plan: &tfjson.Plan{
+ ResourceChanges: []*tfjson.ResourceChange{
+ {
+ Address: "resource1",
+ Type: "example_resource",
+ Change: &tfjson.Change{
+ Actions: tfjson.Actions{tfjson.ActionDelete, tfjson.ActionCreate},
+ ReplacePaths: []interface{}{"path1"},
+ },
+ },
+ },
+ },
+ expected: resourceReplacements{
+ "resource1": {"path1"},
+ },
+ },
+ {
+ name: "valid replacements - multiple paths",
+ plan: &tfjson.Plan{
+ ResourceChanges: []*tfjson.ResourceChange{
+ {
+ Address: "resource1",
+ Type: "example_resource",
+ Change: &tfjson.Change{
+ Actions: tfjson.Actions{tfjson.ActionDelete, tfjson.ActionCreate},
+ ReplacePaths: []interface{}{"path1", "path2"},
+ },
+ },
+ },
+ },
+ expected: resourceReplacements{
+ "resource1": {"path1", "path2"},
+ },
+ },
+ {
+ name: "complex replace path",
+ plan: &tfjson.Plan{
+ ResourceChanges: []*tfjson.ResourceChange{
+ {
+ Address: "resource1",
+ Type: "example_resource",
+ Change: &tfjson.Change{
+ Actions: tfjson.Actions{tfjson.ActionDelete, tfjson.ActionCreate},
+ ReplacePaths: []interface{}{
+ []interface{}{"path", "to", "key"},
+ },
+ },
+ },
+ },
+ },
+ expected: resourceReplacements{
+ "resource1": {"path.to.key"},
+ },
+ },
+ {
+ name: "multiple changes",
+ plan: &tfjson.Plan{
+ ResourceChanges: []*tfjson.ResourceChange{
+ {
+ Address: "resource1",
+ Type: "example_resource",
+ Change: &tfjson.Change{
+ Actions: tfjson.Actions{tfjson.ActionDelete, tfjson.ActionCreate},
+ ReplacePaths: []interface{}{"path1"},
+ },
+ },
+ {
+ Address: "resource2",
+ Type: "example_resource",
+ Change: &tfjson.Change{
+ Actions: tfjson.Actions{tfjson.ActionDelete, tfjson.ActionCreate},
+ ReplacePaths: []interface{}{"path2", "path3"},
+ },
+ },
+ {
+ Address: "resource3",
+ Type: "coder_example",
+ Change: &tfjson.Change{
+ Actions: tfjson.Actions{tfjson.ActionDelete, tfjson.ActionCreate},
+ ReplacePaths: []interface{}{"ignored_path"},
+ },
+ },
+ },
+ },
+ expected: resourceReplacements{
+ "resource1": {"path1"},
+ "resource2": {"path2", "path3"},
+ },
+ },
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+
+ require.EqualValues(t, tc.expected, findResourceReplacements(tc.plan))
+ })
+ }
+}
diff --git a/provisionerd/proto/provisionerd.pb.go b/provisionerd/proto/provisionerd.pb.go
index 9e41e8a428758..475b9a0e564ff 100644
--- a/provisionerd/proto/provisionerd.pb.go
+++ b/provisionerd/proto/provisionerd.pb.go
@@ -1212,10 +1212,11 @@ type CompletedJob_WorkspaceBuild struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
- State []byte `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"`
- Resources []*proto.Resource `protobuf:"bytes,2,rep,name=resources,proto3" json:"resources,omitempty"`
- Timings []*proto.Timing `protobuf:"bytes,3,rep,name=timings,proto3" json:"timings,omitempty"`
- Modules []*proto.Module `protobuf:"bytes,4,rep,name=modules,proto3" json:"modules,omitempty"`
+ State []byte `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"`
+ Resources []*proto.Resource `protobuf:"bytes,2,rep,name=resources,proto3" json:"resources,omitempty"`
+ Timings []*proto.Timing `protobuf:"bytes,3,rep,name=timings,proto3" json:"timings,omitempty"`
+ Modules []*proto.Module `protobuf:"bytes,4,rep,name=modules,proto3" json:"modules,omitempty"`
+ ResourceReplacements []*proto.ResourceReplacement `protobuf:"bytes,5,rep,name=resource_replacements,json=resourceReplacements,proto3" json:"resource_replacements,omitempty"`
}
func (x *CompletedJob_WorkspaceBuild) Reset() {
@@ -1278,6 +1279,13 @@ func (x *CompletedJob_WorkspaceBuild) GetModules() []*proto.Module {
return nil
}
+func (x *CompletedJob_WorkspaceBuild) GetResourceReplacements() []*proto.ResourceReplacement {
+ if x != nil {
+ return x.ResourceReplacements
+ }
+ return nil
+}
+
type CompletedJob_TemplateImport struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -1572,7 +1580,7 @@ var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{
0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73,
0x1a, 0x10, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f,
0x72, 0x74, 0x1a, 0x10, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72,
- 0x79, 0x52, 0x75, 0x6e, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x93, 0x09, 0x0a,
+ 0x79, 0x52, 0x75, 0x6e, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xea, 0x09, 0x0a,
0x0c, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x12, 0x15, 0x0a,
0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a,
0x6f, 0x62, 0x49, 0x64, 0x12, 0x54, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
@@ -1591,7 +1599,7 @@ var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{
0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65,
0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44,
0x72, 0x79, 0x52, 0x75, 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74,
- 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x1a, 0xb9, 0x01, 0x0a, 0x0e, 0x57, 0x6f, 0x72, 0x6b,
+ 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x1a, 0x90, 0x02, 0x0a, 0x0e, 0x57, 0x6f, 0x72, 0x6b,
0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74,
0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65,
0x12, 0x33, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20,
@@ -1603,143 +1611,148 @@ var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{
0x69, 0x6e, 0x67, 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18,
0x04, 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, 0x1a, 0xae, 0x04, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65,
- 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x3e, 0x0a, 0x0f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f,
- 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
- 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65,
- 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73,
- 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x0e, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x72,
- 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15,
- 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73,
- 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0d, 0x73, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75,
- 0x72, 0x63, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x72, 0x69, 0x63, 0x68, 0x5f, 0x70, 0x61, 0x72,
- 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
- 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68,
- 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x52, 0x0e, 0x72, 0x69, 0x63, 0x68, 0x50,
- 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x41, 0x0a, 0x1d, 0x65, 0x78, 0x74,
- 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69,
- 0x64, 0x65, 0x72, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09,
- 0x52, 0x1a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72,
- 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x4e, 0x61, 0x6d, 0x65, 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,
- 0x38, 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73,
- 0x18, 0x06, 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, 0x0c, 0x73, 0x74, 0x61,
- 0x72, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x36, 0x0a, 0x0c, 0x73, 0x74, 0x6f,
- 0x70, 0x5f, 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, 0x0b, 0x73, 0x74, 0x6f, 0x70, 0x4d, 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, 0x1a, 0x74, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65,
- 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x33, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
- 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76,
- 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
- 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x6d,
- 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x02, 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, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79,
- 0x70, 0x65, 0x22, 0xb0, 0x01, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x2f, 0x0a, 0x06, 0x73, 0x6f,
- 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f,
- 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x4c, 0x6f, 0x67, 0x53, 0x6f, 0x75,
- 0x72, 0x63, 0x65, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x6c,
- 0x65, 0x76, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f,
- 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65,
- 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61,
- 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72,
- 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65,
- 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a,
- 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f,
- 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0xa6, 0x03, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
- 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f,
- 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49,
- 0x64, 0x12, 0x25, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
- 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x4c,
- 0x6f, 0x67, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x4c, 0x0a, 0x12, 0x74, 0x65, 0x6d, 0x70,
- 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x04,
- 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
- 0x65, 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61,
- 0x62, 0x6c, 0x65, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72,
- 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x4c, 0x0a, 0x14, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x76,
- 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x05,
+ 0x6c, 0x65, 0x73, 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, 0x1a, 0xae, 0x04, 0x0a, 0x0e, 0x54,
+ 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x3e, 0x0a,
+ 0x0f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
+ 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
+ 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0e, 0x73,
+ 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3c, 0x0a,
+ 0x0e, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18,
+ 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
+ 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0d, 0x73, 0x74,
+ 0x6f, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x72,
+ 0x69, 0x63, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
- 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65,
- 0x52, 0x12, 0x75, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61,
- 0x6c, 0x75, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x18, 0x06,
- 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x12, 0x58, 0x0a, 0x0e,
- 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x07,
- 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
- 0x65, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71,
- 0x75, 0x65, 0x73, 0x74, 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, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x7a,
- 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f,
- 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x18,
- 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x12,
- 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, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0x4a, 0x0a, 0x12, 0x43, 0x6f,
- 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
- 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x61, 0x69, 0x6c, 0x79,
- 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x64, 0x61, 0x69,
- 0x6c, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x22, 0x68, 0x0a, 0x13, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
- 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a,
- 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x12, 0x29, 0x0a,
- 0x10, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65,
- 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x73,
- 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x64, 0x67,
- 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74,
- 0x22, 0x0f, 0x0a, 0x0d, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72,
- 0x65, 0x2a, 0x34, 0x0a, 0x09, 0x4c, 0x6f, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x16,
- 0x0a, 0x12, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x52, 0x5f, 0x44, 0x41,
- 0x45, 0x4d, 0x4f, 0x4e, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53,
- 0x49, 0x4f, 0x4e, 0x45, 0x52, 0x10, 0x01, 0x32, 0xc5, 0x03, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x76,
- 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x41, 0x0a,
- 0x0a, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x13, 0x2e, 0x70, 0x72,
- 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
- 0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e,
- 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x22, 0x03, 0x88, 0x02, 0x01,
- 0x12, 0x52, 0x0a, 0x14, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x4a, 0x6f, 0x62, 0x57, 0x69,
- 0x74, 0x68, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x12, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
- 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x63,
- 0x71, 0x75, 0x69, 0x72, 0x65, 0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
- 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x4a, 0x6f, 0x62,
- 0x28, 0x01, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75,
- 0x6f, 0x74, 0x61, 0x12, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
- 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65,
- 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
- 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61,
- 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x09, 0x55, 0x70, 0x64, 0x61,
- 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
- 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65,
- 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
- 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65,
- 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x46, 0x61, 0x69, 0x6c, 0x4a, 0x6f,
- 0x62, 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64,
- 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f,
- 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12,
- 0x3e, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1a,
- 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f,
- 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f,
- 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42,
- 0x2e, 0x5a, 0x2c, 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, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
- 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72,
+ 0x52, 0x0e, 0x72, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73,
+ 0x12, 0x41, 0x0a, 0x1d, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61, 0x75, 0x74,
+ 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
+ 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x1a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61,
+ 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x4e, 0x61,
+ 0x6d, 0x65, 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, 0x38, 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f,
+ 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x06, 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, 0x0c, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73,
+ 0x12, 0x36, 0x0a, 0x0c, 0x73, 0x74, 0x6f, 0x70, 0x5f, 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, 0x0b, 0x73, 0x74, 0x6f,
+ 0x70, 0x4d, 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, 0x1a, 0x74, 0x0a, 0x0e, 0x54,
+ 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x33, 0x0a,
+ 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
+ 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52,
+ 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
+ 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x02, 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, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xb0, 0x01, 0x0a, 0x03, 0x4c, 0x6f,
+ 0x67, 0x12, 0x2f, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0e, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64,
+ 0x2e, 0x4c, 0x6f, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72,
+ 0x63, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x0e, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
+ 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12,
+ 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20,
+ 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x14,
+ 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73,
+ 0x74, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x05,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0xa6, 0x03, 0x0a,
+ 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+ 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73,
+ 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
+ 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x12,
+ 0x4c, 0x0a, 0x12, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x72, 0x69,
+ 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72,
+ 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61,
+ 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70,
+ 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x4c, 0x0a,
+ 0x14, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76,
+ 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72,
+ 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62,
+ 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x75, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72,
+ 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72,
+ 0x65, 0x61, 0x64, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, 0x65, 0x61,
+ 0x64, 0x6d, 0x65, 0x12, 0x58, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
+ 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x70, 0x72,
+ 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74,
+ 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 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, 0x4a,
+ 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x7a, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a,
+ 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61,
+ 0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x63, 0x61,
+ 0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x12, 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, 0x4a, 0x04, 0x08, 0x02, 0x10,
+ 0x03, 0x22, 0x4a, 0x0a, 0x12, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61,
+ 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69,
+ 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x1d,
+ 0x0a, 0x0a, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01,
+ 0x28, 0x05, 0x52, 0x09, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x22, 0x68, 0x0a,
+ 0x13, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70,
+ 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
+ 0x52, 0x02, 0x6f, 0x6b, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x73, 0x5f,
+ 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f,
+ 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x12,
+ 0x16, 0x0a, 0x06, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52,
+ 0x06, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x22, 0x0f, 0x0a, 0x0d, 0x43, 0x61, 0x6e, 0x63, 0x65,
+ 0x6c, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x2a, 0x34, 0x0a, 0x09, 0x4c, 0x6f, 0x67, 0x53,
+ 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53, 0x49,
+ 0x4f, 0x4e, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x45, 0x4d, 0x4f, 0x4e, 0x10, 0x00, 0x12, 0x0f, 0x0a,
+ 0x0b, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x52, 0x10, 0x01, 0x32, 0xc5,
+ 0x03, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x44, 0x61,
+ 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x41, 0x0a, 0x0a, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x4a,
+ 0x6f, 0x62, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
+ 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73,
+ 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x4a,
+ 0x6f, 0x62, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x52, 0x0a, 0x14, 0x41, 0x63, 0x71, 0x75, 0x69,
+ 0x72, 0x65, 0x4a, 0x6f, 0x62, 0x57, 0x69, 0x74, 0x68, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x12,
+ 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43,
+ 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x1a, 0x19, 0x2e, 0x70,
+ 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x41, 0x63, 0x71, 0x75,
+ 0x69, 0x72, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x28, 0x01, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0b, 0x43,
+ 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x12, 0x20, 0x2e, 0x70, 0x72, 0x6f,
+ 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x70,
+ 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d,
+ 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
+ 0x4c, 0x0a, 0x09, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1e, 0x2e, 0x70,
+ 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x64, 0x61,
+ 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70,
+ 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x64, 0x61,
+ 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a,
+ 0x07, 0x46, 0x61, 0x69, 0x6c, 0x4a, 0x6f, 0x62, 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
+ 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f,
+ 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64,
+ 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3e, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65,
+ 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
+ 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f,
+ 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64,
+ 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x2e, 0x5a, 0x2c, 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, 0x64,
+ 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -1788,9 +1801,10 @@ var file_provisionerd_proto_provisionerd_proto_goTypes = []interface{}{
(*proto.Timing)(nil), // 28: provisioner.Timing
(*proto.Resource)(nil), // 29: provisioner.Resource
(*proto.Module)(nil), // 30: provisioner.Module
- (*proto.RichParameter)(nil), // 31: provisioner.RichParameter
- (*proto.ExternalAuthProviderResource)(nil), // 32: provisioner.ExternalAuthProviderResource
- (*proto.Preset)(nil), // 33: provisioner.Preset
+ (*proto.ResourceReplacement)(nil), // 31: provisioner.ResourceReplacement
+ (*proto.RichParameter)(nil), // 32: provisioner.RichParameter
+ (*proto.ExternalAuthProviderResource)(nil), // 33: provisioner.ExternalAuthProviderResource
+ (*proto.Preset)(nil), // 34: provisioner.Preset
}
var file_provisionerd_proto_provisionerd_proto_depIdxs = []int32{
11, // 0: provisionerd.AcquiredJob.workspace_build:type_name -> provisionerd.AcquiredJob.WorkspaceBuild
@@ -1823,32 +1837,33 @@ var file_provisionerd_proto_provisionerd_proto_depIdxs = []int32{
29, // 27: provisionerd.CompletedJob.WorkspaceBuild.resources:type_name -> provisioner.Resource
28, // 28: provisionerd.CompletedJob.WorkspaceBuild.timings:type_name -> provisioner.Timing
30, // 29: provisionerd.CompletedJob.WorkspaceBuild.modules:type_name -> provisioner.Module
- 29, // 30: provisionerd.CompletedJob.TemplateImport.start_resources:type_name -> provisioner.Resource
- 29, // 31: provisionerd.CompletedJob.TemplateImport.stop_resources:type_name -> provisioner.Resource
- 31, // 32: provisionerd.CompletedJob.TemplateImport.rich_parameters:type_name -> provisioner.RichParameter
- 32, // 33: provisionerd.CompletedJob.TemplateImport.external_auth_providers:type_name -> provisioner.ExternalAuthProviderResource
- 30, // 34: provisionerd.CompletedJob.TemplateImport.start_modules:type_name -> provisioner.Module
- 30, // 35: provisionerd.CompletedJob.TemplateImport.stop_modules:type_name -> provisioner.Module
- 33, // 36: provisionerd.CompletedJob.TemplateImport.presets:type_name -> provisioner.Preset
- 29, // 37: provisionerd.CompletedJob.TemplateDryRun.resources:type_name -> provisioner.Resource
- 30, // 38: provisionerd.CompletedJob.TemplateDryRun.modules:type_name -> provisioner.Module
- 1, // 39: provisionerd.ProvisionerDaemon.AcquireJob:input_type -> provisionerd.Empty
- 10, // 40: provisionerd.ProvisionerDaemon.AcquireJobWithCancel:input_type -> provisionerd.CancelAcquire
- 8, // 41: provisionerd.ProvisionerDaemon.CommitQuota:input_type -> provisionerd.CommitQuotaRequest
- 6, // 42: provisionerd.ProvisionerDaemon.UpdateJob:input_type -> provisionerd.UpdateJobRequest
- 3, // 43: provisionerd.ProvisionerDaemon.FailJob:input_type -> provisionerd.FailedJob
- 4, // 44: provisionerd.ProvisionerDaemon.CompleteJob:input_type -> provisionerd.CompletedJob
- 2, // 45: provisionerd.ProvisionerDaemon.AcquireJob:output_type -> provisionerd.AcquiredJob
- 2, // 46: provisionerd.ProvisionerDaemon.AcquireJobWithCancel:output_type -> provisionerd.AcquiredJob
- 9, // 47: provisionerd.ProvisionerDaemon.CommitQuota:output_type -> provisionerd.CommitQuotaResponse
- 7, // 48: provisionerd.ProvisionerDaemon.UpdateJob:output_type -> provisionerd.UpdateJobResponse
- 1, // 49: provisionerd.ProvisionerDaemon.FailJob:output_type -> provisionerd.Empty
- 1, // 50: provisionerd.ProvisionerDaemon.CompleteJob:output_type -> provisionerd.Empty
- 45, // [45:51] is the sub-list for method output_type
- 39, // [39:45] is the sub-list for method input_type
- 39, // [39:39] is the sub-list for extension type_name
- 39, // [39:39] is the sub-list for extension extendee
- 0, // [0:39] is the sub-list for field type_name
+ 31, // 30: provisionerd.CompletedJob.WorkspaceBuild.resource_replacements:type_name -> provisioner.ResourceReplacement
+ 29, // 31: provisionerd.CompletedJob.TemplateImport.start_resources:type_name -> provisioner.Resource
+ 29, // 32: provisionerd.CompletedJob.TemplateImport.stop_resources:type_name -> provisioner.Resource
+ 32, // 33: provisionerd.CompletedJob.TemplateImport.rich_parameters:type_name -> provisioner.RichParameter
+ 33, // 34: provisionerd.CompletedJob.TemplateImport.external_auth_providers:type_name -> provisioner.ExternalAuthProviderResource
+ 30, // 35: provisionerd.CompletedJob.TemplateImport.start_modules:type_name -> provisioner.Module
+ 30, // 36: provisionerd.CompletedJob.TemplateImport.stop_modules:type_name -> provisioner.Module
+ 34, // 37: provisionerd.CompletedJob.TemplateImport.presets:type_name -> provisioner.Preset
+ 29, // 38: provisionerd.CompletedJob.TemplateDryRun.resources:type_name -> provisioner.Resource
+ 30, // 39: provisionerd.CompletedJob.TemplateDryRun.modules:type_name -> provisioner.Module
+ 1, // 40: provisionerd.ProvisionerDaemon.AcquireJob:input_type -> provisionerd.Empty
+ 10, // 41: provisionerd.ProvisionerDaemon.AcquireJobWithCancel:input_type -> provisionerd.CancelAcquire
+ 8, // 42: provisionerd.ProvisionerDaemon.CommitQuota:input_type -> provisionerd.CommitQuotaRequest
+ 6, // 43: provisionerd.ProvisionerDaemon.UpdateJob:input_type -> provisionerd.UpdateJobRequest
+ 3, // 44: provisionerd.ProvisionerDaemon.FailJob:input_type -> provisionerd.FailedJob
+ 4, // 45: provisionerd.ProvisionerDaemon.CompleteJob:input_type -> provisionerd.CompletedJob
+ 2, // 46: provisionerd.ProvisionerDaemon.AcquireJob:output_type -> provisionerd.AcquiredJob
+ 2, // 47: provisionerd.ProvisionerDaemon.AcquireJobWithCancel:output_type -> provisionerd.AcquiredJob
+ 9, // 48: provisionerd.ProvisionerDaemon.CommitQuota:output_type -> provisionerd.CommitQuotaResponse
+ 7, // 49: provisionerd.ProvisionerDaemon.UpdateJob:output_type -> provisionerd.UpdateJobResponse
+ 1, // 50: provisionerd.ProvisionerDaemon.FailJob:output_type -> provisionerd.Empty
+ 1, // 51: provisionerd.ProvisionerDaemon.CompleteJob:output_type -> provisionerd.Empty
+ 46, // [46:52] is the sub-list for method output_type
+ 40, // [40:46] is the sub-list for method input_type
+ 40, // [40:40] is the sub-list for extension type_name
+ 40, // [40:40] is the sub-list for extension extendee
+ 0, // [0:40] is the sub-list for field type_name
}
func init() { file_provisionerd_proto_provisionerd_proto_init() }
diff --git a/provisionerd/proto/provisionerd.proto b/provisionerd/proto/provisionerd.proto
index 7db8c807151fb..0e50718ce75de 100644
--- a/provisionerd/proto/provisionerd.proto
+++ b/provisionerd/proto/provisionerd.proto
@@ -75,6 +75,7 @@ message CompletedJob {
repeated provisioner.Resource resources = 2;
repeated provisioner.Timing timings = 3;
repeated provisioner.Module modules = 4;
+ repeated provisioner.ResourceReplacement resource_replacements = 5;
}
message TemplateImport {
repeated provisioner.Resource start_resources = 1;
diff --git a/provisionerd/proto/version.go b/provisionerd/proto/version.go
index d502a1f544fe3..62ece2e02d224 100644
--- a/provisionerd/proto/version.go
+++ b/provisionerd/proto/version.go
@@ -12,12 +12,19 @@ import "github.com/coder/coder/v2/apiversion"
//
// API v1.4:
// - Add new field named `devcontainers` in the Agent.
+//
+// API v1.5:
+// - Add new field named `prebuilt_workspace_build_stage` enum in the Metadata message.
+// - Add new field named `running_agent_auth_tokens` to provisioner job metadata.
+// - Add new field named `resource_replacements` in PlanComplete & CompletedJob.WorkspaceBuild.
const (
CurrentMajor = 1
- CurrentMinor = 4
+ CurrentMinor = 5
)
// CurrentVersion is the current provisionerd API version.
// Breaking changes to the provisionerd API **MUST** increment
// CurrentMajor above.
+// Non-breaking changes to the provisionerd API **MUST** increment
+// CurrentMinor above.
var CurrentVersion = apiversion.New(CurrentMajor, CurrentMinor)
diff --git a/provisionerd/provisionerd.go b/provisionerd/provisionerd.go
index 6635495a2553a..76a06d7fa68b1 100644
--- a/provisionerd/provisionerd.go
+++ b/provisionerd/provisionerd.go
@@ -378,7 +378,7 @@ func (p *Server) acquireAndRunOne(client proto.DRPCProvisionerDaemonClient) erro
slog.F("workspace_build_id", build.WorkspaceBuildId),
slog.F("workspace_id", build.Metadata.WorkspaceId),
slog.F("workspace_name", build.WorkspaceName),
- slog.F("is_prebuild", build.Metadata.IsPrebuild),
+ slog.F("prebuilt_workspace_build_stage", build.Metadata.GetPrebuiltWorkspaceBuildStage().String()),
)
span.SetAttributes(
@@ -388,7 +388,7 @@ func (p *Server) acquireAndRunOne(client proto.DRPCProvisionerDaemonClient) erro
attribute.String("workspace_owner_id", build.Metadata.WorkspaceOwnerId),
attribute.String("workspace_owner", build.Metadata.WorkspaceOwner),
attribute.String("workspace_transition", build.Metadata.WorkspaceTransition.String()),
- attribute.Bool("is_prebuild", build.Metadata.IsPrebuild),
+ attribute.String("prebuilt_workspace_build_stage", build.Metadata.GetPrebuiltWorkspaceBuildStage().String()),
)
}
diff --git a/provisionerd/runner/runner.go b/provisionerd/runner/runner.go
index 70d424c47a0c6..f74836b07faf7 100644
--- a/provisionerd/runner/runner.go
+++ b/provisionerd/runner/runner.go
@@ -1059,6 +1059,8 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p
// called by `plan`. `apply` does not modify them, so we can use the
// modules from the plan response.
Modules: planComplete.Modules,
+ // Resource replacements are discovered at plan time, only.
+ ResourceReplacements: planComplete.ResourceReplacements,
},
},
}, nil
diff --git a/provisionersdk/proto/prebuilt_workspace.go b/provisionersdk/proto/prebuilt_workspace.go
new file mode 100644
index 0000000000000..3aa80512344b6
--- /dev/null
+++ b/provisionersdk/proto/prebuilt_workspace.go
@@ -0,0 +1,9 @@
+package proto
+
+func (p PrebuiltWorkspaceBuildStage) IsPrebuild() bool {
+ return p == PrebuiltWorkspaceBuildStage_CREATE
+}
+
+func (p PrebuiltWorkspaceBuildStage) IsPrebuiltWorkspaceClaim() bool {
+ return p == PrebuiltWorkspaceBuildStage_CLAIM
+}
diff --git a/provisionersdk/proto/provisioner.pb.go b/provisionersdk/proto/provisioner.pb.go
index f258f79e36f94..c1286e6f9fe0e 100644
--- a/provisionersdk/proto/provisioner.pb.go
+++ b/provisionersdk/proto/provisioner.pb.go
@@ -226,6 +226,55 @@ func (WorkspaceTransition) EnumDescriptor() ([]byte, []int) {
return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{3}
}
+type PrebuiltWorkspaceBuildStage int32
+
+const (
+ PrebuiltWorkspaceBuildStage_NONE PrebuiltWorkspaceBuildStage = 0 // Default value for builds unrelated to prebuilds.
+ PrebuiltWorkspaceBuildStage_CREATE PrebuiltWorkspaceBuildStage = 1 // A prebuilt workspace is being provisioned.
+ PrebuiltWorkspaceBuildStage_CLAIM PrebuiltWorkspaceBuildStage = 2 // A prebuilt workspace is being claimed.
+)
+
+// Enum value maps for PrebuiltWorkspaceBuildStage.
+var (
+ PrebuiltWorkspaceBuildStage_name = map[int32]string{
+ 0: "NONE",
+ 1: "CREATE",
+ 2: "CLAIM",
+ }
+ PrebuiltWorkspaceBuildStage_value = map[string]int32{
+ "NONE": 0,
+ "CREATE": 1,
+ "CLAIM": 2,
+ }
+)
+
+func (x PrebuiltWorkspaceBuildStage) Enum() *PrebuiltWorkspaceBuildStage {
+ p := new(PrebuiltWorkspaceBuildStage)
+ *p = x
+ return p
+}
+
+func (x PrebuiltWorkspaceBuildStage) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (PrebuiltWorkspaceBuildStage) Descriptor() protoreflect.EnumDescriptor {
+ return file_provisionersdk_proto_provisioner_proto_enumTypes[4].Descriptor()
+}
+
+func (PrebuiltWorkspaceBuildStage) Type() protoreflect.EnumType {
+ return &file_provisionersdk_proto_provisioner_proto_enumTypes[4]
+}
+
+func (x PrebuiltWorkspaceBuildStage) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use PrebuiltWorkspaceBuildStage.Descriptor instead.
+func (PrebuiltWorkspaceBuildStage) EnumDescriptor() ([]byte, []int) {
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{4}
+}
+
type TimingState int32
const (
@@ -259,11 +308,11 @@ func (x TimingState) String() string {
}
func (TimingState) Descriptor() protoreflect.EnumDescriptor {
- return file_provisionersdk_proto_provisioner_proto_enumTypes[4].Descriptor()
+ return file_provisionersdk_proto_provisioner_proto_enumTypes[5].Descriptor()
}
func (TimingState) Type() protoreflect.EnumType {
- return &file_provisionersdk_proto_provisioner_proto_enumTypes[4]
+ return &file_provisionersdk_proto_provisioner_proto_enumTypes[5]
}
func (x TimingState) Number() protoreflect.EnumNumber {
@@ -272,7 +321,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{4}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{5}
}
// Empty indicates a successful request/response.
@@ -865,6 +914,61 @@ func (x *PresetParameter) GetValue() string {
return ""
}
+type ResourceReplacement struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Resource string `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"`
+ Paths []string `protobuf:"bytes,2,rep,name=paths,proto3" json:"paths,omitempty"`
+}
+
+func (x *ResourceReplacement) Reset() {
+ *x = ResourceReplacement{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[8]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *ResourceReplacement) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ResourceReplacement) ProtoMessage() {}
+
+func (x *ResourceReplacement) ProtoReflect() protoreflect.Message {
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[8]
+ 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 ResourceReplacement.ProtoReflect.Descriptor instead.
+func (*ResourceReplacement) Descriptor() ([]byte, []int) {
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{8}
+}
+
+func (x *ResourceReplacement) GetResource() string {
+ if x != nil {
+ return x.Resource
+ }
+ return ""
+}
+
+func (x *ResourceReplacement) GetPaths() []string {
+ if x != nil {
+ return x.Paths
+ }
+ return nil
+}
+
// VariableValue holds the key/value mapping of a Terraform variable.
type VariableValue struct {
state protoimpl.MessageState
@@ -879,7 +983,7 @@ type VariableValue struct {
func (x *VariableValue) Reset() {
*x = VariableValue{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[8]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -892,7 +996,7 @@ func (x *VariableValue) String() string {
func (*VariableValue) ProtoMessage() {}
func (x *VariableValue) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[8]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[9]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -905,7 +1009,7 @@ func (x *VariableValue) ProtoReflect() protoreflect.Message {
// Deprecated: Use VariableValue.ProtoReflect.Descriptor instead.
func (*VariableValue) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{8}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{9}
}
func (x *VariableValue) GetName() string {
@@ -942,7 +1046,7 @@ type Log struct {
func (x *Log) Reset() {
*x = Log{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[9]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -955,7 +1059,7 @@ func (x *Log) String() string {
func (*Log) ProtoMessage() {}
func (x *Log) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[9]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[10]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -968,7 +1072,7 @@ func (x *Log) ProtoReflect() protoreflect.Message {
// Deprecated: Use Log.ProtoReflect.Descriptor instead.
func (*Log) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{9}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{10}
}
func (x *Log) GetLevel() LogLevel {
@@ -996,7 +1100,7 @@ type InstanceIdentityAuth struct {
func (x *InstanceIdentityAuth) Reset() {
*x = InstanceIdentityAuth{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[10]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1009,7 +1113,7 @@ func (x *InstanceIdentityAuth) String() string {
func (*InstanceIdentityAuth) ProtoMessage() {}
func (x *InstanceIdentityAuth) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[10]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[11]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1022,7 +1126,7 @@ func (x *InstanceIdentityAuth) ProtoReflect() protoreflect.Message {
// Deprecated: Use InstanceIdentityAuth.ProtoReflect.Descriptor instead.
func (*InstanceIdentityAuth) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{10}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{11}
}
func (x *InstanceIdentityAuth) GetInstanceId() string {
@@ -1044,7 +1148,7 @@ type ExternalAuthProviderResource struct {
func (x *ExternalAuthProviderResource) Reset() {
*x = ExternalAuthProviderResource{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[11]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[12]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1057,7 +1161,7 @@ func (x *ExternalAuthProviderResource) String() string {
func (*ExternalAuthProviderResource) ProtoMessage() {}
func (x *ExternalAuthProviderResource) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[11]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[12]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1070,7 +1174,7 @@ func (x *ExternalAuthProviderResource) ProtoReflect() protoreflect.Message {
// Deprecated: Use ExternalAuthProviderResource.ProtoReflect.Descriptor instead.
func (*ExternalAuthProviderResource) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{11}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{12}
}
func (x *ExternalAuthProviderResource) GetId() string {
@@ -1099,7 +1203,7 @@ type ExternalAuthProvider struct {
func (x *ExternalAuthProvider) Reset() {
*x = ExternalAuthProvider{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[12]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[13]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1112,7 +1216,7 @@ func (x *ExternalAuthProvider) String() string {
func (*ExternalAuthProvider) ProtoMessage() {}
func (x *ExternalAuthProvider) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[12]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[13]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1125,7 +1229,7 @@ func (x *ExternalAuthProvider) ProtoReflect() protoreflect.Message {
// Deprecated: Use ExternalAuthProvider.ProtoReflect.Descriptor instead.
func (*ExternalAuthProvider) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{12}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{13}
}
func (x *ExternalAuthProvider) GetId() string {
@@ -1179,7 +1283,7 @@ type Agent struct {
func (x *Agent) Reset() {
*x = Agent{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[13]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1192,7 +1296,7 @@ func (x *Agent) String() string {
func (*Agent) ProtoMessage() {}
func (x *Agent) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[13]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[14]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1205,7 +1309,7 @@ func (x *Agent) ProtoReflect() protoreflect.Message {
// Deprecated: Use Agent.ProtoReflect.Descriptor instead.
func (*Agent) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{13}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{14}
}
func (x *Agent) GetId() string {
@@ -1376,7 +1480,7 @@ type ResourcesMonitoring struct {
func (x *ResourcesMonitoring) Reset() {
*x = ResourcesMonitoring{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[14]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[15]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1389,7 +1493,7 @@ func (x *ResourcesMonitoring) String() string {
func (*ResourcesMonitoring) ProtoMessage() {}
func (x *ResourcesMonitoring) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[14]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[15]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1402,7 +1506,7 @@ func (x *ResourcesMonitoring) ProtoReflect() protoreflect.Message {
// Deprecated: Use ResourcesMonitoring.ProtoReflect.Descriptor instead.
func (*ResourcesMonitoring) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{14}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{15}
}
func (x *ResourcesMonitoring) GetMemory() *MemoryResourceMonitor {
@@ -1431,7 +1535,7 @@ type MemoryResourceMonitor struct {
func (x *MemoryResourceMonitor) Reset() {
*x = MemoryResourceMonitor{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[15]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[16]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1444,7 +1548,7 @@ func (x *MemoryResourceMonitor) String() string {
func (*MemoryResourceMonitor) ProtoMessage() {}
func (x *MemoryResourceMonitor) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[15]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[16]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1457,7 +1561,7 @@ func (x *MemoryResourceMonitor) ProtoReflect() protoreflect.Message {
// Deprecated: Use MemoryResourceMonitor.ProtoReflect.Descriptor instead.
func (*MemoryResourceMonitor) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{15}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{16}
}
func (x *MemoryResourceMonitor) GetEnabled() bool {
@@ -1487,7 +1591,7 @@ type VolumeResourceMonitor struct {
func (x *VolumeResourceMonitor) Reset() {
*x = VolumeResourceMonitor{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[16]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[17]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1500,7 +1604,7 @@ func (x *VolumeResourceMonitor) String() string {
func (*VolumeResourceMonitor) ProtoMessage() {}
func (x *VolumeResourceMonitor) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[16]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[17]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1513,7 +1617,7 @@ func (x *VolumeResourceMonitor) ProtoReflect() protoreflect.Message {
// Deprecated: Use VolumeResourceMonitor.ProtoReflect.Descriptor instead.
func (*VolumeResourceMonitor) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{16}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{17}
}
func (x *VolumeResourceMonitor) GetPath() string {
@@ -1552,7 +1656,7 @@ type DisplayApps struct {
func (x *DisplayApps) Reset() {
*x = DisplayApps{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[17]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[18]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1565,7 +1669,7 @@ func (x *DisplayApps) String() string {
func (*DisplayApps) ProtoMessage() {}
func (x *DisplayApps) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[17]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[18]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1578,7 +1682,7 @@ func (x *DisplayApps) ProtoReflect() protoreflect.Message {
// Deprecated: Use DisplayApps.ProtoReflect.Descriptor instead.
func (*DisplayApps) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{17}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{18}
}
func (x *DisplayApps) GetVscode() bool {
@@ -1628,7 +1732,7 @@ type Env struct {
func (x *Env) Reset() {
*x = Env{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[18]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[19]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1641,7 +1745,7 @@ func (x *Env) String() string {
func (*Env) ProtoMessage() {}
func (x *Env) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[18]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[19]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1654,7 +1758,7 @@ func (x *Env) ProtoReflect() protoreflect.Message {
// Deprecated: Use Env.ProtoReflect.Descriptor instead.
func (*Env) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{18}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{19}
}
func (x *Env) GetName() string {
@@ -1691,7 +1795,7 @@ type Script struct {
func (x *Script) Reset() {
*x = Script{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[19]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[20]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1704,7 +1808,7 @@ func (x *Script) String() string {
func (*Script) ProtoMessage() {}
func (x *Script) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[19]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[20]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1717,7 +1821,7 @@ func (x *Script) ProtoReflect() protoreflect.Message {
// Deprecated: Use Script.ProtoReflect.Descriptor instead.
func (*Script) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{19}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{20}
}
func (x *Script) GetDisplayName() string {
@@ -1796,7 +1900,7 @@ type Devcontainer struct {
func (x *Devcontainer) Reset() {
*x = Devcontainer{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[20]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[21]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1809,7 +1913,7 @@ func (x *Devcontainer) String() string {
func (*Devcontainer) ProtoMessage() {}
func (x *Devcontainer) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[20]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[21]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1822,7 +1926,7 @@ func (x *Devcontainer) ProtoReflect() protoreflect.Message {
// Deprecated: Use Devcontainer.ProtoReflect.Descriptor instead.
func (*Devcontainer) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{20}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{21}
}
func (x *Devcontainer) GetWorkspaceFolder() string {
@@ -1871,7 +1975,7 @@ type App struct {
func (x *App) Reset() {
*x = App{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[21]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[22]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1884,7 +1988,7 @@ func (x *App) String() string {
func (*App) ProtoMessage() {}
func (x *App) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[21]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[22]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1897,7 +2001,7 @@ func (x *App) ProtoReflect() protoreflect.Message {
// Deprecated: Use App.ProtoReflect.Descriptor instead.
func (*App) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{21}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{22}
}
func (x *App) GetSlug() string {
@@ -1998,7 +2102,7 @@ type Healthcheck struct {
func (x *Healthcheck) Reset() {
*x = Healthcheck{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[22]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[23]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2011,7 +2115,7 @@ func (x *Healthcheck) String() string {
func (*Healthcheck) ProtoMessage() {}
func (x *Healthcheck) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[22]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[23]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2024,7 +2128,7 @@ func (x *Healthcheck) ProtoReflect() protoreflect.Message {
// Deprecated: Use Healthcheck.ProtoReflect.Descriptor instead.
func (*Healthcheck) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{22}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{23}
}
func (x *Healthcheck) GetUrl() string {
@@ -2068,7 +2172,7 @@ type Resource struct {
func (x *Resource) Reset() {
*x = Resource{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[23]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[24]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2081,7 +2185,7 @@ func (x *Resource) String() string {
func (*Resource) ProtoMessage() {}
func (x *Resource) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[23]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[24]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2094,7 +2198,7 @@ func (x *Resource) ProtoReflect() protoreflect.Message {
// Deprecated: Use Resource.ProtoReflect.Descriptor instead.
func (*Resource) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{23}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{24}
}
func (x *Resource) GetName() string {
@@ -2173,7 +2277,7 @@ type Module struct {
func (x *Module) Reset() {
*x = Module{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[24]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[25]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2186,7 +2290,7 @@ func (x *Module) String() string {
func (*Module) ProtoMessage() {}
func (x *Module) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[24]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[25]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2199,7 +2303,7 @@ func (x *Module) ProtoReflect() protoreflect.Message {
// Deprecated: Use Module.ProtoReflect.Descriptor instead.
func (*Module) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{24}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{25}
}
func (x *Module) GetSource() string {
@@ -2235,7 +2339,7 @@ type Role struct {
func (x *Role) Reset() {
*x = Role{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[25]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[26]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2248,7 +2352,7 @@ func (x *Role) String() string {
func (*Role) ProtoMessage() {}
func (x *Role) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[25]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[26]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2261,7 +2365,7 @@ func (x *Role) ProtoReflect() protoreflect.Message {
// Deprecated: Use Role.ProtoReflect.Descriptor instead.
func (*Role) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{25}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{26}
}
func (x *Role) GetName() string {
@@ -2278,39 +2382,94 @@ func (x *Role) GetOrgId() string {
return ""
}
+type RunningAgentAuthToken struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ AgentId string `protobuf:"bytes,1,opt,name=agent_id,json=agentId,proto3" json:"agent_id,omitempty"`
+ Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"`
+}
+
+func (x *RunningAgentAuthToken) Reset() {
+ *x = RunningAgentAuthToken{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[27]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *RunningAgentAuthToken) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*RunningAgentAuthToken) ProtoMessage() {}
+
+func (x *RunningAgentAuthToken) ProtoReflect() protoreflect.Message {
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[27]
+ 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 RunningAgentAuthToken.ProtoReflect.Descriptor instead.
+func (*RunningAgentAuthToken) Descriptor() ([]byte, []int) {
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{27}
+}
+
+func (x *RunningAgentAuthToken) GetAgentId() string {
+ if x != nil {
+ return x.AgentId
+ }
+ return ""
+}
+
+func (x *RunningAgentAuthToken) GetToken() string {
+ if x != nil {
+ return x.Token
+ }
+ return ""
+}
+
// Metadata is information about a workspace used in the execution of a build
type Metadata struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
- CoderUrl string `protobuf:"bytes,1,opt,name=coder_url,json=coderUrl,proto3" json:"coder_url,omitempty"`
- WorkspaceTransition WorkspaceTransition `protobuf:"varint,2,opt,name=workspace_transition,json=workspaceTransition,proto3,enum=provisioner.WorkspaceTransition" json:"workspace_transition,omitempty"`
- WorkspaceName string `protobuf:"bytes,3,opt,name=workspace_name,json=workspaceName,proto3" json:"workspace_name,omitempty"`
- WorkspaceOwner string `protobuf:"bytes,4,opt,name=workspace_owner,json=workspaceOwner,proto3" json:"workspace_owner,omitempty"`
- WorkspaceId string `protobuf:"bytes,5,opt,name=workspace_id,json=workspaceId,proto3" json:"workspace_id,omitempty"`
- WorkspaceOwnerId string `protobuf:"bytes,6,opt,name=workspace_owner_id,json=workspaceOwnerId,proto3" json:"workspace_owner_id,omitempty"`
- WorkspaceOwnerEmail string `protobuf:"bytes,7,opt,name=workspace_owner_email,json=workspaceOwnerEmail,proto3" json:"workspace_owner_email,omitempty"`
- TemplateName string `protobuf:"bytes,8,opt,name=template_name,json=templateName,proto3" json:"template_name,omitempty"`
- TemplateVersion string `protobuf:"bytes,9,opt,name=template_version,json=templateVersion,proto3" json:"template_version,omitempty"`
- WorkspaceOwnerOidcAccessToken string `protobuf:"bytes,10,opt,name=workspace_owner_oidc_access_token,json=workspaceOwnerOidcAccessToken,proto3" json:"workspace_owner_oidc_access_token,omitempty"`
- WorkspaceOwnerSessionToken string `protobuf:"bytes,11,opt,name=workspace_owner_session_token,json=workspaceOwnerSessionToken,proto3" json:"workspace_owner_session_token,omitempty"`
- TemplateId string `protobuf:"bytes,12,opt,name=template_id,json=templateId,proto3" json:"template_id,omitempty"`
- WorkspaceOwnerName string `protobuf:"bytes,13,opt,name=workspace_owner_name,json=workspaceOwnerName,proto3" json:"workspace_owner_name,omitempty"`
- WorkspaceOwnerGroups []string `protobuf:"bytes,14,rep,name=workspace_owner_groups,json=workspaceOwnerGroups,proto3" json:"workspace_owner_groups,omitempty"`
- WorkspaceOwnerSshPublicKey string `protobuf:"bytes,15,opt,name=workspace_owner_ssh_public_key,json=workspaceOwnerSshPublicKey,proto3" json:"workspace_owner_ssh_public_key,omitempty"`
- WorkspaceOwnerSshPrivateKey string `protobuf:"bytes,16,opt,name=workspace_owner_ssh_private_key,json=workspaceOwnerSshPrivateKey,proto3" json:"workspace_owner_ssh_private_key,omitempty"`
- WorkspaceBuildId string `protobuf:"bytes,17,opt,name=workspace_build_id,json=workspaceBuildId,proto3" json:"workspace_build_id,omitempty"`
- WorkspaceOwnerLoginType string `protobuf:"bytes,18,opt,name=workspace_owner_login_type,json=workspaceOwnerLoginType,proto3" json:"workspace_owner_login_type,omitempty"`
- WorkspaceOwnerRbacRoles []*Role `protobuf:"bytes,19,rep,name=workspace_owner_rbac_roles,json=workspaceOwnerRbacRoles,proto3" json:"workspace_owner_rbac_roles,omitempty"`
- IsPrebuild bool `protobuf:"varint,20,opt,name=is_prebuild,json=isPrebuild,proto3" json:"is_prebuild,omitempty"`
- RunningWorkspaceAgentToken string `protobuf:"bytes,21,opt,name=running_workspace_agent_token,json=runningWorkspaceAgentToken,proto3" json:"running_workspace_agent_token,omitempty"`
+ CoderUrl string `protobuf:"bytes,1,opt,name=coder_url,json=coderUrl,proto3" json:"coder_url,omitempty"`
+ WorkspaceTransition WorkspaceTransition `protobuf:"varint,2,opt,name=workspace_transition,json=workspaceTransition,proto3,enum=provisioner.WorkspaceTransition" json:"workspace_transition,omitempty"`
+ WorkspaceName string `protobuf:"bytes,3,opt,name=workspace_name,json=workspaceName,proto3" json:"workspace_name,omitempty"`
+ WorkspaceOwner string `protobuf:"bytes,4,opt,name=workspace_owner,json=workspaceOwner,proto3" json:"workspace_owner,omitempty"`
+ WorkspaceId string `protobuf:"bytes,5,opt,name=workspace_id,json=workspaceId,proto3" json:"workspace_id,omitempty"`
+ WorkspaceOwnerId string `protobuf:"bytes,6,opt,name=workspace_owner_id,json=workspaceOwnerId,proto3" json:"workspace_owner_id,omitempty"`
+ WorkspaceOwnerEmail string `protobuf:"bytes,7,opt,name=workspace_owner_email,json=workspaceOwnerEmail,proto3" json:"workspace_owner_email,omitempty"`
+ TemplateName string `protobuf:"bytes,8,opt,name=template_name,json=templateName,proto3" json:"template_name,omitempty"`
+ TemplateVersion string `protobuf:"bytes,9,opt,name=template_version,json=templateVersion,proto3" json:"template_version,omitempty"`
+ WorkspaceOwnerOidcAccessToken string `protobuf:"bytes,10,opt,name=workspace_owner_oidc_access_token,json=workspaceOwnerOidcAccessToken,proto3" json:"workspace_owner_oidc_access_token,omitempty"`
+ WorkspaceOwnerSessionToken string `protobuf:"bytes,11,opt,name=workspace_owner_session_token,json=workspaceOwnerSessionToken,proto3" json:"workspace_owner_session_token,omitempty"`
+ TemplateId string `protobuf:"bytes,12,opt,name=template_id,json=templateId,proto3" json:"template_id,omitempty"`
+ WorkspaceOwnerName string `protobuf:"bytes,13,opt,name=workspace_owner_name,json=workspaceOwnerName,proto3" json:"workspace_owner_name,omitempty"`
+ WorkspaceOwnerGroups []string `protobuf:"bytes,14,rep,name=workspace_owner_groups,json=workspaceOwnerGroups,proto3" json:"workspace_owner_groups,omitempty"`
+ WorkspaceOwnerSshPublicKey string `protobuf:"bytes,15,opt,name=workspace_owner_ssh_public_key,json=workspaceOwnerSshPublicKey,proto3" json:"workspace_owner_ssh_public_key,omitempty"`
+ WorkspaceOwnerSshPrivateKey string `protobuf:"bytes,16,opt,name=workspace_owner_ssh_private_key,json=workspaceOwnerSshPrivateKey,proto3" json:"workspace_owner_ssh_private_key,omitempty"`
+ WorkspaceBuildId string `protobuf:"bytes,17,opt,name=workspace_build_id,json=workspaceBuildId,proto3" json:"workspace_build_id,omitempty"`
+ WorkspaceOwnerLoginType string `protobuf:"bytes,18,opt,name=workspace_owner_login_type,json=workspaceOwnerLoginType,proto3" json:"workspace_owner_login_type,omitempty"`
+ WorkspaceOwnerRbacRoles []*Role `protobuf:"bytes,19,rep,name=workspace_owner_rbac_roles,json=workspaceOwnerRbacRoles,proto3" json:"workspace_owner_rbac_roles,omitempty"`
+ PrebuiltWorkspaceBuildStage PrebuiltWorkspaceBuildStage `protobuf:"varint,20,opt,name=prebuilt_workspace_build_stage,json=prebuiltWorkspaceBuildStage,proto3,enum=provisioner.PrebuiltWorkspaceBuildStage" json:"prebuilt_workspace_build_stage,omitempty"` // Indicates that a prebuilt workspace is being built.
+ RunningAgentAuthTokens []*RunningAgentAuthToken `protobuf:"bytes,21,rep,name=running_agent_auth_tokens,json=runningAgentAuthTokens,proto3" json:"running_agent_auth_tokens,omitempty"`
}
func (x *Metadata) Reset() {
*x = Metadata{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[26]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[28]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2323,7 +2482,7 @@ func (x *Metadata) String() string {
func (*Metadata) ProtoMessage() {}
func (x *Metadata) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[26]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[28]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2336,7 +2495,7 @@ func (x *Metadata) ProtoReflect() protoreflect.Message {
// Deprecated: Use Metadata.ProtoReflect.Descriptor instead.
func (*Metadata) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{26}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{28}
}
func (x *Metadata) GetCoderUrl() string {
@@ -2472,18 +2631,18 @@ func (x *Metadata) GetWorkspaceOwnerRbacRoles() []*Role {
return nil
}
-func (x *Metadata) GetIsPrebuild() bool {
+func (x *Metadata) GetPrebuiltWorkspaceBuildStage() PrebuiltWorkspaceBuildStage {
if x != nil {
- return x.IsPrebuild
+ return x.PrebuiltWorkspaceBuildStage
}
- return false
+ return PrebuiltWorkspaceBuildStage_NONE
}
-func (x *Metadata) GetRunningWorkspaceAgentToken() string {
+func (x *Metadata) GetRunningAgentAuthTokens() []*RunningAgentAuthToken {
if x != nil {
- return x.RunningWorkspaceAgentToken
+ return x.RunningAgentAuthTokens
}
- return ""
+ return nil
}
// Config represents execution configuration shared by all subsequent requests in the Session
@@ -2502,7 +2661,7 @@ type Config struct {
func (x *Config) Reset() {
*x = Config{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[27]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[29]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2515,7 +2674,7 @@ func (x *Config) String() string {
func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[27]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[29]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2528,7 +2687,7 @@ func (x *Config) ProtoReflect() protoreflect.Message {
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
func (*Config) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{27}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{29}
}
func (x *Config) GetTemplateSourceArchive() []byte {
@@ -2562,7 +2721,7 @@ type ParseRequest struct {
func (x *ParseRequest) Reset() {
*x = ParseRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[28]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[30]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2575,7 +2734,7 @@ func (x *ParseRequest) String() string {
func (*ParseRequest) ProtoMessage() {}
func (x *ParseRequest) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[28]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[30]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2588,7 +2747,7 @@ func (x *ParseRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use ParseRequest.ProtoReflect.Descriptor instead.
func (*ParseRequest) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{28}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{30}
}
// ParseComplete indicates a request to parse completed.
@@ -2606,7 +2765,7 @@ type ParseComplete struct {
func (x *ParseComplete) Reset() {
*x = ParseComplete{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[29]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[31]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2619,7 +2778,7 @@ func (x *ParseComplete) String() string {
func (*ParseComplete) ProtoMessage() {}
func (x *ParseComplete) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[29]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[31]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2632,7 +2791,7 @@ func (x *ParseComplete) ProtoReflect() protoreflect.Message {
// Deprecated: Use ParseComplete.ProtoReflect.Descriptor instead.
func (*ParseComplete) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{29}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{31}
}
func (x *ParseComplete) GetError() string {
@@ -2678,7 +2837,7 @@ type PlanRequest struct {
func (x *PlanRequest) Reset() {
*x = PlanRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[30]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[32]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2691,7 +2850,7 @@ func (x *PlanRequest) String() string {
func (*PlanRequest) ProtoMessage() {}
func (x *PlanRequest) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[30]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[32]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2704,7 +2863,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{30}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{32}
}
func (x *PlanRequest) GetMetadata() *Metadata {
@@ -2749,12 +2908,13 @@ type PlanComplete struct {
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"`
}
func (x *PlanComplete) Reset() {
*x = PlanComplete{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[31]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[33]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2767,7 +2927,7 @@ func (x *PlanComplete) String() string {
func (*PlanComplete) ProtoMessage() {}
func (x *PlanComplete) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[31]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[33]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2780,7 +2940,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{31}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{33}
}
func (x *PlanComplete) GetError() string {
@@ -2839,6 +2999,13 @@ func (x *PlanComplete) GetPlan() []byte {
return nil
}
+func (x *PlanComplete) GetResourceReplacements() []*ResourceReplacement {
+ if x != nil {
+ return x.ResourceReplacements
+ }
+ return nil
+}
+
// 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 {
@@ -2852,7 +3019,7 @@ type ApplyRequest struct {
func (x *ApplyRequest) Reset() {
*x = ApplyRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[32]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[34]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2865,7 +3032,7 @@ func (x *ApplyRequest) String() string {
func (*ApplyRequest) ProtoMessage() {}
func (x *ApplyRequest) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[32]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[34]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2878,7 +3045,7 @@ func (x *ApplyRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use ApplyRequest.ProtoReflect.Descriptor instead.
func (*ApplyRequest) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{32}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{34}
}
func (x *ApplyRequest) GetMetadata() *Metadata {
@@ -2905,7 +3072,7 @@ type ApplyComplete struct {
func (x *ApplyComplete) Reset() {
*x = ApplyComplete{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[33]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[35]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2918,7 +3085,7 @@ func (x *ApplyComplete) String() string {
func (*ApplyComplete) ProtoMessage() {}
func (x *ApplyComplete) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[33]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[35]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2931,7 +3098,7 @@ func (x *ApplyComplete) ProtoReflect() protoreflect.Message {
// Deprecated: Use ApplyComplete.ProtoReflect.Descriptor instead.
func (*ApplyComplete) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{33}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{35}
}
func (x *ApplyComplete) GetState() []byte {
@@ -2993,7 +3160,7 @@ type Timing struct {
func (x *Timing) Reset() {
*x = Timing{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[34]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[36]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3006,7 +3173,7 @@ func (x *Timing) String() string {
func (*Timing) ProtoMessage() {}
func (x *Timing) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[34]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[36]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3019,7 +3186,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{34}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{36}
}
func (x *Timing) GetStart() *timestamppb.Timestamp {
@@ -3081,7 +3248,7 @@ type CancelRequest struct {
func (x *CancelRequest) Reset() {
*x = CancelRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[35]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[37]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3094,7 +3261,7 @@ func (x *CancelRequest) String() string {
func (*CancelRequest) ProtoMessage() {}
func (x *CancelRequest) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[35]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[37]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3107,7 +3274,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{35}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{37}
}
type Request struct {
@@ -3128,7 +3295,7 @@ type Request struct {
func (x *Request) Reset() {
*x = Request{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[36]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[38]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3141,7 +3308,7 @@ func (x *Request) String() string {
func (*Request) ProtoMessage() {}
func (x *Request) ProtoReflect() protoreflect.Message {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[36]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[38]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3154,7 +3321,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{36}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{38}
}
func (m *Request) GetType() isRequest_Type {
@@ -3250,7 +3417,7 @@ type Response struct {
func (x *Response) Reset() {
*x = Response{}
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)
}
@@ -3263,7 +3430,7 @@ func (x *Response) String() string {
func (*Response) ProtoMessage() {}
func (x *Response) 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 {
@@ -3276,7 +3443,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{37}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{39}
}
func (m *Response) GetType() isResponse_Type {
@@ -3358,7 +3525,7 @@ type Agent_Metadata struct {
func (x *Agent_Metadata) Reset() {
*x = Agent_Metadata{}
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)
}
@@ -3371,7 +3538,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[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 {
@@ -3384,7 +3551,7 @@ func (x *Agent_Metadata) ProtoReflect() protoreflect.Message {
// Deprecated: Use Agent_Metadata.ProtoReflect.Descriptor instead.
func (*Agent_Metadata) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{13, 0}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{14, 0}
}
func (x *Agent_Metadata) GetKey() string {
@@ -3443,7 +3610,7 @@ type Resource_Metadata struct {
func (x *Resource_Metadata) Reset() {
*x = Resource_Metadata{}
if protoimpl.UnsafeEnabled {
- mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[40]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[42]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3456,7 +3623,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[40]
+ mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[42]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3469,7 +3636,7 @@ func (x *Resource_Metadata) ProtoReflect() protoreflect.Message {
// Deprecated: Use Resource_Metadata.ProtoReflect.Descriptor instead.
func (*Resource_Metadata) Descriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{23, 0}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{24, 0}
}
func (x *Resource_Metadata) GetKey() string {
@@ -3587,465 +3754,491 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{
0x3b, 0x0a, 0x0f, 0x50, 0x72, 0x65, 0x73, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74,
0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
- 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x57, 0x0a, 0x0d,
- 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a,
- 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
- 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69,
- 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x73,
- 0x69, 0x74, 0x69, 0x76, 0x65, 0x22, 0x4a, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x2b, 0x0a, 0x05,
- 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x70, 0x72,
- 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76,
- 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74,
- 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75,
- 0x74, 0x22, 0x37, 0x0a, 0x14, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x65,
- 0x6e, 0x74, 0x69, 0x74, 0x79, 0x41, 0x75, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73,
- 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
- 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x22, 0x4a, 0x0a, 0x1c, 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, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
- 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70,
- 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6f, 0x70,
- 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x22, 0x49, 0x0a, 0x14, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e,
- 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0e,
- 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21,
- 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02,
- 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65,
- 0x6e, 0x22, 0xb6, 0x08, 0x0a, 0x05, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69,
- 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e,
- 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
- 0x2d, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70,
- 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74,
- 0x2e, 0x45, 0x6e, 0x76, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x29,
- 0x0a, 0x10, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x79, 0x73, 0x74,
- 0x65, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74,
- 0x69, 0x6e, 0x67, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x72, 0x63,
- 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52,
- 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1c, 0x0a,
- 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x24, 0x0a, 0x04, 0x61,
- 0x70, 0x70, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x76,
- 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x52, 0x04, 0x61, 0x70, 0x70,
- 0x73, 0x12, 0x16, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09,
- 0x48, 0x00, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x21, 0x0a, 0x0b, 0x69, 0x6e, 0x73,
- 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00,
- 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x3c, 0x0a, 0x1a,
- 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f,
- 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05,
- 0x52, 0x18, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65,
- 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x2f, 0x0a, 0x13, 0x74, 0x72,
- 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x73, 0x68, 0x6f, 0x6f, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x75, 0x72,
- 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x74, 0x72, 0x6f, 0x75, 0x62, 0x6c, 0x65,
- 0x73, 0x68, 0x6f, 0x6f, 0x74, 0x69, 0x6e, 0x67, 0x55, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x6d,
- 0x6f, 0x74, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
- 0x6d, 0x6f, 0x74, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61,
- 0x64, 0x61, 0x74, 0x61, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f,
- 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x4d,
- 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
- 0x61, 0x12, 0x3b, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x70, 0x70,
- 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73,
- 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x41, 0x70, 0x70,
- 0x73, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x41, 0x70, 0x70, 0x73, 0x12, 0x2d,
- 0x0a, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32,
- 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x53, 0x63,
- 0x72, 0x69, 0x70, 0x74, 0x52, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x12, 0x2f, 0x0a,
- 0x0a, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x65, 0x6e, 0x76, 0x73, 0x18, 0x16, 0x20, 0x03, 0x28,
- 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
- 0x45, 0x6e, 0x76, 0x52, 0x09, 0x65, 0x78, 0x74, 0x72, 0x61, 0x45, 0x6e, 0x76, 0x73, 0x12, 0x14,
- 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x17, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6f,
- 0x72, 0x64, 0x65, 0x72, 0x12, 0x53, 0x0a, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
- 0x73, 0x5f, 0x6d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x18, 0x20, 0x01,
- 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, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f,
- 0x72, 0x69, 0x6e, 0x67, 0x52, 0x13, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d,
- 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x3f, 0x0a, 0x0d, 0x64, 0x65, 0x76,
- 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x19, 0x20, 0x03, 0x28, 0x0b,
- 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x44,
- 0x65, 0x76, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x0d, 0x64, 0x65, 0x76,
- 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x1a, 0xa3, 0x01, 0x0a, 0x08, 0x4d,
- 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73,
- 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
- 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06,
- 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63,
- 0x72, 0x69, 0x70, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c,
- 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c,
- 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28,
- 0x03, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x72,
- 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72,
- 0x1a, 0x36, 0x0a, 0x08, 0x45, 0x6e, 0x76, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
- 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14,
- 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76,
- 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68,
- 0x4a, 0x04, 0x08, 0x0e, 0x10, 0x0f, 0x52, 0x12, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x62, 0x65,
- 0x66, 0x6f, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x22, 0x8f, 0x01, 0x0a, 0x13, 0x52,
+ 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x47, 0x0a, 0x13,
+ 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d,
+ 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12,
+ 0x14, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05,
+ 0x70, 0x61, 0x74, 0x68, 0x73, 0x22, 0x57, 0x0a, 0x0d, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c,
+ 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61,
+ 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20,
+ 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x22, 0x4a,
+ 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x2b, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
+ 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76,
+ 0x65, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0x37, 0x0a, 0x14, 0x49, 0x6e,
+ 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x41, 0x75,
+ 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69,
+ 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63,
+ 0x65, 0x49, 0x64, 0x22, 0x4a, 0x0a, 0x1c, 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, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x22,
+ 0x49, 0x0a, 0x14, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50,
+ 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73,
+ 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61,
+ 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0xb6, 0x08, 0x0a, 0x05, 0x41,
+ 0x67, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18,
+ 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
+ 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x76, 0x45, 0x6e, 0x74,
+ 0x72, 0x79, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x29, 0x0a, 0x10, 0x6f, 0x70, 0x65, 0x72, 0x61,
+ 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x0f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x79, 0x73, 0x74,
+ 0x65, 0x6d, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75,
+ 0x72, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74,
+ 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
+ 0x6f, 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63,
+ 0x74, 0x6f, 0x72, 0x79, 0x12, 0x24, 0x0a, 0x04, 0x61, 0x70, 0x70, 0x73, 0x18, 0x08, 0x20, 0x03,
+ 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
+ 0x2e, 0x41, 0x70, 0x70, 0x52, 0x04, 0x61, 0x70, 0x70, 0x73, 0x12, 0x16, 0x0a, 0x05, 0x74, 0x6f,
+ 0x6b, 0x65, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x74, 0x6f, 0x6b,
+ 0x65, 0x6e, 0x12, 0x21, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69,
+ 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61,
+ 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x3c, 0x0a, 0x1a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74,
+ 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f,
+ 0x6e, 0x64, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x18, 0x63, 0x6f, 0x6e, 0x6e, 0x65,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f,
+ 0x6e, 0x64, 0x73, 0x12, 0x2f, 0x0a, 0x13, 0x74, 0x72, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x73, 0x68,
+ 0x6f, 0x6f, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x12, 0x74, 0x72, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x73, 0x68, 0x6f, 0x6f, 0x74, 0x69, 0x6e,
+ 0x67, 0x55, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x6f, 0x74, 0x64, 0x5f, 0x66, 0x69, 0x6c,
+ 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x6f, 0x74, 0x64, 0x46, 0x69, 0x6c,
+ 0x65, 0x12, 0x37, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x12, 0x20,
+ 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
+ 0x72, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
+ 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3b, 0x0a, 0x0c, 0x64, 0x69,
+ 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x70, 0x70, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x44,
+ 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x41, 0x70, 0x70, 0x73, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70,
+ 0x6c, 0x61, 0x79, 0x41, 0x70, 0x70, 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
+ 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x07, 0x73,
+ 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x12, 0x2f, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f,
+ 0x65, 0x6e, 0x76, 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f,
+ 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x45, 0x6e, 0x76, 0x52, 0x09, 0x65, 0x78,
+ 0x74, 0x72, 0x61, 0x45, 0x6e, 0x76, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72,
+ 0x18, 0x17, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x53, 0x0a,
+ 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x5f, 0x6d, 0x6f, 0x6e, 0x69, 0x74,
+ 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x18, 0x20, 0x01, 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, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x13, 0x72,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69,
- 0x6e, 0x67, 0x12, 0x3a, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01,
- 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
- 0x2e, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d,
- 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x3c,
- 0x0a, 0x07, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
- 0x22, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x6f,
- 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x6e, 0x69,
- 0x74, 0x6f, 0x72, 0x52, 0x07, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x22, 0x4f, 0x0a, 0x15,
- 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f,
- 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
- 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12,
- 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01,
- 0x28, 0x05, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x22, 0x63, 0x0a,
- 0x15, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d,
- 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e,
- 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61,
- 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c,
- 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f,
- 0x6c, 0x64, 0x22, 0xc6, 0x01, 0x0a, 0x0b, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x41, 0x70,
- 0x70, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01,
- 0x28, 0x08, 0x52, 0x06, 0x76, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x76, 0x73,
- 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20,
- 0x01, 0x28, 0x08, 0x52, 0x0e, 0x76, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x73, 0x69, 0x64,
- 0x65, 0x72, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x65, 0x62, 0x5f, 0x74, 0x65, 0x72, 0x6d, 0x69,
- 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x77, 0x65, 0x62, 0x54, 0x65,
- 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x73, 0x68, 0x5f, 0x68, 0x65,
- 0x6c, 0x70, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x73, 0x68, 0x48,
- 0x65, 0x6c, 0x70, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x66, 0x6f,
- 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x68, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x18,
- 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x6f, 0x72, 0x77, 0x61,
- 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x22, 0x2f, 0x0a, 0x03, 0x45,
- 0x6e, 0x76, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
- 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x9f, 0x02, 0x0a,
- 0x06, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c,
- 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64,
- 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63,
- 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x16,
- 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
- 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x18, 0x04,
- 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x74,
- 0x61, 0x72, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e,
- 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x74, 0x61, 0x72, 0x74, 0x42, 0x6c, 0x6f,
- 0x63, 0x6b, 0x73, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x20, 0x0a, 0x0c, 0x72, 0x75, 0x6e, 0x5f,
- 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a,
- 0x72, 0x75, 0x6e, 0x4f, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x72, 0x75,
- 0x6e, 0x5f, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x6f, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52,
- 0x09, 0x72, 0x75, 0x6e, 0x4f, 0x6e, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x69,
- 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x08, 0x20,
- 0x01, 0x28, 0x05, 0x52, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f,
- 0x6e, 0x64, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18,
- 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x50, 0x61, 0x74, 0x68, 0x22, 0x6e,
- 0x0a, 0x0c, 0x44, 0x65, 0x76, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x29,
- 0x0a, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x6f, 0x6c, 0x64,
- 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70,
- 0x61, 0x63, 0x65, 0x46, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e,
- 0x66, 0x69, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
- 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
- 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x94,
- 0x03, 0x0a, 0x03, 0x41, 0x70, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69,
- 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a,
- 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
- 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x04,
- 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, 0x6f,
- 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x1c, 0x0a,
- 0x09, 0x73, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08,
- 0x52, 0x09, 0x73, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x3a, 0x0a, 0x0b, 0x68,
- 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b,
- 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x48,
- 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x6c,
- 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x41, 0x0a, 0x0d, 0x73, 0x68, 0x61, 0x72, 0x69,
- 0x6e, 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c,
- 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70,
- 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x0c, 0x73, 0x68,
- 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78,
- 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, 0x78,
- 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18,
- 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06,
- 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x68, 0x69,
- 0x64, 0x64, 0x65, 0x6e, 0x12, 0x2f, 0x0a, 0x07, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x69, 0x6e, 0x18,
- 0x0c, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
- 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x6e, 0x52, 0x06, 0x6f,
- 0x70, 0x65, 0x6e, 0x49, 0x6e, 0x22, 0x59, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63,
- 0x68, 0x65, 0x63, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76,
- 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76,
- 0x61, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18,
- 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64,
- 0x22, 0x92, 0x03, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a,
- 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
- 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
- 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x18,
- 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
- 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74,
- 0x73, 0x12, 0x3a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20,
- 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
- 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64,
- 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a,
- 0x04, 0x68, 0x69, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x68, 0x69, 0x64,
- 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52,
- 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63,
- 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e,
- 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x61,
- 0x69, 0x6c, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09,
- 0x64, 0x61, 0x69, 0x6c, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64,
- 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
- 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x1a, 0x69, 0x0a, 0x08, 0x4d, 0x65,
- 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
- 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
- 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1c,
- 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
- 0x08, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x12, 0x17, 0x0a, 0x07,
- 0x69, 0x73, 0x5f, 0x6e, 0x75, 0x6c, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69,
- 0x73, 0x4e, 0x75, 0x6c, 0x6c, 0x22, 0x4c, 0x0a, 0x06, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12,
- 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
- 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69,
- 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
- 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
- 0x6b, 0x65, 0x79, 0x22, 0x31, 0x0a, 0x04, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e,
- 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
- 0x15, 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
- 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x22, 0xe0, 0x08, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64,
- 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c,
- 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x55, 0x72, 0x6c,
- 0x12, 0x53, 0x0a, 0x14, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x72,
- 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20,
- 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x57, 0x6f, 0x72,
- 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e,
- 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73,
- 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61,
- 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77,
- 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f,
- 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18,
- 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
- 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61,
- 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x77, 0x6f, 0x72,
- 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x12, 0x77, 0x6f, 0x72, 0x6b,
- 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x06,
- 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f,
- 0x77, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70,
- 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18,
- 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
- 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x65,
- 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12,
- 0x29, 0x0a, 0x10, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73,
- 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c,
- 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x48, 0x0a, 0x21, 0x77, 0x6f,
- 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6f, 0x69,
- 0x64, 0x63, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18,
- 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
- 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54,
- 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x41, 0x0a, 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
- 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f,
- 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1a, 0x77, 0x6f, 0x72,
- 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69,
- 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x6c,
- 0x61, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x65,
- 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x77, 0x6f, 0x72, 0x6b,
- 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
- 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
- 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x77, 0x6f,
- 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x67, 0x72,
- 0x6f, 0x75, 0x70, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x09, 0x52, 0x14, 0x77, 0x6f, 0x72, 0x6b,
- 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73,
- 0x12, 0x42, 0x0a, 0x1e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77,
- 0x6e, 0x65, 0x72, 0x5f, 0x73, 0x73, 0x68, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b,
- 0x65, 0x79, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1a, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70,
- 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x53, 0x73, 0x68, 0x50, 0x75, 0x62, 0x6c, 0x69,
- 0x63, 0x4b, 0x65, 0x79, 0x12, 0x44, 0x0a, 0x1f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
- 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x73, 0x73, 0x68, 0x5f, 0x70, 0x72, 0x69, 0x76,
- 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1b, 0x77,
- 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x53, 0x73, 0x68,
- 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x12, 0x77, 0x6f,
- 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x64,
- 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
- 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x64, 0x12, 0x3b, 0x0a, 0x1a, 0x77, 0x6f, 0x72, 0x6b,
- 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x69,
- 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x77, 0x6f,
- 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69,
- 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x4e, 0x0a, 0x1a, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61,
- 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x72, 0x62, 0x61, 0x63, 0x5f, 0x72, 0x6f,
- 0x6c, 0x65, 0x73, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x76,
- 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x17, 0x77, 0x6f,
- 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x52, 0x62, 0x61, 0x63,
- 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x73, 0x5f, 0x70, 0x72, 0x65, 0x62,
- 0x75, 0x69, 0x6c, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x50, 0x72,
- 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x41, 0x0a, 0x1d, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e,
- 0x67, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x61, 0x67, 0x65, 0x6e,
- 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1a, 0x72,
- 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41,
- 0x67, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x8a, 0x01, 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, 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,
+ 0x6e, 0x67, 0x12, 0x3f, 0x0a, 0x0d, 0x64, 0x65, 0x76, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e,
+ 0x65, 0x72, 0x73, 0x18, 0x19, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76,
+ 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x44, 0x65, 0x76, 0x63, 0x6f, 0x6e, 0x74, 0x61,
+ 0x69, 0x6e, 0x65, 0x72, 0x52, 0x0d, 0x64, 0x65, 0x76, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e,
+ 0x65, 0x72, 0x73, 0x1a, 0xa3, 0x01, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
- 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xb5, 0x02, 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, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61,
+ 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61,
+ 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18,
+ 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x1a, 0x0a,
+ 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52,
+ 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d,
+ 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65,
+ 0x6f, 0x75, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01,
+ 0x28, 0x03, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x1a, 0x36, 0x0a, 0x08, 0x45, 0x6e, 0x76,
+ 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
+ 0x01, 0x42, 0x06, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68, 0x4a, 0x04, 0x08, 0x0e, 0x10, 0x0f, 0x52,
+ 0x12, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x5f, 0x72, 0x65,
+ 0x61, 0x64, 0x79, 0x22, 0x8f, 0x01, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+ 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x3a, 0x0a, 0x06, 0x6d,
+ 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x72,
+ 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79,
+ 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x52,
+ 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x3c, 0x0a, 0x07, 0x76, 0x6f, 0x6c, 0x75, 0x6d,
+ 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
+ 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73,
+ 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x52, 0x07, 0x76, 0x6f,
+ 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x22, 0x4f, 0x0a, 0x15, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52,
+ 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x12, 0x18,
+ 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
+ 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65,
+ 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x68, 0x72,
+ 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x22, 0x63, 0x0a, 0x15, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65,
+ 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x12,
+ 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70,
+ 0x61, 0x74, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1c, 0x0a,
+ 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05,
+ 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x22, 0xc6, 0x01, 0x0a, 0x0b,
+ 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x41, 0x70, 0x70, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x76,
+ 0x73, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x76, 0x73, 0x63,
+ 0x6f, 0x64, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x76, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e,
+ 0x73, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x76, 0x73,
+ 0x63, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x21, 0x0a, 0x0c,
+ 0x77, 0x65, 0x62, 0x5f, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01,
+ 0x28, 0x08, 0x52, 0x0b, 0x77, 0x65, 0x62, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x12,
+ 0x1d, 0x0a, 0x0a, 0x73, 0x73, 0x68, 0x5f, 0x68, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x18, 0x04, 0x20,
+ 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x73, 0x68, 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x12, 0x34,
+ 0x0a, 0x16, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e,
+ 0x67, 0x5f, 0x68, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14,
+ 0x70, 0x6f, 0x72, 0x74, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x65,
+ 0x6c, 0x70, 0x65, 0x72, 0x22, 0x2f, 0x0a, 0x03, 0x45, 0x6e, 0x76, 0x12, 0x12, 0x0a, 0x04, 0x6e,
+ 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
+ 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x9f, 0x02, 0x0a, 0x06, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74,
+ 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e,
+ 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12,
+ 0x12, 0x0a, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63,
+ 0x72, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x62, 0x6c, 0x6f,
+ 0x63, 0x6b, 0x73, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52,
+ 0x10, 0x73, 0x74, 0x61, 0x72, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x4c, 0x6f, 0x67, 0x69,
+ 0x6e, 0x12, 0x20, 0x0a, 0x0c, 0x72, 0x75, 0x6e, 0x5f, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x72,
+ 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x72, 0x75, 0x6e, 0x4f, 0x6e, 0x53, 0x74,
+ 0x61, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x72, 0x75, 0x6e, 0x5f, 0x6f, 0x6e, 0x5f, 0x73, 0x74,
+ 0x6f, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x75, 0x6e, 0x4f, 0x6e, 0x53,
+ 0x74, 0x6f, 0x70, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73,
+ 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x74, 0x69,
+ 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x19, 0x0a, 0x08,
+ 0x6c, 0x6f, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
+ 0x6c, 0x6f, 0x67, 0x50, 0x61, 0x74, 0x68, 0x22, 0x6e, 0x0a, 0x0c, 0x44, 0x65, 0x76, 0x63, 0x6f,
+ 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x73,
+ 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x46, 0x6f, 0x6c, 0x64,
+ 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x61, 0x74,
+ 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x50,
+ 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x94, 0x03, 0x0a, 0x03, 0x41, 0x70, 0x70, 0x12,
+ 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73,
+ 0x6c, 0x75, 0x67, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e,
+ 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c,
+ 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
+ 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
+ 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75,
+ 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, 0x62, 0x64, 0x6f, 0x6d,
+ 0x61, 0x69, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x75, 0x62, 0x64, 0x6f,
+ 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x3a, 0x0a, 0x0b, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68,
+ 0x65, 0x63, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x76,
+ 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68,
+ 0x65, 0x63, 0x6b, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b,
+ 0x12, 0x41, 0x0a, 0x0d, 0x73, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65,
+ 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73,
+ 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67,
+ 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x0c, 0x73, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65,
+ 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18,
+ 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12,
+ 0x14, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05,
+ 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x18,
+ 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x12, 0x2f, 0x0a,
+ 0x07, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x69, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16,
+ 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70,
+ 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x6e, 0x52, 0x06, 0x6f, 0x70, 0x65, 0x6e, 0x49, 0x6e, 0x22, 0x59,
+ 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x10, 0x0a,
+ 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12,
+ 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x05, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x74,
+ 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09,
+ 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x22, 0x92, 0x03, 0x0a, 0x08, 0x52, 0x65,
+ 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79,
+ 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2a,
+ 0x0a, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12,
+ 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x67, 0x65,
+ 0x6e, 0x74, 0x52, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x3a, 0x0a, 0x08, 0x6d, 0x65,
+ 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70,
+ 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75,
+ 0x72, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65,
+ 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x69, 0x64, 0x65, 0x18, 0x05,
+ 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x68, 0x69, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63,
+ 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x23,
+ 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18,
+ 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54,
+ 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f, 0x63, 0x6f, 0x73,
+ 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x43, 0x6f,
+ 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74,
+ 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x50,
+ 0x61, 0x74, 0x68, 0x1a, 0x69, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12,
+ 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
+ 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69,
+ 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x73,
+ 0x69, 0x74, 0x69, 0x76, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x73, 0x5f, 0x6e, 0x75, 0x6c, 0x6c,
+ 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4e, 0x75, 0x6c, 0x6c, 0x22, 0x4c,
+ 0x0a, 0x06, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72,
+ 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+ 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65,
+ 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x31, 0x0a, 0x04,
+ 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f,
+ 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x22,
+ 0x48, 0x0a, 0x15, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x41,
+ 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x67, 0x65, 0x6e,
+ 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e,
+ 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0xca, 0x09, 0x0a, 0x08, 0x4d, 0x65,
+ 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f,
+ 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x64, 0x65, 0x72,
+ 0x55, 0x72, 0x6c, 0x12, 0x53, 0x0a, 0x14, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
+ 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x0e, 0x32, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
+ 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74,
+ 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72,
+ 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b,
+ 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12,
+ 0x27, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e,
+ 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70,
+ 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b,
+ 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
+ 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x12, 0x77,
+ 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69,
+ 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61,
+ 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x77, 0x6f, 0x72,
+ 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61,
+ 0x69, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70,
+ 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x23, 0x0a,
+ 0x0d, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x4e, 0x61,
+ 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76,
+ 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x65,
+ 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x48, 0x0a,
+ 0x21, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72,
+ 0x5f, 0x6f, 0x69, 0x64, 0x63, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b,
+ 0x65, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70,
+ 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x63, 0x63, 0x65,
+ 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x41, 0x0a, 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x73,
+ 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69,
+ 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1a,
+ 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x53, 0x65,
+ 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x65,
+ 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x0a, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x77,
+ 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6e,
+ 0x61, 0x6d, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x73,
+ 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x34, 0x0a,
+ 0x16, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72,
+ 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x09, 0x52, 0x14, 0x77,
+ 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x47, 0x72, 0x6f,
+ 0x75, 0x70, 0x73, 0x12, 0x42, 0x0a, 0x1e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
+ 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x73, 0x73, 0x68, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69,
+ 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1a, 0x77, 0x6f, 0x72,
+ 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x53, 0x73, 0x68, 0x50, 0x75,
+ 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x44, 0x0a, 0x1f, 0x77, 0x6f, 0x72, 0x6b, 0x73,
+ 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x73, 0x73, 0x68, 0x5f, 0x70,
+ 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x1b, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72,
+ 0x53, 0x73, 0x68, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x2c, 0x0a,
+ 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64,
+ 0x5f, 0x69, 0x64, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x73,
+ 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x64, 0x12, 0x3b, 0x0a, 0x1a, 0x77,
+ 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6c,
+ 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x17, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x4c,
+ 0x6f, 0x67, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x4e, 0x0a, 0x1a, 0x77, 0x6f, 0x72, 0x6b,
+ 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x72, 0x62, 0x61, 0x63,
+ 0x5f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70,
+ 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52,
+ 0x17, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x52,
+ 0x62, 0x61, 0x63, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x6d, 0x0a, 0x1e, 0x70, 0x72, 0x65, 0x62,
+ 0x75, 0x69, 0x6c, 0x74, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x62,
+ 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0e,
+ 0x32, 0x28, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 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, 0x52, 0x1b, 0x70, 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, 0x5d, 0x0a, 0x19, 0x72, 0x75, 0x6e, 0x6e, 0x69,
+ 0x6e, 0x67, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f,
+ 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x72, 0x6f,
+ 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67,
+ 0x41, 0x67, 0x65, 0x6e, 0x74, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x16,
+ 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x41, 0x75, 0x74, 0x68,
+ 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x22, 0x8a, 0x01, 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, 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, 0xb5, 0x02, 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, 0x22, 0xf0, 0x03, 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, 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, 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, 0x21, 0x2e, 0x70, 0x72, 0x6f,
+ 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 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, 0x15, 0x65,
- 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69,
- 0x64, 0x65, 0x72, 0x73, 0x22, 0x99, 0x03, 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, 0x32, 0x15,
+ 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, 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, 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, 0xbe, 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, 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, 0xd1, 0x01, 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, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x2a, 0x3f, 0x0a, 0x08, 0x4c, 0x6f, 0x67,
+ 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x00,
+ 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x49,
+ 0x4e, 0x46, 0x4f, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x03, 0x12,
+ 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x2a, 0x3b, 0x0a, 0x0f, 0x41, 0x70,
+ 0x70, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a,
+ 0x05, 0x4f, 0x57, 0x4e, 0x45, 0x52, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x55, 0x54, 0x48,
+ 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x50,
+ 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x02, 0x2a, 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, 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,
- 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, 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, 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, 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,
- 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, 0xbe, 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, 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, 0xd1, 0x01, 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, 0x42, 0x06, 0x0a,
- 0x04, 0x74, 0x79, 0x70, 0x65, 0x2a, 0x3f, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65,
- 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05,
- 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10,
- 0x02, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x45,
- 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x2a, 0x3b, 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x53, 0x68, 0x61,
- 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x57, 0x4e,
- 0x45, 0x52, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, 0x49,
- 0x43, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c, 0x49,
- 0x43, 0x10, 0x02, 0x2a, 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, 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, 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,
+ 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 (
@@ -4060,116 +4253,122 @@ 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, 5)
-var file_provisionersdk_proto_provisioner_proto_msgTypes = make([]protoimpl.MessageInfo, 42)
+var file_provisionersdk_proto_provisioner_proto_enumTypes = make([]protoimpl.EnumInfo, 6)
+var file_provisionersdk_proto_provisioner_proto_msgTypes = make([]protoimpl.MessageInfo, 44)
var file_provisionersdk_proto_provisioner_proto_goTypes = []interface{}{
(LogLevel)(0), // 0: provisioner.LogLevel
(AppSharingLevel)(0), // 1: provisioner.AppSharingLevel
(AppOpenIn)(0), // 2: provisioner.AppOpenIn
(WorkspaceTransition)(0), // 3: provisioner.WorkspaceTransition
- (TimingState)(0), // 4: provisioner.TimingState
- (*Empty)(nil), // 5: provisioner.Empty
- (*TemplateVariable)(nil), // 6: provisioner.TemplateVariable
- (*RichParameterOption)(nil), // 7: provisioner.RichParameterOption
- (*RichParameter)(nil), // 8: provisioner.RichParameter
- (*RichParameterValue)(nil), // 9: provisioner.RichParameterValue
- (*Prebuild)(nil), // 10: provisioner.Prebuild
- (*Preset)(nil), // 11: provisioner.Preset
- (*PresetParameter)(nil), // 12: provisioner.PresetParameter
- (*VariableValue)(nil), // 13: provisioner.VariableValue
- (*Log)(nil), // 14: provisioner.Log
- (*InstanceIdentityAuth)(nil), // 15: provisioner.InstanceIdentityAuth
- (*ExternalAuthProviderResource)(nil), // 16: provisioner.ExternalAuthProviderResource
- (*ExternalAuthProvider)(nil), // 17: provisioner.ExternalAuthProvider
- (*Agent)(nil), // 18: provisioner.Agent
- (*ResourcesMonitoring)(nil), // 19: provisioner.ResourcesMonitoring
- (*MemoryResourceMonitor)(nil), // 20: provisioner.MemoryResourceMonitor
- (*VolumeResourceMonitor)(nil), // 21: provisioner.VolumeResourceMonitor
- (*DisplayApps)(nil), // 22: provisioner.DisplayApps
- (*Env)(nil), // 23: provisioner.Env
- (*Script)(nil), // 24: provisioner.Script
- (*Devcontainer)(nil), // 25: provisioner.Devcontainer
- (*App)(nil), // 26: provisioner.App
- (*Healthcheck)(nil), // 27: provisioner.Healthcheck
- (*Resource)(nil), // 28: provisioner.Resource
- (*Module)(nil), // 29: provisioner.Module
- (*Role)(nil), // 30: provisioner.Role
- (*Metadata)(nil), // 31: provisioner.Metadata
- (*Config)(nil), // 32: provisioner.Config
- (*ParseRequest)(nil), // 33: provisioner.ParseRequest
- (*ParseComplete)(nil), // 34: provisioner.ParseComplete
- (*PlanRequest)(nil), // 35: provisioner.PlanRequest
- (*PlanComplete)(nil), // 36: provisioner.PlanComplete
- (*ApplyRequest)(nil), // 37: provisioner.ApplyRequest
- (*ApplyComplete)(nil), // 38: provisioner.ApplyComplete
- (*Timing)(nil), // 39: provisioner.Timing
- (*CancelRequest)(nil), // 40: provisioner.CancelRequest
- (*Request)(nil), // 41: provisioner.Request
- (*Response)(nil), // 42: provisioner.Response
- (*Agent_Metadata)(nil), // 43: provisioner.Agent.Metadata
- nil, // 44: provisioner.Agent.EnvEntry
- (*Resource_Metadata)(nil), // 45: provisioner.Resource.Metadata
- nil, // 46: provisioner.ParseComplete.WorkspaceTagsEntry
- (*timestamppb.Timestamp)(nil), // 47: google.protobuf.Timestamp
+ (PrebuiltWorkspaceBuildStage)(0), // 4: provisioner.PrebuiltWorkspaceBuildStage
+ (TimingState)(0), // 5: provisioner.TimingState
+ (*Empty)(nil), // 6: provisioner.Empty
+ (*TemplateVariable)(nil), // 7: provisioner.TemplateVariable
+ (*RichParameterOption)(nil), // 8: provisioner.RichParameterOption
+ (*RichParameter)(nil), // 9: provisioner.RichParameter
+ (*RichParameterValue)(nil), // 10: provisioner.RichParameterValue
+ (*Prebuild)(nil), // 11: provisioner.Prebuild
+ (*Preset)(nil), // 12: provisioner.Preset
+ (*PresetParameter)(nil), // 13: provisioner.PresetParameter
+ (*ResourceReplacement)(nil), // 14: provisioner.ResourceReplacement
+ (*VariableValue)(nil), // 15: provisioner.VariableValue
+ (*Log)(nil), // 16: provisioner.Log
+ (*InstanceIdentityAuth)(nil), // 17: provisioner.InstanceIdentityAuth
+ (*ExternalAuthProviderResource)(nil), // 18: provisioner.ExternalAuthProviderResource
+ (*ExternalAuthProvider)(nil), // 19: provisioner.ExternalAuthProvider
+ (*Agent)(nil), // 20: provisioner.Agent
+ (*ResourcesMonitoring)(nil), // 21: provisioner.ResourcesMonitoring
+ (*MemoryResourceMonitor)(nil), // 22: provisioner.MemoryResourceMonitor
+ (*VolumeResourceMonitor)(nil), // 23: provisioner.VolumeResourceMonitor
+ (*DisplayApps)(nil), // 24: provisioner.DisplayApps
+ (*Env)(nil), // 25: provisioner.Env
+ (*Script)(nil), // 26: provisioner.Script
+ (*Devcontainer)(nil), // 27: provisioner.Devcontainer
+ (*App)(nil), // 28: provisioner.App
+ (*Healthcheck)(nil), // 29: provisioner.Healthcheck
+ (*Resource)(nil), // 30: provisioner.Resource
+ (*Module)(nil), // 31: provisioner.Module
+ (*Role)(nil), // 32: provisioner.Role
+ (*RunningAgentAuthToken)(nil), // 33: provisioner.RunningAgentAuthToken
+ (*Metadata)(nil), // 34: provisioner.Metadata
+ (*Config)(nil), // 35: provisioner.Config
+ (*ParseRequest)(nil), // 36: provisioner.ParseRequest
+ (*ParseComplete)(nil), // 37: provisioner.ParseComplete
+ (*PlanRequest)(nil), // 38: provisioner.PlanRequest
+ (*PlanComplete)(nil), // 39: provisioner.PlanComplete
+ (*ApplyRequest)(nil), // 40: provisioner.ApplyRequest
+ (*ApplyComplete)(nil), // 41: provisioner.ApplyComplete
+ (*Timing)(nil), // 42: provisioner.Timing
+ (*CancelRequest)(nil), // 43: provisioner.CancelRequest
+ (*Request)(nil), // 44: provisioner.Request
+ (*Response)(nil), // 45: provisioner.Response
+ (*Agent_Metadata)(nil), // 46: provisioner.Agent.Metadata
+ nil, // 47: provisioner.Agent.EnvEntry
+ (*Resource_Metadata)(nil), // 48: provisioner.Resource.Metadata
+ nil, // 49: provisioner.ParseComplete.WorkspaceTagsEntry
+ (*timestamppb.Timestamp)(nil), // 50: google.protobuf.Timestamp
}
var file_provisionersdk_proto_provisioner_proto_depIdxs = []int32{
- 7, // 0: provisioner.RichParameter.options:type_name -> provisioner.RichParameterOption
- 12, // 1: provisioner.Preset.parameters:type_name -> provisioner.PresetParameter
- 10, // 2: provisioner.Preset.prebuild:type_name -> provisioner.Prebuild
+ 8, // 0: provisioner.RichParameter.options:type_name -> provisioner.RichParameterOption
+ 13, // 1: provisioner.Preset.parameters:type_name -> provisioner.PresetParameter
+ 11, // 2: provisioner.Preset.prebuild:type_name -> provisioner.Prebuild
0, // 3: provisioner.Log.level:type_name -> provisioner.LogLevel
- 44, // 4: provisioner.Agent.env:type_name -> provisioner.Agent.EnvEntry
- 26, // 5: provisioner.Agent.apps:type_name -> provisioner.App
- 43, // 6: provisioner.Agent.metadata:type_name -> provisioner.Agent.Metadata
- 22, // 7: provisioner.Agent.display_apps:type_name -> provisioner.DisplayApps
- 24, // 8: provisioner.Agent.scripts:type_name -> provisioner.Script
- 23, // 9: provisioner.Agent.extra_envs:type_name -> provisioner.Env
- 19, // 10: provisioner.Agent.resources_monitoring:type_name -> provisioner.ResourcesMonitoring
- 25, // 11: provisioner.Agent.devcontainers:type_name -> provisioner.Devcontainer
- 20, // 12: provisioner.ResourcesMonitoring.memory:type_name -> provisioner.MemoryResourceMonitor
- 21, // 13: provisioner.ResourcesMonitoring.volumes:type_name -> provisioner.VolumeResourceMonitor
- 27, // 14: provisioner.App.healthcheck:type_name -> provisioner.Healthcheck
+ 47, // 4: provisioner.Agent.env:type_name -> provisioner.Agent.EnvEntry
+ 28, // 5: provisioner.Agent.apps:type_name -> provisioner.App
+ 46, // 6: provisioner.Agent.metadata:type_name -> provisioner.Agent.Metadata
+ 24, // 7: provisioner.Agent.display_apps:type_name -> provisioner.DisplayApps
+ 26, // 8: provisioner.Agent.scripts:type_name -> provisioner.Script
+ 25, // 9: provisioner.Agent.extra_envs:type_name -> provisioner.Env
+ 21, // 10: provisioner.Agent.resources_monitoring:type_name -> provisioner.ResourcesMonitoring
+ 27, // 11: provisioner.Agent.devcontainers:type_name -> provisioner.Devcontainer
+ 22, // 12: provisioner.ResourcesMonitoring.memory:type_name -> provisioner.MemoryResourceMonitor
+ 23, // 13: provisioner.ResourcesMonitoring.volumes:type_name -> provisioner.VolumeResourceMonitor
+ 29, // 14: provisioner.App.healthcheck:type_name -> provisioner.Healthcheck
1, // 15: provisioner.App.sharing_level:type_name -> provisioner.AppSharingLevel
2, // 16: provisioner.App.open_in:type_name -> provisioner.AppOpenIn
- 18, // 17: provisioner.Resource.agents:type_name -> provisioner.Agent
- 45, // 18: provisioner.Resource.metadata:type_name -> provisioner.Resource.Metadata
+ 20, // 17: provisioner.Resource.agents:type_name -> provisioner.Agent
+ 48, // 18: provisioner.Resource.metadata:type_name -> provisioner.Resource.Metadata
3, // 19: provisioner.Metadata.workspace_transition:type_name -> provisioner.WorkspaceTransition
- 30, // 20: provisioner.Metadata.workspace_owner_rbac_roles:type_name -> provisioner.Role
- 6, // 21: provisioner.ParseComplete.template_variables:type_name -> provisioner.TemplateVariable
- 46, // 22: provisioner.ParseComplete.workspace_tags:type_name -> provisioner.ParseComplete.WorkspaceTagsEntry
- 31, // 23: provisioner.PlanRequest.metadata:type_name -> provisioner.Metadata
- 9, // 24: provisioner.PlanRequest.rich_parameter_values:type_name -> provisioner.RichParameterValue
- 13, // 25: provisioner.PlanRequest.variable_values:type_name -> provisioner.VariableValue
- 17, // 26: provisioner.PlanRequest.external_auth_providers:type_name -> provisioner.ExternalAuthProvider
- 28, // 27: provisioner.PlanComplete.resources:type_name -> provisioner.Resource
- 8, // 28: provisioner.PlanComplete.parameters:type_name -> provisioner.RichParameter
- 16, // 29: provisioner.PlanComplete.external_auth_providers:type_name -> provisioner.ExternalAuthProviderResource
- 39, // 30: provisioner.PlanComplete.timings:type_name -> provisioner.Timing
- 29, // 31: provisioner.PlanComplete.modules:type_name -> provisioner.Module
- 11, // 32: provisioner.PlanComplete.presets:type_name -> provisioner.Preset
- 31, // 33: provisioner.ApplyRequest.metadata:type_name -> provisioner.Metadata
- 28, // 34: provisioner.ApplyComplete.resources:type_name -> provisioner.Resource
- 8, // 35: provisioner.ApplyComplete.parameters:type_name -> provisioner.RichParameter
- 16, // 36: provisioner.ApplyComplete.external_auth_providers:type_name -> provisioner.ExternalAuthProviderResource
- 39, // 37: provisioner.ApplyComplete.timings:type_name -> provisioner.Timing
- 47, // 38: provisioner.Timing.start:type_name -> google.protobuf.Timestamp
- 47, // 39: provisioner.Timing.end:type_name -> google.protobuf.Timestamp
- 4, // 40: provisioner.Timing.state:type_name -> provisioner.TimingState
- 32, // 41: provisioner.Request.config:type_name -> provisioner.Config
- 33, // 42: provisioner.Request.parse:type_name -> provisioner.ParseRequest
- 35, // 43: provisioner.Request.plan:type_name -> provisioner.PlanRequest
- 37, // 44: provisioner.Request.apply:type_name -> provisioner.ApplyRequest
- 40, // 45: provisioner.Request.cancel:type_name -> provisioner.CancelRequest
- 14, // 46: provisioner.Response.log:type_name -> provisioner.Log
- 34, // 47: provisioner.Response.parse:type_name -> provisioner.ParseComplete
- 36, // 48: provisioner.Response.plan:type_name -> provisioner.PlanComplete
- 38, // 49: provisioner.Response.apply:type_name -> provisioner.ApplyComplete
- 41, // 50: provisioner.Provisioner.Session:input_type -> provisioner.Request
- 42, // 51: provisioner.Provisioner.Session:output_type -> provisioner.Response
- 51, // [51:52] is the sub-list for method output_type
- 50, // [50:51] is the sub-list for method input_type
- 50, // [50:50] is the sub-list for extension type_name
- 50, // [50:50] is the sub-list for extension extendee
- 0, // [0:50] is the sub-list for field type_name
+ 32, // 20: provisioner.Metadata.workspace_owner_rbac_roles:type_name -> provisioner.Role
+ 4, // 21: provisioner.Metadata.prebuilt_workspace_build_stage:type_name -> provisioner.PrebuiltWorkspaceBuildStage
+ 33, // 22: provisioner.Metadata.running_agent_auth_tokens:type_name -> provisioner.RunningAgentAuthToken
+ 7, // 23: provisioner.ParseComplete.template_variables:type_name -> provisioner.TemplateVariable
+ 49, // 24: provisioner.ParseComplete.workspace_tags:type_name -> provisioner.ParseComplete.WorkspaceTagsEntry
+ 34, // 25: provisioner.PlanRequest.metadata:type_name -> provisioner.Metadata
+ 10, // 26: provisioner.PlanRequest.rich_parameter_values:type_name -> provisioner.RichParameterValue
+ 15, // 27: provisioner.PlanRequest.variable_values:type_name -> provisioner.VariableValue
+ 19, // 28: provisioner.PlanRequest.external_auth_providers:type_name -> provisioner.ExternalAuthProvider
+ 30, // 29: provisioner.PlanComplete.resources:type_name -> provisioner.Resource
+ 9, // 30: provisioner.PlanComplete.parameters:type_name -> provisioner.RichParameter
+ 18, // 31: provisioner.PlanComplete.external_auth_providers:type_name -> provisioner.ExternalAuthProviderResource
+ 42, // 32: provisioner.PlanComplete.timings:type_name -> provisioner.Timing
+ 31, // 33: provisioner.PlanComplete.modules:type_name -> provisioner.Module
+ 12, // 34: provisioner.PlanComplete.presets:type_name -> provisioner.Preset
+ 14, // 35: provisioner.PlanComplete.resource_replacements:type_name -> provisioner.ResourceReplacement
+ 34, // 36: provisioner.ApplyRequest.metadata:type_name -> provisioner.Metadata
+ 30, // 37: provisioner.ApplyComplete.resources:type_name -> provisioner.Resource
+ 9, // 38: provisioner.ApplyComplete.parameters:type_name -> provisioner.RichParameter
+ 18, // 39: provisioner.ApplyComplete.external_auth_providers:type_name -> provisioner.ExternalAuthProviderResource
+ 42, // 40: provisioner.ApplyComplete.timings:type_name -> provisioner.Timing
+ 50, // 41: provisioner.Timing.start:type_name -> google.protobuf.Timestamp
+ 50, // 42: provisioner.Timing.end:type_name -> google.protobuf.Timestamp
+ 5, // 43: provisioner.Timing.state:type_name -> provisioner.TimingState
+ 35, // 44: provisioner.Request.config:type_name -> provisioner.Config
+ 36, // 45: provisioner.Request.parse:type_name -> provisioner.ParseRequest
+ 38, // 46: provisioner.Request.plan:type_name -> provisioner.PlanRequest
+ 40, // 47: provisioner.Request.apply:type_name -> provisioner.ApplyRequest
+ 43, // 48: provisioner.Request.cancel:type_name -> provisioner.CancelRequest
+ 16, // 49: provisioner.Response.log:type_name -> provisioner.Log
+ 37, // 50: provisioner.Response.parse:type_name -> provisioner.ParseComplete
+ 39, // 51: provisioner.Response.plan:type_name -> provisioner.PlanComplete
+ 41, // 52: provisioner.Response.apply:type_name -> provisioner.ApplyComplete
+ 44, // 53: provisioner.Provisioner.Session:input_type -> provisioner.Request
+ 45, // 54: provisioner.Provisioner.Session:output_type -> provisioner.Response
+ 54, // [54:55] is the sub-list for method output_type
+ 53, // [53:54] is the sub-list for method input_type
+ 53, // [53:53] is the sub-list for extension type_name
+ 53, // [53:53] is the sub-list for extension extendee
+ 0, // [0:53] is the sub-list for field type_name
}
func init() { file_provisionersdk_proto_provisioner_proto_init() }
@@ -4275,7 +4474,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*VariableValue); i {
+ switch v := v.(*ResourceReplacement); i {
case 0:
return &v.state
case 1:
@@ -4287,7 +4486,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Log); i {
+ switch v := v.(*VariableValue); i {
case 0:
return &v.state
case 1:
@@ -4299,7 +4498,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*InstanceIdentityAuth); i {
+ switch v := v.(*Log); i {
case 0:
return &v.state
case 1:
@@ -4311,7 +4510,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*ExternalAuthProviderResource); i {
+ switch v := v.(*InstanceIdentityAuth); i {
case 0:
return &v.state
case 1:
@@ -4323,7 +4522,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*ExternalAuthProvider); i {
+ switch v := v.(*ExternalAuthProviderResource); i {
case 0:
return &v.state
case 1:
@@ -4335,7 +4534,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Agent); i {
+ switch v := v.(*ExternalAuthProvider); i {
case 0:
return &v.state
case 1:
@@ -4347,7 +4546,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*ResourcesMonitoring); i {
+ switch v := v.(*Agent); i {
case 0:
return &v.state
case 1:
@@ -4359,7 +4558,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*MemoryResourceMonitor); i {
+ switch v := v.(*ResourcesMonitoring); i {
case 0:
return &v.state
case 1:
@@ -4371,7 +4570,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*VolumeResourceMonitor); i {
+ switch v := v.(*MemoryResourceMonitor); i {
case 0:
return &v.state
case 1:
@@ -4383,7 +4582,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*DisplayApps); i {
+ switch v := v.(*VolumeResourceMonitor); i {
case 0:
return &v.state
case 1:
@@ -4395,7 +4594,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Env); i {
+ switch v := v.(*DisplayApps); i {
case 0:
return &v.state
case 1:
@@ -4407,7 +4606,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Script); i {
+ switch v := v.(*Env); i {
case 0:
return &v.state
case 1:
@@ -4419,7 +4618,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Devcontainer); i {
+ switch v := v.(*Script); i {
case 0:
return &v.state
case 1:
@@ -4431,7 +4630,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*App); i {
+ switch v := v.(*Devcontainer); i {
case 0:
return &v.state
case 1:
@@ -4443,7 +4642,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Healthcheck); i {
+ switch v := v.(*App); i {
case 0:
return &v.state
case 1:
@@ -4455,7 +4654,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Resource); i {
+ switch v := v.(*Healthcheck); i {
case 0:
return &v.state
case 1:
@@ -4467,7 +4666,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Module); i {
+ switch v := v.(*Resource); i {
case 0:
return &v.state
case 1:
@@ -4479,7 +4678,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Role); i {
+ switch v := v.(*Module); i {
case 0:
return &v.state
case 1:
@@ -4491,7 +4690,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Metadata); i {
+ switch v := v.(*Role); i {
case 0:
return &v.state
case 1:
@@ -4503,7 +4702,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Config); i {
+ switch v := v.(*RunningAgentAuthToken); i {
case 0:
return &v.state
case 1:
@@ -4515,7 +4714,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*ParseRequest); i {
+ switch v := v.(*Metadata); i {
case 0:
return &v.state
case 1:
@@ -4527,7 +4726,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*ParseComplete); i {
+ switch v := v.(*Config); i {
case 0:
return &v.state
case 1:
@@ -4539,7 +4738,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*PlanRequest); i {
+ switch v := v.(*ParseRequest); i {
case 0:
return &v.state
case 1:
@@ -4551,7 +4750,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*PlanComplete); i {
+ switch v := v.(*ParseComplete); i {
case 0:
return &v.state
case 1:
@@ -4563,7 +4762,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*ApplyRequest); i {
+ switch v := v.(*PlanRequest); i {
case 0:
return &v.state
case 1:
@@ -4575,7 +4774,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*ApplyComplete); i {
+ switch v := v.(*PlanComplete); i {
case 0:
return &v.state
case 1:
@@ -4587,7 +4786,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Timing); i {
+ switch v := v.(*ApplyRequest); i {
case 0:
return &v.state
case 1:
@@ -4599,7 +4798,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*CancelRequest); i {
+ switch v := v.(*ApplyComplete); i {
case 0:
return &v.state
case 1:
@@ -4611,7 +4810,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Request); i {
+ switch v := v.(*Timing); i {
case 0:
return &v.state
case 1:
@@ -4623,7 +4822,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.(*Response); i {
+ switch v := v.(*CancelRequest); i {
case 0:
return &v.state
case 1:
@@ -4635,7 +4834,19 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[38].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[39].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Response); i {
case 0:
return &v.state
case 1:
@@ -4647,6 +4858,18 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[40].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[42].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Resource_Metadata); i {
case 0:
return &v.state
@@ -4660,18 +4883,18 @@ func file_provisionersdk_proto_provisioner_proto_init() {
}
}
file_provisionersdk_proto_provisioner_proto_msgTypes[3].OneofWrappers = []interface{}{}
- file_provisionersdk_proto_provisioner_proto_msgTypes[13].OneofWrappers = []interface{}{
+ file_provisionersdk_proto_provisioner_proto_msgTypes[14].OneofWrappers = []interface{}{
(*Agent_Token)(nil),
(*Agent_InstanceId)(nil),
}
- file_provisionersdk_proto_provisioner_proto_msgTypes[36].OneofWrappers = []interface{}{
+ file_provisionersdk_proto_provisioner_proto_msgTypes[38].OneofWrappers = []interface{}{
(*Request_Config)(nil),
(*Request_Parse)(nil),
(*Request_Plan)(nil),
(*Request_Apply)(nil),
(*Request_Cancel)(nil),
}
- file_provisionersdk_proto_provisioner_proto_msgTypes[37].OneofWrappers = []interface{}{
+ file_provisionersdk_proto_provisioner_proto_msgTypes[39].OneofWrappers = []interface{}{
(*Response_Log)(nil),
(*Response_Parse)(nil),
(*Response_Plan)(nil),
@@ -4682,8 +4905,8 @@ func file_provisionersdk_proto_provisioner_proto_init() {
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_provisionersdk_proto_provisioner_proto_rawDesc,
- NumEnums: 5,
- NumMessages: 42,
+ NumEnums: 6,
+ NumMessages: 44,
NumExtensions: 0,
NumServices: 1,
},
diff --git a/provisionersdk/proto/provisioner.proto b/provisionersdk/proto/provisioner.proto
index 3e6841fb24450..25c70b00a5486 100644
--- a/provisionersdk/proto/provisioner.proto
+++ b/provisionersdk/proto/provisioner.proto
@@ -73,6 +73,11 @@ message PresetParameter {
string value = 2;
}
+message ResourceReplacement {
+ string resource = 1;
+ repeated string paths = 2;
+}
+
// VariableValue holds the key/value mapping of a Terraform variable.
message VariableValue {
string name = 1;
@@ -272,6 +277,16 @@ message Role {
string org_id = 2;
}
+message RunningAgentAuthToken {
+ string agent_id = 1;
+ string token = 2;
+}
+enum PrebuiltWorkspaceBuildStage {
+ NONE = 0; // Default value for builds unrelated to prebuilds.
+ CREATE = 1; // A prebuilt workspace is being provisioned.
+ CLAIM = 2; // A prebuilt workspace is being claimed.
+}
+
// Metadata is information about a workspace used in the execution of a build
message Metadata {
string coder_url = 1;
@@ -293,8 +308,8 @@ message Metadata {
string workspace_build_id = 17;
string workspace_owner_login_type = 18;
repeated Role workspace_owner_rbac_roles = 19;
- bool is_prebuild = 20;
- string running_workspace_agent_token = 21;
+ PrebuiltWorkspaceBuildStage prebuilt_workspace_build_stage = 20; // Indicates that a prebuilt workspace is being built.
+ repeated RunningAgentAuthToken running_agent_auth_tokens = 21;
}
// Config represents execution configuration shared by all subsequent requests in the Session
@@ -336,6 +351,7 @@ message PlanComplete {
repeated Module modules = 7;
repeated Preset presets = 8;
bytes plan = 9;
+ repeated ResourceReplacement resource_replacements = 10;
}
// ApplyRequest asks the provisioner to apply the changes. Apply MUST be preceded by a successful plan request/response
diff --git a/site/e2e/helpers.ts b/site/e2e/helpers.ts
index f4ad6485b2681..335cf0f1e7ddc 100644
--- a/site/e2e/helpers.ts
+++ b/site/e2e/helpers.ts
@@ -583,6 +583,7 @@ const createTemplateVersionTar = async (
externalAuthProviders: response.apply?.externalAuthProviders ?? [],
timings: response.apply?.timings ?? [],
presets: [],
+ resourceReplacements: [],
plan: emptyPlan,
},
};
@@ -706,6 +707,7 @@ const createTemplateVersionTar = async (
timings: [],
modules: [],
presets: [],
+ resourceReplacements: [],
plan: emptyPlan,
...response.plan,
} as PlanComplete;
diff --git a/site/e2e/provisionerGenerated.ts b/site/e2e/provisionerGenerated.ts
index cea6f9cb364af..fd23950631e6a 100644
--- a/site/e2e/provisionerGenerated.ts
+++ b/site/e2e/provisionerGenerated.ts
@@ -38,6 +38,16 @@ export enum WorkspaceTransition {
UNRECOGNIZED = -1,
}
+export enum PrebuiltWorkspaceBuildStage {
+ /** NONE - Default value for builds unrelated to prebuilds. */
+ NONE = 0,
+ /** CREATE - A prebuilt workspace is being provisioned. */
+ CREATE = 1,
+ /** CLAIM - A prebuilt workspace is being claimed. */
+ CLAIM = 2,
+ UNRECOGNIZED = -1,
+}
+
export enum TimingState {
STARTED = 0,
COMPLETED = 1,
@@ -110,6 +120,11 @@ export interface PresetParameter {
value: string;
}
+export interface ResourceReplacement {
+ resource: string;
+ paths: string[];
+}
+
/** VariableValue holds the key/value mapping of a Terraform variable. */
export interface VariableValue {
name: string;
@@ -286,6 +301,11 @@ export interface Role {
orgId: string;
}
+export interface RunningAgentAuthToken {
+ agentId: string;
+ token: string;
+}
+
/** Metadata is information about a workspace used in the execution of a build */
export interface Metadata {
coderUrl: string;
@@ -307,8 +327,9 @@ export interface Metadata {
workspaceBuildId: string;
workspaceOwnerLoginType: string;
workspaceOwnerRbacRoles: Role[];
- isPrebuild: boolean;
- runningWorkspaceAgentToken: string;
+ /** Indicates that a prebuilt workspace is being built. */
+ prebuiltWorkspaceBuildStage: PrebuiltWorkspaceBuildStage;
+ runningAgentAuthTokens: RunningAgentAuthToken[];
}
/** Config represents execution configuration shared by all subsequent requests in the Session */
@@ -355,6 +376,7 @@ export interface PlanComplete {
modules: Module[];
presets: Preset[];
plan: Uint8Array;
+ resourceReplacements: ResourceReplacement[];
}
/**
@@ -554,6 +576,18 @@ export const PresetParameter = {
},
};
+export const ResourceReplacement = {
+ encode(message: ResourceReplacement, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
+ if (message.resource !== "") {
+ writer.uint32(10).string(message.resource);
+ }
+ for (const v of message.paths) {
+ writer.uint32(18).string(v!);
+ }
+ return writer;
+ },
+};
+
export const VariableValue = {
encode(message: VariableValue, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
if (message.name !== "") {
@@ -968,6 +1002,18 @@ export const Role = {
},
};
+export const RunningAgentAuthToken = {
+ encode(message: RunningAgentAuthToken, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
+ if (message.agentId !== "") {
+ writer.uint32(10).string(message.agentId);
+ }
+ if (message.token !== "") {
+ writer.uint32(18).string(message.token);
+ }
+ return writer;
+ },
+};
+
export const Metadata = {
encode(message: Metadata, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
if (message.coderUrl !== "") {
@@ -1027,11 +1073,11 @@ export const Metadata = {
for (const v of message.workspaceOwnerRbacRoles) {
Role.encode(v!, writer.uint32(154).fork()).ldelim();
}
- if (message.isPrebuild === true) {
- writer.uint32(160).bool(message.isPrebuild);
+ if (message.prebuiltWorkspaceBuildStage !== 0) {
+ writer.uint32(160).int32(message.prebuiltWorkspaceBuildStage);
}
- if (message.runningWorkspaceAgentToken !== "") {
- writer.uint32(170).string(message.runningWorkspaceAgentToken);
+ for (const v of message.runningAgentAuthTokens) {
+ RunningAgentAuthToken.encode(v!, writer.uint32(170).fork()).ldelim();
}
return writer;
},
@@ -1132,6 +1178,9 @@ export const PlanComplete = {
if (message.plan.length !== 0) {
writer.uint32(74).bytes(message.plan);
}
+ for (const v of message.resourceReplacements) {
+ ResourceReplacement.encode(v!, writer.uint32(82).fork()).ldelim();
+ }
return writer;
},
};
diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts
index afa766af64a84..7ec54965c2efb 100644
--- a/site/src/api/typesGenerated.ts
+++ b/site/src/api/typesGenerated.ts
@@ -1740,8 +1740,7 @@ export interface PreviewParameterData {
readonly type: PreviewParameterType;
// this is likely an enum in an external package "github.com/coder/terraform-provider-coder/v2/provider.ParameterFormType"
readonly form_type: string;
- // empty interface{} type, falling back to unknown
- readonly styling: unknown;
+ readonly styling: PreviewParameterStyling;
readonly mutable: boolean;
readonly default_value: NullHCLString;
readonly icon: string;
@@ -1760,6 +1759,13 @@ export interface PreviewParameterOption {
readonly icon: string;
}
+// From types/parameter.go
+export interface PreviewParameterStyling {
+ readonly placeholder?: string;
+ readonly disabled?: boolean;
+ readonly label?: string;
+}
+
// From types/enum.go
export type PreviewParameterType = string;