diff --git a/cli/create_test.go b/cli/create_test.go index dd26e450d3916..b927c3713a8c1 100644 --- a/cli/create_test.go +++ b/cli/create_test.go @@ -302,10 +302,10 @@ func TestCreate(t *testing.T) { func prepareEchoResponses(parameters []*proto.RichParameter, presets ...*proto.Preset) *echo.Responses { return &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Parameters: parameters, Presets: presets, }, @@ -1573,11 +1573,13 @@ func TestCreateValidateRichParameters(t *testing.T) { func TestCreateWithGitAuth(t *testing.T) { t.Parallel() echoResponses := &echo.Responses{ - Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + Parse: echo.ParseComplete, + ProvisionInit: echo.InitComplete, + ProvisionPlan: echo.PlanComplete, + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ ExternalAuthProviders: []*proto.ExternalAuthProviderResource{{Id: "github"}}, }, }, diff --git a/cli/restart_test.go b/cli/restart_test.go index 01be7e590cebf..a8cd7ee5f362f 100644 --- a/cli/restart_test.go +++ b/cli/restart_test.go @@ -306,10 +306,10 @@ func TestRestartWithParameters(t *testing.T) { echoResponses := func() *echo.Responses { return &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Parameters: []*proto.RichParameter{ { Name: immutableParameterName, diff --git a/cli/ssh_test.go b/cli/ssh_test.go index 7ce9d85258fa0..fac8c754056a8 100644 --- a/cli/ssh_test.go +++ b/cli/ssh_test.go @@ -155,7 +155,7 @@ func TestSSH(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID) @@ -244,7 +244,7 @@ func TestSSH(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID) @@ -305,7 +305,7 @@ func TestSSH(t *testing.T) { echoResponses := &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), } version := coderdtest.CreateTemplateVersion(t, ownerClient, owner.OrganizationID, echoResponses) @@ -326,7 +326,7 @@ func TestSSH(t *testing.T) { echoResponses2 := &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken2), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken2), } version = coderdtest.UpdateTemplateVersion(t, ownerClient, owner.OrganizationID, echoResponses2, template.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, ownerClient, version.ID) @@ -655,7 +655,7 @@ func TestSSH(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID) diff --git a/cli/start_test.go b/cli/start_test.go index 6e58b40e30778..e710a4185e3f3 100644 --- a/cli/start_test.go +++ b/cli/start_test.go @@ -36,10 +36,10 @@ const ( func mutableParamsResponse() *echo.Responses { return &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Parameters: []*proto.RichParameter{ { Name: mutableParameterName, @@ -59,10 +59,10 @@ func mutableParamsResponse() *echo.Responses { func immutableParamsResponse() *echo.Responses { return &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Parameters: []*proto.RichParameter{ { Name: immutableParameterName, @@ -83,11 +83,13 @@ func TestStart(t *testing.T) { echoResponses := func() *echo.Responses { return &echo.Responses{ - Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + Parse: echo.ParseComplete, + ProvisionInit: echo.InitComplete, + ProvisionPlan: echo.PlanComplete, + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Parameters: []*proto.RichParameter{ { Name: ephemeralParameterName, diff --git a/cli/task_test.go b/cli/task_test.go index fca04372600d8..ec44930e23b96 100644 --- a/cli/task_test.go +++ b/cli/task_test.go @@ -285,19 +285,10 @@ func createAITaskTemplate(t *testing.T, client *codersdk.Client, orgID uuid.UUID taskAppID := uuid.New() version := coderdtest.CreateTemplateVersion(t, client, orgID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ - HasAiTasks: true, - }, - }, - }, - }, - ProvisionApply: []*proto.Response{ - { - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{ { Name: "example", @@ -321,6 +312,7 @@ func createAITaskTemplate(t *testing.T, client *codersdk.Client, orgID uuid.UUID }, }, }, + HasAiTasks: true, AiTasks: []*proto.AITask{ { AppId: taskAppID.String(), diff --git a/cli/templatepresets_test.go b/cli/templatepresets_test.go index 3a8c8c39f0211..4b324692b8c00 100644 --- a/cli/templatepresets_test.go +++ b/cli/templatepresets_test.go @@ -282,10 +282,10 @@ func TestTemplatePresets(t *testing.T) { func templateWithPresets(presets []*proto.Preset) *echo.Responses { return &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Presets: presets, }, }, diff --git a/cli/templatepush_test.go b/cli/templatepush_test.go index 28c5adc20f213..df4d5327ba65c 100644 --- a/cli/templatepush_test.go +++ b/cli/templatepush_test.go @@ -1323,31 +1323,10 @@ func createEchoResponsesWithTemplateVariables(templateVariables []*proto.Templat func completeWithAgent() *echo.Responses { return &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ - Resources: []*proto.Resource{ - { - Type: "compute", - Name: "main", - Agents: []*proto.Agent{ - { - Name: "smith", - OperatingSystem: "linux", - Architecture: "i386", - }, - }, - }, - }, - }, - }, - }, - }, - ProvisionApply: []*proto.Response{ - { - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{ { Type: "compute", diff --git a/cli/templateversionarchive_test.go b/cli/templateversionarchive_test.go index 02fb72a6b7b74..b26b9dd2af492 100644 --- a/cli/templateversionarchive_test.go +++ b/cli/templateversionarchive_test.go @@ -71,6 +71,7 @@ func TestTemplateVersionsArchive(t *testing.T) { Parse: echo.ParseComplete, ProvisionApply: echo.ApplyFailed, ProvisionPlan: echo.PlanFailed, + ProvisionInit: echo.InitComplete, }, func(request *codersdk.CreateTemplateVersionRequest) { request.TemplateID = template.ID }) diff --git a/coderd/activitybump_test.go b/coderd/activitybump_test.go index e45895dd14a66..157640d828fe5 100644 --- a/coderd/activitybump_test.go +++ b/coderd/activitybump_test.go @@ -58,7 +58,7 @@ func TestWorkspaceActivityBump(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(agentToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(agentToken), }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) diff --git a/coderd/aitasks_test.go b/coderd/aitasks_test.go index 3301f8bdd5f31..289e7052c8a85 100644 --- a/coderd/aitasks_test.go +++ b/coderd/aitasks_test.go @@ -61,19 +61,11 @@ func TestTasks(t *testing.T) { taskAppID := uuid.New() version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ HasAiTasks: true, - }, - }, - }, - }, - ProvisionApply: []*proto.Response{ - { - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ Resources: []*proto.Resource{ { Name: "example", @@ -945,8 +937,8 @@ func TestTasksCreate(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionApply: echo.ApplyComplete, - ProvisionPlan: []*proto.Response{ - {Type: &proto.Response_Plan{Plan: &proto.PlanComplete{ + ProvisionGraph: []*proto.Response{ + {Type: &proto.Response_Graph{Graph: &proto.GraphComplete{ HasAiTasks: true, }}}, }, @@ -989,8 +981,8 @@ func TestTasksCreate(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionApply: echo.ApplyComplete, - ProvisionPlan: []*proto.Response{ - {Type: &proto.Response_Plan{Plan: &proto.PlanComplete{ + ProvisionGraph: []*proto.Response{ + {Type: &proto.Response_Graph{Graph: &proto.GraphComplete{ Parameters: []*proto.RichParameter{{Name: codersdk.AITaskPromptParameterName, Type: "string"}}, HasAiTasks: true, }}}, @@ -1091,8 +1083,8 @@ func TestTasksCreate(t *testing.T) { version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionApply: echo.ApplyComplete, - ProvisionPlan: []*proto.Response{ - {Type: &proto.Response_Plan{Plan: &proto.PlanComplete{ + ProvisionGraph: []*proto.Response{ + {Type: &proto.Response_Graph{Graph: &proto.GraphComplete{ HasAiTasks: true, }}}, }, @@ -1212,8 +1204,8 @@ func TestTasksCreate(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionApply: echo.ApplyComplete, - ProvisionPlan: []*proto.Response{ - {Type: &proto.Response_Plan{Plan: &proto.PlanComplete{ + ProvisionGraph: []*proto.Response{ + {Type: &proto.Response_Graph{Graph: &proto.GraphComplete{ HasAiTasks: true, }}}, }, @@ -1269,8 +1261,8 @@ func TestTasksCreate(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionApply: echo.ApplyComplete, - ProvisionPlan: []*proto.Response{ - {Type: &proto.Response_Plan{Plan: &proto.PlanComplete{ + ProvisionGraph: []*proto.Response{ + {Type: &proto.Response_Graph{Graph: &proto.GraphComplete{ HasAiTasks: true, }}}, }, @@ -1303,8 +1295,8 @@ func TestTasksCreate(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionApply: echo.ApplyComplete, - ProvisionPlan: []*proto.Response{ - {Type: &proto.Response_Plan{Plan: &proto.PlanComplete{ + ProvisionGraph: []*proto.Response{ + {Type: &proto.Response_Graph{Graph: &proto.GraphComplete{ HasAiTasks: true, }}}, }, @@ -1353,8 +1345,8 @@ func TestTasksCreate(t *testing.T) { version1 := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionApply: echo.ApplyComplete, - ProvisionPlan: []*proto.Response{ - {Type: &proto.Response_Plan{Plan: &proto.PlanComplete{ + ProvisionGraph: []*proto.Response{ + {Type: &proto.Response_Graph{Graph: &proto.GraphComplete{ HasAiTasks: true, }}}, }, @@ -1365,8 +1357,8 @@ func TestTasksCreate(t *testing.T) { version2 := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionApply: echo.ApplyComplete, - ProvisionPlan: []*proto.Response{ - {Type: &proto.Response_Plan{Plan: &proto.PlanComplete{ + ProvisionGraph: []*proto.Response{ + {Type: &proto.Response_Graph{Graph: &proto.GraphComplete{ HasAiTasks: true, }}}, }, diff --git a/coderd/audit_test.go b/coderd/audit_test.go index 13dbc9ccd8406..28bb05fbef5db 100644 --- a/coderd/audit_test.go +++ b/coderd/audit_test.go @@ -476,37 +476,10 @@ func TestAuditLogsFilter(t *testing.T) { func completeWithAgentAndApp() *echo.Responses { return &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ - Resources: []*proto.Resource{ - { - Type: "compute", - Name: "main", - Agents: []*proto.Agent{ - { - Name: "smith", - OperatingSystem: "linux", - Architecture: "i386", - Apps: []*proto.App{ - { - Slug: "app", - DisplayName: "App", - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - ProvisionApply: []*proto.Response{ - { - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{ { Type: "compute", diff --git a/coderd/autobuild/lifecycle_executor_test.go b/coderd/autobuild/lifecycle_executor_test.go index 0610c781fe966..58e48f321a8f8 100644 --- a/coderd/autobuild/lifecycle_executor_test.go +++ b/coderd/autobuild/lifecycle_executor_test.go @@ -233,9 +233,9 @@ func TestExecutorAutostartTemplateUpdated(t *testing.T) { // Since initial version has no parameters, any parameters in the new version will be incompatible res = &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Parameters: []*proto.RichParameter{ { Name: "new", @@ -1105,8 +1105,10 @@ func TestExecutorFailedWorkspace(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, + ProvisionInit: echo.InitComplete, ProvisionPlan: echo.PlanComplete, ProvisionApply: echo.ApplyFailed, + ProvisionGraph: echo.GraphComplete, }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { ctr.FailureTTLMillis = ptr.Ref[int64](failureTTL.Milliseconds()) @@ -1644,10 +1646,10 @@ func mustProvisionWorkspaceWithParameters(t *testing.T, client *codersdk.Client, user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Parameters: richParameters, }, }, @@ -1774,17 +1776,10 @@ func TestExecutorTaskWorkspace(t *testing.T) { taskAppID := uuid.New() version := coderdtest.CreateTemplateVersion(t, client, orgID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{HasAiTasks: true}, - }, - }, - }, - ProvisionApply: []*proto.Response{ - { - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{ { Agents: []*proto.Agent{ @@ -1804,6 +1799,7 @@ func TestExecutorTaskWorkspace(t *testing.T) { }, }, }, + HasAiTasks: true, AiTasks: []*proto.AITask{ { AppId: taskAppID.String(), diff --git a/coderd/coderd_test.go b/coderd/coderd_test.go index c94462814999e..4608a4aad9a0e 100644 --- a/coderd/coderd_test.go +++ b/coderd/coderd_test.go @@ -199,7 +199,7 @@ func TestDERPForceWebSockets(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) diff --git a/coderd/coderdtest/dynamicparameters.go b/coderd/coderdtest/dynamicparameters.go index 1cb60632aeaaa..7facd83221371 100644 --- a/coderd/coderdtest/dynamicparameters.go +++ b/coderd/coderdtest/dynamicparameters.go @@ -50,12 +50,24 @@ func DynamicParameterTemplate(t *testing.T, client *codersdk.Client, org uuid.UU } files := echo.WithExtraFiles(extraFiles) + files.ProvisionInit = []*proto.Response{{ + Type: &proto.Response_Init{ + Init: &proto.InitComplete{ + ModuleFiles: args.ModulesArchive, + }, + }, + }} files.ProvisionPlan = []*proto.Response{{ Type: &proto.Response_Plan{ Plan: &proto.PlanComplete{ - Plan: args.Plan, - ModuleFiles: args.ModulesArchive, - Parameters: args.StaticParams, + Plan: args.Plan, + }, + }, + }} + files.ProvisionGraph = []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ + Parameters: args.StaticParams, }, }, }} diff --git a/coderd/externalauth_test.go b/coderd/externalauth_test.go index 5219b54344320..7951ec3c51d8a 100644 --- a/coderd/externalauth_test.go +++ b/coderd/externalauth_test.go @@ -476,7 +476,7 @@ func TestExternalAuthCallback(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) @@ -507,7 +507,7 @@ func TestExternalAuthCallback(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) @@ -607,7 +607,7 @@ func TestExternalAuthCallback(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) @@ -668,7 +668,7 @@ func TestExternalAuthCallback(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) @@ -714,7 +714,7 @@ func TestExternalAuthCallback(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) @@ -779,7 +779,7 @@ func TestExternalAuthCallback(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgentAndAPIKeyScope(authToken, tt.apiKeyScope), + ProvisionGraph: echo.ProvisionApplyWithAgentAndAPIKeyScope(authToken, tt.apiKeyScope), }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) diff --git a/coderd/gitsshkey_test.go b/coderd/gitsshkey_test.go index 27f9121bd39b4..bd3b14f3c1fa2 100644 --- a/coderd/gitsshkey_test.go +++ b/coderd/gitsshkey_test.go @@ -111,7 +111,7 @@ func TestAgentGitSSHKey(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) project := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) @@ -149,7 +149,7 @@ func TestAgentGitSSHKey_APIKeyScopes(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgentAndAPIKeyScope(authToken, tt.apiKeyScope), + ProvisionGraph: echo.ProvisionApplyWithAgentAndAPIKeyScope(authToken, tt.apiKeyScope), }) project := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) diff --git a/coderd/insights_test.go b/coderd/insights_test.go index a4a47bea396a6..08b44839c0376 100644 --- a/coderd/insights_test.go +++ b/coderd/insights_test.go @@ -78,7 +78,7 @@ func TestDeploymentInsights(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) require.Empty(t, template.BuildTimeStats[codersdk.WorkspaceTransitionStart]) @@ -168,7 +168,7 @@ func TestUserActivityInsights_SanityCheck(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) require.Empty(t, template.BuildTimeStats[codersdk.WorkspaceTransitionStart]) @@ -266,7 +266,7 @@ func TestUserLatencyInsights(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) require.Empty(t, template.BuildTimeStats[codersdk.WorkspaceTransitionStart]) @@ -641,22 +641,16 @@ func TestTemplateInsights_Golden(t *testing.T) { // Create the template version and template. version := coderdtest.CreateTemplateVersion(t, client, firstUser.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Parameters: parameters, + Resources: resources, }, }, }, }, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ - Resources: resources, - }, - }, - }}, }) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) @@ -1539,9 +1533,9 @@ func TestUserActivityInsights_Golden(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, firstUser.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: resources, }, }, diff --git a/coderd/prometheusmetrics/prometheusmetrics_test.go b/coderd/prometheusmetrics/prometheusmetrics_test.go index e75f86e51b55c..d5d7242142a66 100644 --- a/coderd/prometheusmetrics/prometheusmetrics_test.go +++ b/coderd/prometheusmetrics/prometheusmetrics_test.go @@ -536,9 +536,9 @@ func TestAgents(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "aws_instance", @@ -866,7 +866,7 @@ func prepareWorkspaceAndAgent(ctx context.Context, t *testing.T, client *codersd version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) diff --git a/coderd/templates_test.go b/coderd/templates_test.go index df50b28ab861e..f99f5d07be2f6 100644 --- a/coderd/templates_test.go +++ b/coderd/templates_test.go @@ -1771,7 +1771,7 @@ func TestTemplateMetrics(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) require.Equal(t, -1, template.ActiveUserCount) diff --git a/coderd/templateversions_test.go b/coderd/templateversions_test.go index f282f8420b52e..938c202863bd5 100644 --- a/coderd/templateversions_test.go +++ b/coderd/templateversions_test.go @@ -857,9 +857,9 @@ func TestTemplateVersionsExternalAuth(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{{ - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ ExternalAuthProviders: []*proto.ExternalAuthProviderResource{{Id: "github", Optional: true}}, }, }, @@ -912,9 +912,9 @@ func TestTemplateVersionResources(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "some", Type: "example", @@ -953,7 +953,7 @@ func TestTemplateVersionLogs(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: []*proto.Response{{ + ProvisionGraph: []*proto.Response{{ Type: &proto.Response_Log{ Log: &proto.Log{ Level: proto.LogLevel_INFO, @@ -961,8 +961,8 @@ func TestTemplateVersionLogs(t *testing.T) { }, }, }, { - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "some", Type: "example", @@ -1211,15 +1211,15 @@ func TestTemplateVersionDryRun(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { Type: &proto.Response_Log{ Log: &proto.Log{}, }, }, { - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{resource}, }, }, @@ -2060,10 +2060,10 @@ func TestTemplateVersionParameters_Order(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Parameters: []*proto.RichParameter{ { Name: firstParameterName, @@ -2133,6 +2133,7 @@ func TestTemplateArchiveVersions(t *testing.T) { Parse: echo.ParseComplete, ProvisionPlan: echo.PlanFailed, ProvisionApply: echo.ApplyFailed, + ProvisionInit: echo.InitComplete, }, func(req *codersdk.CreateTemplateVersionRequest) { req.TemplateID = template.ID }) @@ -2228,10 +2229,10 @@ func TestTemplateVersionHasExternalAgent(t *testing.T) { ctx := testutil.Context(t, testutil.WaitMedium) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{ { Name: "example", diff --git a/coderd/workspaceagents_test.go b/coderd/workspaceagents_test.go index 6c12f91d37388..5a4a930ebe3b2 100644 --- a/coderd/workspaceagents_test.go +++ b/coderd/workspaceagents_test.go @@ -495,7 +495,7 @@ func TestWorkspaceAgentConnectRPC(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) @@ -506,9 +506,9 @@ func TestWorkspaceAgentConnectRPC(t *testing.T) { version = coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "aws_instance", diff --git a/coderd/workspaceapps/apptest/setup.go b/coderd/workspaceapps/apptest/setup.go index 65eebf8ecada5..0cd09f6c333a8 100644 --- a/coderd/workspaceapps/apptest/setup.go +++ b/coderd/workspaceapps/apptest/setup.go @@ -463,9 +463,9 @@ func createWorkspaceWithApps(t *testing.T, client *codersdk.Client, orgID uuid.U version := coderdtest.CreateTemplateVersion(t, client, orgID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "aws_instance", diff --git a/coderd/workspaceapps/db_test.go b/coderd/workspaceapps/db_test.go index a7ad1a85e5521..aa436f4cc3c30 100644 --- a/coderd/workspaceapps/db_test.go +++ b/coderd/workspaceapps/db_test.go @@ -121,9 +121,9 @@ func Test_ResolveRequest(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, firstUser.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "aws_instance", diff --git a/coderd/workspacebuilds_test.go b/coderd/workspacebuilds_test.go index d0ab64b1aeb32..ae2e7180ea8c0 100644 --- a/coderd/workspacebuilds_test.go +++ b/coderd/workspacebuilds_test.go @@ -556,13 +556,16 @@ func TestPatchCancelWorkspaceBuild(t *testing.T) { client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ - Parse: echo.ParseComplete, + Parse: echo.ParseComplete, + ProvisionInit: echo.InitComplete, + ProvisionGraph: echo.GraphComplete, + ProvisionPlan: echo.PlanComplete, + // Echo will never applying since there is no complete message ProvisionApply: []*proto.Response{{ Type: &proto.Response_Log{ Log: &proto.Log{}, }, }}, - ProvisionPlan: echo.PlanComplete, }) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) @@ -603,13 +606,16 @@ func TestPatchCancelWorkspaceBuild(t *testing.T) { client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, Logger: &logger}) owner := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, &echo.Responses{ - Parse: echo.ParseComplete, + Parse: echo.ParseComplete, + ProvisionInit: echo.InitComplete, + ProvisionGraph: echo.GraphComplete, + ProvisionPlan: echo.PlanComplete, + // Echo will never applying ProvisionApply: []*proto.Response{{ Type: &proto.Response_Log{ Log: &proto.Log{}, }, }}, - ProvisionPlan: echo.PlanComplete, }) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID) @@ -694,13 +700,16 @@ func TestPatchCancelWorkspaceBuild(t *testing.T) { client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ - Parse: echo.ParseComplete, + Parse: echo.ParseComplete, + ProvisionInit: echo.InitComplete, + ProvisionGraph: echo.GraphComplete, + ProvisionPlan: echo.PlanComplete, + // Echo will never applying ProvisionApply: []*proto.Response{{ Type: &proto.Response_Log{ Log: &proto.Log{}, }, }}, - ProvisionPlan: echo.PlanComplete, }) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) @@ -791,13 +800,16 @@ func TestPatchCancelWorkspaceBuild(t *testing.T) { client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ - Parse: echo.ParseComplete, + Parse: echo.ParseComplete, + ProvisionInit: echo.InitComplete, + ProvisionGraph: echo.GraphComplete, + ProvisionPlan: echo.PlanComplete, + // Echo will never applying ProvisionApply: []*proto.Response{{ Type: &proto.Response_Log{ Log: &proto.Log{}, }, }}, - ProvisionPlan: echo.PlanComplete, }) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) @@ -825,9 +837,9 @@ func TestWorkspaceBuildResources(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "first_resource", Type: "example", @@ -1032,7 +1044,7 @@ func TestWorkspaceBuildLogs(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{{ + ProvisionGraph: []*proto.Response{{ Type: &proto.Response_Log{ Log: &proto.Log{ Level: proto.LogLevel_INFO, @@ -1040,8 +1052,8 @@ func TestWorkspaceBuildLogs(t *testing.T) { }, }, }, { - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "some", Type: "example", @@ -1208,9 +1220,9 @@ func TestWorkspaceDeleteSuspendedUser(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, first.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionApply: echo.ApplyComplete, - ProvisionPlan: []*proto.Response{{ - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Error: "", Resources: nil, Parameters: nil, @@ -1483,28 +1495,6 @@ func TestPostWorkspaceBuild(t *testing.T) { require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) }) - t.Run("TemplateVersionFailedImport", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) - user := coderdtest.CreateFirstUser(t, client) - version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ - ProvisionApply: []*proto.Response{{}}, - }) - template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) - coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) - - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) - defer cancel() - - _, err := client.CreateWorkspace(ctx, user.OrganizationID, codersdk.Me, codersdk.CreateWorkspaceRequest{ - TemplateID: template.ID, - Name: "workspace", - }) - var apiErr *codersdk.Error - require.ErrorAs(t, err, &apiErr) - require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) - }) - t.Run("AlreadyActive", func(t *testing.T) { t.Parallel() client, closer := coderdtest.NewWithProvisionerCloser(t, nil) @@ -1642,9 +1632,9 @@ func TestPostWorkspaceBuild(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{{ - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Presets: []*proto.Preset{ { Name: "autodetected", diff --git a/coderd/workspaceresourceauth_test.go b/coderd/workspaceresourceauth_test.go index 73524a63ade62..5282adb0fb4d2 100644 --- a/coderd/workspaceresourceauth_test.go +++ b/coderd/workspaceresourceauth_test.go @@ -26,9 +26,9 @@ func TestPostWorkspaceAuthAzureInstanceIdentity(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "somename", Type: "someinstance", @@ -70,9 +70,9 @@ func TestPostWorkspaceAuthAWSInstanceIdentity(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "somename", Type: "someinstance", @@ -151,9 +151,9 @@ func TestPostWorkspaceAuthGoogleInstanceIdentity(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "somename", Type: "someinstance", diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index 4ab334222a438..232ffd8c3f302 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -213,9 +213,9 @@ func TestWorkspace(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "some", Type: "example", @@ -254,9 +254,9 @@ func TestWorkspace(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "some", Type: "example", @@ -299,9 +299,9 @@ func TestWorkspace(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "some", Type: "example", @@ -660,9 +660,9 @@ func TestWorkspace(t *testing.T) { authz := coderdtest.AssertRBAC(t, api, client) // Create a plan response with the specified presets and parameters - planResponse := &proto.Response{ - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + graphResponse := &proto.Response{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Presets: tc.presets, Parameters: tc.templateVersionParameters, }, @@ -671,7 +671,7 @@ func TestWorkspace(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{planResponse}, + ProvisionGraph: []*proto.Response{graphResponse}, ProvisionApply: echo.ApplyComplete, }) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) @@ -2194,7 +2194,7 @@ func TestWorkspaceFilterManual(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) @@ -2222,7 +2222,7 @@ func TestWorkspaceFilterManual(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) @@ -2253,9 +2253,9 @@ func TestWorkspaceFilterManual(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "aws_instance", @@ -2345,7 +2345,7 @@ func TestWorkspaceFilterManual(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) _ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) @@ -2450,10 +2450,10 @@ func TestWorkspaceFilterManual(t *testing.T) { makeParameters := func(extra ...*proto.RichParameter) *echo.Responses { return &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Parameters: append([]*proto.RichParameter{ {Name: paramOneName, Description: "", Mutable: true, Type: "string"}, {Name: paramTwoName, DisplayName: "", Description: "", Mutable: true, Type: "string"}, @@ -3307,9 +3307,9 @@ func TestWorkspaceWatcher(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "aws_instance", @@ -3392,8 +3392,10 @@ func TestWorkspaceWatcher(t *testing.T) { // Add a new version that will fail. badVersion := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ - Parse: echo.ParseComplete, - ProvisionPlan: echo.PlanComplete, + Parse: echo.ParseComplete, + ProvisionPlan: echo.PlanComplete, + ProvisionInit: echo.InitComplete, + ProvisionGraph: echo.GraphComplete, ProvisionApply: []*proto.Response{{ Type: &proto.Response_Apply{ Apply: &proto.ApplyComplete{ @@ -3461,9 +3463,9 @@ func TestWorkspaceResource(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "beta", Type: "example", @@ -3529,9 +3531,9 @@ func TestWorkspaceResource(t *testing.T) { } version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "some", Type: "example", @@ -3604,9 +3606,9 @@ func TestWorkspaceResource(t *testing.T) { } version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "some", Type: "example", @@ -3648,9 +3650,9 @@ func TestWorkspaceResource(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "some", Type: "example", @@ -3728,10 +3730,10 @@ func TestWorkspaceWithRichParameters(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Parameters: []*proto.RichParameter{ { Name: firstParameterName, @@ -3832,10 +3834,10 @@ func TestWorkspaceWithMultiSelectFailure(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Parameters: []*proto.RichParameter{ { Name: "param", @@ -3911,10 +3913,10 @@ func TestWorkspaceWithOptionalRichParameters(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Parameters: []*proto.RichParameter{ { Name: firstParameterName, @@ -4002,10 +4004,10 @@ func TestWorkspaceWithEphemeralRichParameters(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Parameters: []*proto.RichParameter{ { Name: firstParameterName, @@ -4804,8 +4806,8 @@ func TestWorkspaceListTasks(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionApply: echo.ApplyComplete, - ProvisionPlan: []*proto.Response{ - {Type: &proto.Response_Plan{Plan: &proto.PlanComplete{ + ProvisionGraph: []*proto.Response{ + {Type: &proto.Response_Graph{Graph: &proto.GraphComplete{ HasAiTasks: true, }}}, }, @@ -4874,9 +4876,9 @@ func TestWorkspaceAppUpsertRestart(t *testing.T) { // Create template version with workspace app version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "test-resource", Type: "example", @@ -4948,9 +4950,9 @@ func TestMultipleAITasksDisallowed(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{{ - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ HasAiTasks: true, AiTasks: []*proto.AITask{ { @@ -5172,10 +5174,10 @@ func TestWorkspaceCreateWithImplicitPreset(t *testing.T) { createTemplateWithPresets := func(t *testing.T, client *codersdk.Client, user codersdk.CreateFirstUserResponse, presets []*proto.Preset) (codersdk.Template, codersdk.TemplateVersion) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Presets: presets, }, }, diff --git a/codersdk/toolsdk/toolsdk_test.go b/codersdk/toolsdk/toolsdk_test.go index f69bcc4d0e7fe..0a8a94c8dc9c7 100644 --- a/codersdk/toolsdk/toolsdk_test.go +++ b/codersdk/toolsdk/toolsdk_test.go @@ -1015,8 +1015,8 @@ func TestTools(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionApply: echo.ApplyComplete, - ProvisionPlan: []*proto.Response{ - {Type: &proto.Response_Plan{Plan: &proto.PlanComplete{ + ProvisionGraph: []*proto.Response{ + {Type: &proto.Response_Graph{Graph: &proto.GraphComplete{ Parameters: []*proto.RichParameter{{Name: "AI Prompt", Type: "string"}}, HasAiTasks: true, }}}, diff --git a/enterprise/cli/create_test.go b/enterprise/cli/create_test.go index 44218abb5a58d..b6699430b8c6e 100644 --- a/enterprise/cli/create_test.go +++ b/enterprise/cli/create_test.go @@ -560,20 +560,12 @@ func TestEnterpriseCreateWithPreset(t *testing.T) { func prepareEchoResponses(parameters []*proto.RichParameter, presets ...*proto.Preset) *echo.Responses { return &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Parameters: parameters, Presets: presets, - }, - }, - }, - }, - ProvisionApply: []*proto.Response{ - { - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ Resources: []*proto.Resource{ { Type: "compute", diff --git a/enterprise/cli/externalworkspaces_test.go b/enterprise/cli/externalworkspaces_test.go index 9ce39c7c28afb..f8491e37fe040 100644 --- a/enterprise/cli/externalworkspaces_test.go +++ b/enterprise/cli/externalworkspaces_test.go @@ -24,10 +24,10 @@ import ( func completeWithExternalAgent() *echo.Responses { return &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{ { Type: "coder_external_agent", @@ -46,27 +46,6 @@ func completeWithExternalAgent() *echo.Responses { }, }, }, - ProvisionApply: []*proto.Response{ - { - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ - Resources: []*proto.Resource{ - { - Type: "coder_external_agent", - Name: "main", - Agents: []*proto.Agent{ - { - Name: "external-agent", - OperatingSystem: "linux", - Architecture: "amd64", - }, - }, - }, - }, - }, - }, - }, - }, } } @@ -74,31 +53,10 @@ func completeWithExternalAgent() *echo.Responses { func completeWithRegularAgent() *echo.Responses { return &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ - { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ - Resources: []*proto.Resource{ - { - Type: "compute", - Name: "main", - Agents: []*proto.Agent{ - { - Name: "regular-agent", - OperatingSystem: "linux", - Architecture: "amd64", - }, - }, - }, - }, - }, - }, - }, - }, - ProvisionApply: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{ { Type: "compute", diff --git a/enterprise/coderd/coderd_test.go b/enterprise/coderd/coderd_test.go index c3e6e1579fe91..8e14a156f9826 100644 --- a/enterprise/coderd/coderd_test.go +++ b/enterprise/coderd/coderd_test.go @@ -655,21 +655,21 @@ func TestManagedAgentLimit(t *testing.T) { // build. appID := uuid.NewString() echoRes := &echo.Responses{ - Parse: echo.ParseComplete, + Parse: echo.ParseComplete, + ProvisionInit: echo.InitComplete, ProvisionPlan: []*proto.Response{ { Type: &proto.Response_Plan{ Plan: &proto.PlanComplete{ - Plan: []byte("{}"), - ModuleFiles: []byte{}, - HasAiTasks: true, + Plan: []byte("{}"), }, }, }, }, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionApply: echo.ApplyComplete, + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "aws_instance", diff --git a/enterprise/coderd/gitsshkey_test.go b/enterprise/coderd/gitsshkey_test.go index 7045c8dd860fe..c51952ce19a8a 100644 --- a/enterprise/coderd/gitsshkey_test.go +++ b/enterprise/coderd/gitsshkey_test.go @@ -62,7 +62,7 @@ func TestAgentGitSSHKeyCustomRoles(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, org.ID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) project := coderdtest.CreateTemplate(t, client, org.ID, version.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) diff --git a/enterprise/coderd/prebuilds/claim_test.go b/enterprise/coderd/prebuilds/claim_test.go index 217a9ff09614a..d4326a4fd0eff 100644 --- a/enterprise/coderd/prebuilds/claim_test.go +++ b/enterprise/coderd/prebuilds/claim_test.go @@ -384,10 +384,10 @@ func TestClaimPrebuild(t *testing.T) { func templateWithAgentAndPresetsWithPrebuilds(desiredInstances int32) *echo.Responses { return &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{ { Type: "compute", @@ -442,26 +442,5 @@ func templateWithAgentAndPresetsWithPrebuilds(desiredInstances int32) *echo.Resp }, }, }, - 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", - }, - }, - }, - }, - }, - }, - }, - }, } } diff --git a/enterprise/coderd/provisionerdaemons_test.go b/enterprise/coderd/provisionerdaemons_test.go index 5797e978fa34c..31f31285ee263 100644 --- a/enterprise/coderd/provisionerdaemons_test.go +++ b/enterprise/coderd/provisionerdaemons_test.go @@ -256,21 +256,16 @@ func TestProvisionerDaemonServe(t *testing.T) { authToken := uuid.NewString() data, err := echo.Tar(&echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*sdkproto.Response{{ - Type: &sdkproto.Response_Plan{ - Plan: &sdkproto.PlanComplete{ - Resources: []*sdkproto.Resource{{ - Name: "example", - Type: "aws_instance", - Agents: []*sdkproto.Agent{{ - Id: uuid.NewString(), - Name: "example", - }}, - }}, - }, - }, - }}, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken, func(g *sdkproto.GraphComplete) { + g.Resources = []*sdkproto.Resource{{ + Name: "example", + Type: "aws_instance", + Agents: []*sdkproto.Agent{{ + Id: uuid.NewString(), + Name: "example", + }}, + }} + }), }) require.NoError(t, err) //nolint:gocritic // Not testing file upload in this test. @@ -446,9 +441,9 @@ func TestProvisionerDaemonServe(t *testing.T) { authToken := uuid.NewString() version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: []*sdkproto.Response{{ - Type: &sdkproto.Response_Apply{ - Apply: &sdkproto.ApplyComplete{ + ProvisionGraph: []*sdkproto.Response{{ + Type: &sdkproto.Response_Graph{ + Graph: &sdkproto.GraphComplete{ Resources: []*sdkproto.Resource{{ Name: "example", Type: "aws_instance", diff --git a/enterprise/coderd/templates_test.go b/enterprise/coderd/templates_test.go index e5e97085716da..cbb30be6e36d7 100644 --- a/enterprise/coderd/templates_test.go +++ b/enterprise/coderd/templates_test.go @@ -147,7 +147,7 @@ func TestTemplates(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: []*proto.Response{{ + ProvisionGraph: []*proto.Response{{ Type: &proto.Response_Log{ Log: &proto.Log{ Level: proto.LogLevel_INFO, @@ -155,8 +155,8 @@ func TestTemplates(t *testing.T) { }, }, }, { - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "some", Type: "example", @@ -2161,10 +2161,10 @@ func TestInvalidateTemplatePrebuilds(t *testing.T) { }) templateAdminClient, _ := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID, rbac.RoleTemplateAdmin()) - buildPlanResponse := func(presets ...*proto.Preset) *proto.Response { + buildGraphResponse := func(presets ...*proto.Preset) *proto.Response { return &proto.Response{ - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Presets: presets, Parameters: templateVersionParameters, }, @@ -2174,8 +2174,8 @@ func TestInvalidateTemplatePrebuilds(t *testing.T) { version1 := coderdtest.CreateTemplateVersion(t, templateAdminClient, owner.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{buildPlanResponse(presetWithParameters1, presetWithParameters2)}, ProvisionApply: echo.ApplyComplete, + ProvisionGraph: []*proto.Response{buildGraphResponse(presetWithParameters1, presetWithParameters2)}, }) coderdtest.AwaitTemplateVersionJobCompleted(t, templateAdminClient, version1.ID) template := coderdtest.CreateTemplate(t, templateAdminClient, owner.OrganizationID, version1.ID) @@ -2193,7 +2193,7 @@ func TestInvalidateTemplatePrebuilds(t *testing.T) { // Given the template is updated... version2 := coderdtest.UpdateTemplateVersion(t, templateAdminClient, owner.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{buildPlanResponse(presetWithParameters2, presetWithParameters3)}, + ProvisionGraph: []*proto.Response{buildGraphResponse(presetWithParameters2, presetWithParameters3)}, ProvisionApply: echo.ApplyComplete, }, template.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, templateAdminClient, version2.ID) @@ -2239,10 +2239,10 @@ func TestInvalidateTemplatePrebuilds_RegularUser(t *testing.T) { // Given version1 := coderdtest.CreateTemplateVersion(t, ownerClient, owner.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Presets: []*proto.Preset{presetWithParameters1}, Parameters: templateVersionParameters, }, diff --git a/enterprise/coderd/workspaceagents_test.go b/enterprise/coderd/workspaceagents_test.go index a150c0cdc06d5..6fc1e8aff4825 100644 --- a/enterprise/coderd/workspaceagents_test.go +++ b/enterprise/coderd/workspaceagents_test.go @@ -134,10 +134,10 @@ func TestReinitializeAgent(t *testing.T) { agentToken := uuid.UUID{3} version := coderdtest.CreateTemplateVersion(t, client, orgID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Presets: []*proto.Preset{ { Name: "test-preset", @@ -146,25 +146,6 @@ func TestReinitializeAgent(t *testing.T) { }, }, }, - 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", @@ -191,6 +172,13 @@ func TestReinitializeAgent(t *testing.T) { }, }, }, + ProvisionApply: []*proto.Response{ + { + Type: &proto.Response_Apply{ + Apply: &proto.ApplyComplete{}, + }, + }, + }, }) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) @@ -273,9 +261,9 @@ func setupWorkspaceAgent(t *testing.T, client *codersdk.Client, user codersdk.Cr authToken := uuid.NewString() version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "aws_instance", diff --git a/enterprise/coderd/workspaceproxy_test.go b/enterprise/coderd/workspaceproxy_test.go index d4be30d82293b..e0f73a5c0d745 100644 --- a/enterprise/coderd/workspaceproxy_test.go +++ b/enterprise/coderd/workspaceproxy_test.go @@ -633,7 +633,7 @@ func TestIssueSignedAppToken(t *testing.T) { authToken := uuid.NewString() version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) @@ -756,7 +756,7 @@ func TestReconnectingPTYSignedToken(t *testing.T) { authToken := uuid.NewString() version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) diff --git a/enterprise/coderd/workspacequota_test.go b/enterprise/coderd/workspacequota_test.go index 937aa8d57433a..8c39a29ada248 100644 --- a/enterprise/coderd/workspacequota_test.go +++ b/enterprise/coderd/workspacequota_test.go @@ -121,9 +121,16 @@ func TestWorkspaceQuota(t *testing.T) { authToken := uuid.NewString() version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionPlan: []*proto.Response{{ + Type: &proto.Response_Plan{ + Plan: &proto.PlanComplete{ + DailyCost: 1, + }, + }, + }}, + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "aws_instance", @@ -216,14 +223,17 @@ func TestWorkspaceQuota(t *testing.T) { verifyQuota(ctx, t, client, user.OrganizationID.String(), 0, 4) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ - Parse: echo.ParseComplete, + Parse: echo.ParseComplete, + ProvisionInit: echo.InitComplete, + ProvisionPlan: echo.PlanComplete, + ProvisionApply: echo.ApplyComplete, ProvisionPlanMap: map[proto.WorkspaceTransition][]*proto.Response{ proto.WorkspaceTransition_START: planWithCost(2), proto.WorkspaceTransition_STOP: planWithCost(1), }, - ProvisionApplyMap: map[proto.WorkspaceTransition][]*proto.Response{ - proto.WorkspaceTransition_START: applyWithCost(2), - proto.WorkspaceTransition_STOP: applyWithCost(1), + ProvisionGraphMap: map[proto.WorkspaceTransition][]*proto.Response{ + proto.WorkspaceTransition_START: graphWithCost(2), + proto.WorkspaceTransition_STOP: graphWithCost(1), }, }) @@ -422,10 +432,19 @@ func TestWorkspaceQuota(t *testing.T) { // Create a template with a workspace that costs 1 credit authToken := uuid.NewString() version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ - Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Parse: echo.ParseComplete, + ProvisionInit: echo.InitComplete, + ProvisionPlan: []*proto.Response{{ + Type: &proto.Response_Plan{ + Plan: &proto.PlanComplete{ + DailyCost: 1, + }, + }, + }}, + ProvisionApply: echo.ApplyComplete, + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "aws_instance", @@ -458,10 +477,19 @@ func TestWorkspaceQuota(t *testing.T) { // Test with a template that has zero cost - should pass versionZeroCost := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ - Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Parse: echo.ParseComplete, + ProvisionInit: echo.InitComplete, + ProvisionPlan: []*proto.Response{{ + Type: &proto.Response_Plan{ + Plan: &proto.PlanComplete{ + DailyCost: 0, + }, + }, + }}, + ProvisionApply: echo.ApplyComplete, + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "aws_instance", @@ -542,10 +570,19 @@ func TestWorkspaceQuota(t *testing.T) { // Create templates for both organizations authToken := uuid.NewString() version1 := coderdtest.CreateTemplateVersion(t, owner, first.OrganizationID, &echo.Responses{ - Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Parse: echo.ParseComplete, + ProvisionInit: echo.InitComplete, + ProvisionPlan: []*proto.Response{{ + Type: &proto.Response_Plan{ + Plan: &proto.PlanComplete{ + DailyCost: 1, + }, + }, + }}, + ProvisionApply: echo.ApplyComplete, + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "aws_instance", @@ -566,10 +603,19 @@ func TestWorkspaceQuota(t *testing.T) { template1 := coderdtest.CreateTemplate(t, owner, first.OrganizationID, version1.ID) version2 := coderdtest.CreateTemplateVersion(t, owner, second.ID, &echo.Responses{ - Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Parse: echo.ParseComplete, + ProvisionInit: echo.InitComplete, + ProvisionPlan: []*proto.Response{{ + Type: &proto.Response_Plan{ + Plan: &proto.PlanComplete{ + DailyCost: 1, + }, + }, + }}, + ProvisionApply: echo.ApplyComplete, + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "aws_instance", @@ -1156,20 +1202,16 @@ func planWithCost(cost int32) []*proto.Response { return []*proto.Response{{ Type: &proto.Response_Plan{ Plan: &proto.PlanComplete{ - Resources: []*proto.Resource{{ - Name: "example", - Type: "aws_instance", - DailyCost: cost, - }}, + DailyCost: cost, }, }, }} } -func applyWithCost(cost int32) []*proto.Response { +func graphWithCost(cost int32) []*proto.Response { return []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "aws_instance", diff --git a/enterprise/coderd/workspaces_test.go b/enterprise/coderd/workspaces_test.go index 7cf9cd890b6df..7ebab011a9a65 100644 --- a/enterprise/coderd/workspaces_test.go +++ b/enterprise/coderd/workspaces_test.go @@ -629,6 +629,8 @@ func TestWorkspaceAutobuild(t *testing.T) { Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, ProvisionApply: echo.ApplyFailed, + ProvisionInit: echo.InitComplete, + ProvisionGraph: echo.GraphComplete, }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { ctr.FailureTTLMillis = ptr.Ref[int64](failureTTL.Milliseconds()) @@ -680,6 +682,8 @@ func TestWorkspaceAutobuild(t *testing.T) { Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, ProvisionApply: echo.ApplyFailed, + ProvisionInit: echo.InitComplete, + ProvisionGraph: echo.GraphComplete, }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { ctr.FailureTTLMillis = ptr.Ref[int64](failureTTL.Milliseconds()) @@ -861,7 +865,7 @@ func TestWorkspaceAutobuild(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { @@ -1384,6 +1388,8 @@ func TestWorkspaceAutobuild(t *testing.T) { Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, ProvisionApply: echo.ApplyComplete, + ProvisionInit: echo.InitComplete, + ProvisionGraph: echo.GraphComplete, }) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) @@ -1397,6 +1403,8 @@ func TestWorkspaceAutobuild(t *testing.T) { Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, ProvisionApply: echo.ApplyFailed, + ProvisionInit: echo.InitComplete, + ProvisionGraph: echo.GraphComplete, }, func(ctvr *codersdk.CreateTemplateVersionRequest) { ctvr.TemplateID = template.ID }) @@ -2579,11 +2587,16 @@ func templateWithAgentAndPresetsWithPrebuilds(desiredInstances int32) *echo.Resp return r } - applyResponse := func(withAgent bool) *proto.Response { + graphResponse := func(withAgent bool) *proto.Response { return &proto.Response{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{resource(withAgent)}, + Presets: []*proto.Preset{{ + Name: "preset-test", + Parameters: []*proto.PresetParameter{{Name: "k1", Value: "v1"}}, + Prebuild: &proto.Prebuild{Instances: desiredInstances}, + }}, }, }, } @@ -2591,20 +2604,14 @@ func templateWithAgentAndPresetsWithPrebuilds(desiredInstances int32) *echo.Resp return &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{{ - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ - Presets: []*proto.Preset{{ - Name: "preset-test", - Parameters: []*proto.PresetParameter{{Name: "k1", Value: "v1"}}, - Prebuild: &proto.Prebuild{Instances: desiredInstances}, - }}, - }, + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{}, }, }}, - ProvisionApplyMap: map[proto.WorkspaceTransition][]*proto.Response{ - proto.WorkspaceTransition_START: {applyResponse(true)}, - proto.WorkspaceTransition_STOP: {applyResponse(false)}, + ProvisionGraphMap: map[proto.WorkspaceTransition][]*proto.Response{ + proto.WorkspaceTransition_START: {graphResponse(true)}, + proto.WorkspaceTransition_STOP: {graphResponse(false)}, }, } } @@ -2612,10 +2619,10 @@ func templateWithAgentAndPresetsWithPrebuilds(desiredInstances int32) *echo.Resp func templateWithFailedResponseAndPresetsWithPrebuilds(desiredInstances int32) *echo.Responses { return &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Presets: []*proto.Preset{ { Name: "preset-test", diff --git a/enterprise/provisionerd/remoteprovisioners_test.go b/enterprise/provisionerd/remoteprovisioners_test.go index 7b89d696ee20e..ea4ad11eb1fd5 100644 --- a/enterprise/provisionerd/remoteprovisioners_test.go +++ b/enterprise/provisionerd/remoteprovisioners_test.go @@ -74,10 +74,14 @@ func TestRemoteConnector_Mainline(t *testing.T) { c := resp.Client s, err := c.Session(ctx) require.NoError(t, err) - err = s.Send(&sdkproto.Request{Type: &sdkproto.Request_Config{Config: &sdkproto.Config{ + err = s.Send(&sdkproto.Request{Type: &sdkproto.Request_Config{Config: &sdkproto.Config{}}}) + require.NoError(t, err) + err = s.Send(&sdkproto.Request{Type: &sdkproto.Request_Init{Init: &sdkproto.InitRequest{ TemplateSourceArchive: arc, }}}) require.NoError(t, err) + _, err = s.Recv() + require.NoError(t, err) err = s.Send(&sdkproto.Request{Type: &sdkproto.Request_Parse{Parse: &sdkproto.ParseRequest{}}}) require.NoError(t, err) r, err := s.Recv() diff --git a/enterprise/wsproxy/wsproxy_test.go b/enterprise/wsproxy/wsproxy_test.go index c876db113ea60..fcb6493ad1e7c 100644 --- a/enterprise/wsproxy/wsproxy_test.go +++ b/enterprise/wsproxy/wsproxy_test.go @@ -173,7 +173,7 @@ func TestDERP(t *testing.T) { authToken := uuid.NewString() version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) @@ -411,7 +411,7 @@ func TestDERPEndToEnd(t *testing.T) { authToken := uuid.NewString() version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + ProvisionGraph: echo.ProvisionGraphWithAgent(authToken), }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) diff --git a/provisioner/echo/serve.go b/provisioner/echo/serve.go index 26d1fcbe3ad06..8f69e9e8ab0a1 100644 --- a/provisioner/echo/serve.go +++ b/provisioner/echo/serve.go @@ -12,6 +12,7 @@ import ( "text/template" "github.com/google/uuid" + "github.com/spf13/afero" "golang.org/x/xerrors" protobuf "google.golang.org/protobuf/proto" @@ -21,12 +22,12 @@ import ( "github.com/coder/coder/v2/provisionersdk/proto" ) -// ProvisionApplyWithAgent returns provision responses that will mock a fake +// ProvisionGraphWithAgent returns provision responses that will mock a fake // "aws_instance" resource with an agent that has the given auth token. func ProvisionApplyWithAgentAndAPIKeyScope(authToken string, apiKeyScope string) []*proto.Response { return []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example_with_scope", Type: "aws_instance", @@ -44,24 +45,29 @@ func ProvisionApplyWithAgentAndAPIKeyScope(authToken string, apiKeyScope string) }} } -// ProvisionApplyWithAgent returns provision responses that will mock a fake +// ProvisionGraphWithAgent returns provision responses that will mock a fake // "aws_instance" resource with an agent that has the given auth token. -func ProvisionApplyWithAgent(authToken string) []*proto.Response { +func ProvisionGraphWithAgent(authToken string, muts ...func(g *proto.GraphComplete)) []*proto.Response { + gc := &proto.GraphComplete{ + Resources: []*proto.Resource{{ + Name: "example", + Type: "aws_instance", + Agents: []*proto.Agent{{ + Id: uuid.NewString(), + Name: "example", + Auth: &proto.Agent_Token{ + Token: authToken, + }, + }}, + }}, + } + for _, mut := range muts { + mut(gc) + } + return []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ - Resources: []*proto.Resource{{ - Name: "example", - Type: "aws_instance", - Agents: []*proto.Agent{{ - Id: uuid.NewString(), - Name: "example", - Auth: &proto.Agent_Token{ - Token: authToken, - }, - }}, - }}, - }, + Type: &proto.Response_Graph{ + Graph: gc, }, }} } @@ -73,12 +79,19 @@ var ( Parse: &proto.ParseComplete{}, }, }} + // InitComplete is a helper to indicate an empty init completion. + InitComplete = []*proto.Response{{ + Type: &proto.Response_Init{ + Init: &proto.InitComplete{ + ModuleFiles: []byte{}, + }, + }, + }} // PlanComplete is a helper to indicate an empty provision completion. PlanComplete = []*proto.Response{{ Type: &proto.Response_Plan{ Plan: &proto.PlanComplete{ - Plan: []byte("{}"), - ModuleFiles: []byte{}, + Plan: []byte("{}"), }, }, }} @@ -88,7 +101,19 @@ var ( Apply: &proto.ApplyComplete{}, }, }} + GraphComplete = []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{}, + }, + }} + InitFailed = []*proto.Response{{ + Type: &proto.Response_Init{ + Init: &proto.InitComplete{ + Error: "failed!", + }, + }, + }} // PlanFailed is a helper to convey a failed plan operation PlanFailed = []*proto.Response{{ Type: &proto.Response_Plan{ @@ -105,6 +130,13 @@ var ( }, }, }} + GraphFailed = []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ + Error: "failed!", + }, + }, + }} ) // Serve starts the echo provisioner. @@ -174,6 +206,59 @@ func (*echo) Parse(sess *provisionersdk.Session, _ *proto.ParseRequest, _ <-chan return provisionersdk.ParseErrorf("complete response missing") } +func (*echo) Init(sess *provisionersdk.Session, req *proto.InitRequest, canceledOrComplete <-chan struct{}) *proto.InitComplete { + err := sess.Files.ExtractArchive(sess.Context(), sess.Logger, afero.NewOsFs(), req.TemplateSourceArchive) + if err != nil { + return provisionersdk.InitErrorf("extract archive: %s", err.Error()) + } + + responses, err := readResponses( + sess, + "", // transition not supported for echo graph responses + "init.protobuf") + if err != nil { + return &proto.InitComplete{Error: err.Error()} + } + for _, response := range responses { + if log := response.GetLog(); log != nil { + sess.ProvisionLog(log.Level, log.Output) + } + if complete := response.GetInit(); complete != nil { + return complete + } + } + + // some tests use Echo without a complete response to test cancel + <-canceledOrComplete + return provisionersdk.InitErrorf("canceled") +} + +func (*echo) Graph(sess *provisionersdk.Session, req *proto.GraphRequest, canceledOrComplete <-chan struct{}) *proto.GraphComplete { + responses, err := readResponses( + sess, + strings.ToLower(req.GetMetadata().GetWorkspaceTransition().String()), + "graph.protobuf") + if err != nil { + return &proto.GraphComplete{Error: err.Error()} + } + for _, response := range responses { + if log := response.GetLog(); log != nil { + sess.ProvisionLog(log.Level, log.Output) + } + if complete := response.GetGraph(); complete != nil { + if len(complete.AiTasks) > 0 { + // These two fields are linked; if there are AI tasks, indicate that. + complete.HasAiTasks = true + } + return complete + } + } + + // some tests use Echo without a complete response to test cancel + <-canceledOrComplete + return provisionersdk.GraphError("canceled") +} + // Plan reads requests from the provided directory to stream responses. func (*echo) Plan(sess *provisionersdk.Session, req *proto.PlanRequest, canceledOrComplete <-chan struct{}) *proto.PlanComplete { responses, err := readResponses( @@ -228,19 +313,73 @@ func (*echo) Shutdown(_ context.Context, _ *proto.Empty) (*proto.Empty, error) { type Responses struct { Parse []*proto.Response - // ProvisionApply and ProvisionPlan are used to mock ALL responses of - // Apply and Plan, regardless of transition. - ProvisionApply []*proto.Response + // Used to mock ALL responses regardless of transition. + ProvisionInit []*proto.Response ProvisionPlan []*proto.Response + ProvisionApply []*proto.Response + ProvisionGraph []*proto.Response - // ProvisionApplyMap and ProvisionPlanMap are used to mock specific - // transition responses. They are prioritized over the generic responses. - ProvisionApplyMap map[proto.WorkspaceTransition][]*proto.Response + // Used to mock specific transition responses. They are prioritized over the generic responses. ProvisionPlanMap map[proto.WorkspaceTransition][]*proto.Response + ProvisionApplyMap map[proto.WorkspaceTransition][]*proto.Response + ProvisionGraphMap map[proto.WorkspaceTransition][]*proto.Response ExtraFiles map[string][]byte } +func isType[T any](x any) bool { + _, ok := x.(T) + return ok +} + +func (r *Responses) Valid() error { + isLog := isType[*proto.Response_Log] + isParse := isType[*proto.Response_Parse] + isInit := isType[*proto.Response_Init] + isDataUpload := isType[*proto.Response_DataUpload] + isChunkPiece := isType[*proto.Response_ChunkPiece] + isPlan := isType[*proto.Response_Plan] + isApply := isType[*proto.Response_Apply] + isGraph := isType[*proto.Response_Graph] + + for _, parse := range r.Parse { + ty := parse.Type + if !(isParse(ty) || isLog(ty)) { + return xerrors.Errorf("invalid parse response type: %T", ty) + } + } + + for _, init := range r.ProvisionInit { + ty := init.Type + if !(isInit(ty) || isLog(ty) || isChunkPiece(ty) || isDataUpload(ty)) { + return xerrors.Errorf("invalid init response type: %T", ty) + } + } + + for _, plan := range r.ProvisionPlan { + ty := plan.Type + if !(isPlan(ty) || isLog(ty)) { + return xerrors.Errorf("invalid plan response type: %T", ty) + } + } + + for _, apply := range r.ProvisionApply { + ty := apply.Type + if !(isApply(ty) || isLog(ty)) { + return xerrors.Errorf("invalid apply response type: %T", ty) + } + } + + for _, graph := range r.ProvisionGraph { + ty := graph.Type + if !(isGraph(ty) || isLog(ty)) { + return xerrors.Errorf("invalid graph response type: %T", ty) + } + } + + return nil +} + // Tar returns a tar archive of responses to provisioner operations. func Tar(responses *Responses) ([]byte, error) { logger := slog.Make() @@ -255,27 +394,74 @@ func TarWithOptions(ctx context.Context, logger slog.Logger, responses *Response if responses == nil { responses = &Responses{ Parse: ParseComplete, - ProvisionApply: ApplyComplete, + ProvisionInit: InitComplete, ProvisionPlan: PlanComplete, + ProvisionApply: ApplyComplete, + ProvisionGraph: GraphComplete, ProvisionApplyMap: nil, ProvisionPlanMap: nil, ExtraFiles: nil, } } - if responses.ProvisionPlan == nil { + // source apply from the graph if graph exists + if responses.ProvisionApply == nil && len(responses.ProvisionGraph) > 0 { + for _, resp := range responses.ProvisionGraph { + if resp.GetLog() != nil { + responses.ProvisionApply = append(responses.ProvisionApply, resp) + continue + } + responses.ProvisionApply = append(responses.ProvisionApply, &proto.Response{ + Type: &proto.Response_Apply{Apply: &proto.ApplyComplete{ + Error: resp.GetGraph().GetError(), + }}, + }) + } + } + if responses.ProvisionGraph == nil { for _, resp := range responses.ProvisionApply { + if resp.GetLog() != nil { + responses.ProvisionGraph = append(responses.ProvisionGraph, resp) + continue + } + responses.ProvisionGraph = append(responses.ProvisionGraph, &proto.Response{ + Type: &proto.Response_Graph{Graph: &proto.GraphComplete{ + Error: resp.GetApply().GetError(), + }}, + }) + } + } + if responses.ProvisionInit == nil { + for _, resp := range responses.ProvisionGraph { + if resp.GetLog() != nil { + responses.ProvisionInit = append(responses.ProvisionInit, resp) + continue + } + responses.ProvisionInit = append(responses.ProvisionInit, &proto.Response{ + Type: &proto.Response_Init{ + Init: &proto.InitComplete{ + Error: resp.GetGraph().GetError(), + Timings: nil, + Modules: nil, + ModuleFiles: nil, + ModuleFilesHash: nil, + }, + }, + }, + ) + } + } + if responses.ProvisionPlan == nil { + for _, resp := range responses.ProvisionGraph { if resp.GetLog() != nil { responses.ProvisionPlan = append(responses.ProvisionPlan, resp) continue } responses.ProvisionPlan = append(responses.ProvisionPlan, &proto.Response{ Type: &proto.Response_Plan{Plan: &proto.PlanComplete{ - Error: resp.GetApply().GetError(), - Resources: resp.GetApply().GetResources(), - Parameters: resp.GetApply().GetParameters(), - ExternalAuthProviders: resp.GetApply().GetExternalAuthProviders(), - Plan: []byte("{}"), - ModuleFiles: []byte{}, + Error: resp.GetGraph().GetError(), + Plan: []byte("{}"), + //nolint:gosec // the number of resources will not exceed int32 + AiTaskCount: int32(len(resp.GetGraph().GetAiTasks())), }}, }) } @@ -315,6 +501,13 @@ func TarWithOptions(ctx context.Context, logger slog.Logger, responses *Response if err != nil { return err } + + response := new(proto.Response) + err = protobuf.Unmarshal(data, response) + if err != nil { + return xerrors.Errorf("you must have saved the wrong type, the proto cannot unmarshal: %w", err) + } + logger.Debug(context.Background(), "proto written", slog.F("name", name), slog.F("bytes_written", n)) return nil @@ -325,6 +518,12 @@ func TarWithOptions(ctx context.Context, logger slog.Logger, responses *Response return nil, err } } + for index, response := range responses.ProvisionInit { + err := writeProto(fmt.Sprintf("%d.init.protobuf", index), response) + if err != nil { + return nil, err + } + } for index, response := range responses.ProvisionApply { err := writeProto(fmt.Sprintf("%d.apply.protobuf", index), response) if err != nil { @@ -337,6 +536,12 @@ func TarWithOptions(ctx context.Context, logger slog.Logger, responses *Response return nil, err } } + for index, response := range responses.ProvisionGraph { + err := writeProto(fmt.Sprintf("%d.graph.protobuf", index), response) + if err != nil { + return nil, err + } + } for trans, m := range responses.ProvisionApplyMap { for i, rs := range m { err := writeProto(fmt.Sprintf("%d.%s.apply.protobuf", i, strings.ToLower(trans.String())), rs) @@ -360,6 +565,14 @@ func TarWithOptions(ctx context.Context, logger slog.Logger, responses *Response } } } + for trans, m := range responses.ProvisionGraphMap { + for i, resp := range m { + err := writeProto(fmt.Sprintf("%d.%s.graph.protobuf", i, strings.ToLower(trans.String())), resp) + if err != nil { + return nil, err + } + } + } dirs := []string{} for name, content := range responses.ExtraFiles { logger.Debug(ctx, "extra file", slog.F("name", name)) @@ -401,8 +614,8 @@ func TarWithOptions(ctx context.Context, logger slog.Logger, responses *Response // that matches the parameters defined in the responses. Dynamic parameters // parsed these, even in the echo provisioner. var mainTF bytes.Buffer - for _, respPlan := range responses.ProvisionPlan { - plan := respPlan.GetPlan() + for _, respPlan := range responses.ProvisionGraph { + plan := respPlan.GetGraph() if plan == nil { continue } @@ -440,6 +653,11 @@ terraform { if err != nil { return nil, err } + + if err := responses.Valid(); err != nil { + return nil, xerrors.Errorf("responses invalid: %w", err) + } + return buffer.Bytes(), nil } @@ -508,13 +726,14 @@ data "coder_parameter" "{{ .Name }}" { func WithResources(resources []*proto.Resource) *Responses { return &Responses{ - Parse: ParseComplete, - ProvisionApply: []*proto.Response{{Type: &proto.Response_Apply{Apply: &proto.ApplyComplete{ + Parse: ParseComplete, + ProvisionInit: InitComplete, + ProvisionApply: []*proto.Response{{Type: &proto.Response_Apply{Apply: &proto.ApplyComplete{}}}}, + ProvisionGraph: []*proto.Response{{Type: &proto.Response_Graph{Graph: &proto.GraphComplete{ Resources: resources, }}}}, ProvisionPlan: []*proto.Response{{Type: &proto.Response_Plan{Plan: &proto.PlanComplete{ - Resources: resources, - Plan: []byte("{}"), + Plan: []byte("{}"), }}}}, } } @@ -522,8 +741,10 @@ func WithResources(resources []*proto.Resource) *Responses { func WithExtraFiles(extraFiles map[string][]byte) *Responses { return &Responses{ Parse: ParseComplete, + ProvisionInit: InitComplete, ProvisionApply: ApplyComplete, ProvisionPlan: PlanComplete, + ProvisionGraph: GraphComplete, ExtraFiles: extraFiles, } } diff --git a/provisioner/echo/serve_test.go b/provisioner/echo/serve_test.go index 9168f1be6d22e..d170e9bfc6809 100644 --- a/provisioner/echo/serve_test.go +++ b/provisioner/echo/serve_test.go @@ -56,7 +56,8 @@ func TestEcho(t *testing.T) { }, } data, err := echo.Tar(&echo.Responses{ - Parse: responses, + Parse: responses, + ProvisionInit: echo.InitComplete, }) require.NoError(t, err) client, err := api.Session(ctx) @@ -65,9 +66,12 @@ func TestEcho(t *testing.T) { err := client.Close() require.NoError(t, err) }() - err = client.Send(&proto.Request{Type: &proto.Request_Config{Config: &proto.Config{ - TemplateSourceArchive: data, - }}}) + err = client.Send(&proto.Request{Type: &proto.Request_Config{Config: &proto.Config{}}}) + require.NoError(t, err) + + err = client.Send(&proto.Request{Type: &proto.Request_Init{Init: &proto.InitRequest{TemplateSourceArchive: data}}}) + require.NoError(t, err) + _, err = client.Recv() require.NoError(t, err) err = client.Send(&proto.Request{Type: &proto.Request_Parse{Parse: &proto.ParseRequest{}}}) @@ -96,11 +100,7 @@ func TestEcho(t *testing.T) { }, { Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ - Resources: []*proto.Resource{{ - Name: "resource", - }}, - }, + Plan: &proto.PlanComplete{}, }, }, } @@ -115,7 +115,22 @@ func TestEcho(t *testing.T) { }, { Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Apply: &proto.ApplyComplete{}, + }, + }, + } + graphResponses := []*proto.Response{ + { + Type: &proto.Response_Log{ + Log: &proto.Log{ + Level: proto.LogLevel_INFO, + Output: "graph output", + }, + }, + }, + { + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "resource", }}, @@ -124,8 +139,10 @@ func TestEcho(t *testing.T) { }, } data, err := echo.Tar(&echo.Responses{ + ProvisionInit: echo.InitComplete, ProvisionPlan: planResponses, ProvisionApply: applyResponses, + ProvisionGraph: graphResponses, }) require.NoError(t, err) client, err := api.Session(ctx) @@ -134,30 +151,53 @@ func TestEcho(t *testing.T) { err := client.Close() require.NoError(t, err) }() - err = client.Send(&proto.Request{Type: &proto.Request_Config{Config: &proto.Config{ - TemplateSourceArchive: data, - }}}) + err = client.Send(&proto.Request{Type: &proto.Request_Config{Config: &proto.Config{}}}) + require.NoError(t, err) + + err = client.Send(&proto.Request{Type: &proto.Request_Init{ + Init: &proto.InitRequest{ + TemplateSourceArchive: data, + }, + }}) + require.NoError(t, err) + + _, err = client.Recv() require.NoError(t, err) err = client.Send(&proto.Request{Type: &proto.Request_Plan{Plan: &proto.PlanRequest{}}}) require.NoError(t, err) + log, err := client.Recv() require.NoError(t, err) require.Equal(t, planResponses[0].GetLog().Output, log.GetLog().Output) + complete, err := client.Recv() require.NoError(t, err) - require.Equal(t, planResponses[1].GetPlan().Resources[0].Name, - complete.GetPlan().Resources[0].Name) + require.NotNil(t, complete) err = client.Send(&proto.Request{Type: &proto.Request_Apply{Apply: &proto.ApplyRequest{}}}) require.NoError(t, err) log, err = client.Recv() require.NoError(t, err) require.Equal(t, applyResponses[0].GetLog().Output, log.GetLog().Output) + + _, err = client.Recv() + require.NoError(t, err) + + err = client.Send(&proto.Request{Type: &proto.Request_Graph{ + Graph: &proto.GraphRequest{ + Source: proto.GraphSource_SOURCE_STATE, + }, + }}) + require.NoError(t, err) + + log, err = client.Recv() + require.NoError(t, err) + require.Equal(t, graphResponses[0].GetLog().Output, log.GetLog().Output) complete, err = client.Recv() require.NoError(t, err) - require.Equal(t, applyResponses[1].GetApply().Resources[0].Name, - complete.GetApply().Resources[0].Name) + require.Equal(t, graphResponses[1].GetGraph().Resources[0].Name, + complete.GetGraph().Resources[0].Name) }) t.Run("ProvisionStop", func(t *testing.T) { @@ -165,13 +205,12 @@ func TestEcho(t *testing.T) { // Stop responses should be returned when the workspace is being stopped. data, err := echo.Tar(&echo.Responses{ - ProvisionApply: applyCompleteResource("DEFAULT"), - ProvisionPlan: planCompleteResource("DEFAULT"), - ProvisionPlanMap: map[proto.WorkspaceTransition][]*proto.Response{ - proto.WorkspaceTransition_STOP: planCompleteResource("STOP"), - }, - ProvisionApplyMap: map[proto.WorkspaceTransition][]*proto.Response{ - proto.WorkspaceTransition_STOP: applyCompleteResource("STOP"), + ProvisionInit: echo.InitComplete, + ProvisionApply: echo.ApplyComplete, + ProvisionPlan: echo.PlanComplete, + ProvisionGraph: graphCompleteResource("DEFAULT"), + ProvisionGraphMap: map[proto.WorkspaceTransition][]*proto.Response{ + proto.WorkspaceTransition_STOP: graphCompleteResource("STOP"), }, }) require.NoError(t, err) @@ -182,15 +221,18 @@ func TestEcho(t *testing.T) { err := client.Close() require.NoError(t, err) }() - err = client.Send(&proto.Request{Type: &proto.Request_Config{Config: &proto.Config{ - TemplateSourceArchive: data, - }}}) + err = client.Send(&proto.Request{Type: &proto.Request_Config{Config: &proto.Config{}}}) + require.NoError(t, err) + + err = client.Send(&proto.Request{Type: &proto.Request_Init{Init: &proto.InitRequest{TemplateSourceArchive: data}}}) + require.NoError(t, err) + _, err = client.Recv() require.NoError(t, err) // Do stop. err = client.Send(&proto.Request{ - Type: &proto.Request_Plan{ - Plan: &proto.PlanRequest{ + Type: &proto.Request_Graph{ + Graph: &proto.GraphRequest{ Metadata: &proto.Metadata{ WorkspaceTransition: proto.WorkspaceTransition_STOP, }, @@ -203,13 +245,13 @@ func TestEcho(t *testing.T) { require.NoError(t, err) require.Equal(t, "STOP", - complete.GetPlan().Resources[0].Name, + complete.GetGraph().Resources[0].Name, ) // Do start. err = client.Send(&proto.Request{ - Type: &proto.Request_Plan{ - Plan: &proto.PlanRequest{ + Type: &proto.Request_Graph{ + Graph: &proto.GraphRequest{ Metadata: &proto.Metadata{ WorkspaceTransition: proto.WorkspaceTransition_START, }, @@ -222,7 +264,7 @@ func TestEcho(t *testing.T) { require.NoError(t, err) require.Equal(t, "DEFAULT", - complete.GetPlan().Resources[0].Name, + complete.GetGraph().Resources[0].Name, ) }) @@ -246,8 +288,8 @@ func TestEcho(t *testing.T) { }, }, }, { - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "resource", }}, @@ -255,8 +297,10 @@ func TestEcho(t *testing.T) { }, }} data, err := echo.Tar(&echo.Responses{ + ProvisionInit: echo.InitComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: responses, + ProvisionApply: echo.ApplyComplete, + ProvisionGraph: responses, }) require.NoError(t, err) client, err := api.Session(ctx) @@ -266,11 +310,15 @@ func TestEcho(t *testing.T) { require.NoError(t, err) }() err = client.Send(&proto.Request{Type: &proto.Request_Config{Config: &proto.Config{ - TemplateSourceArchive: data, - ProvisionerLogLevel: "debug", + ProvisionerLogLevel: "debug", }}}) require.NoError(t, err) + err = client.Send(&proto.Request{Type: &proto.Request_Init{Init: &proto.InitRequest{TemplateSourceArchive: data}}}) + require.NoError(t, err) + _, err = client.Recv() + require.NoError(t, err) + // Plan is required before apply err = client.Send(&proto.Request{Type: &proto.Request_Plan{Plan: &proto.PlanRequest{}}}) require.NoError(t, err) @@ -280,33 +328,30 @@ func TestEcho(t *testing.T) { err = client.Send(&proto.Request{Type: &proto.Request_Apply{Apply: &proto.ApplyRequest{}}}) require.NoError(t, err) + _, err = client.Recv() + require.NoError(t, err) + + err = client.Send(&proto.Request{Type: &proto.Request_Graph{ + Graph: &proto.GraphRequest{ + Source: proto.GraphSource_SOURCE_STATE, + }, + }}) + require.NoError(t, err) log, err := client.Recv() require.NoError(t, err) // Skip responses[0] as it's trace level require.Equal(t, responses[1].GetLog().Output, log.GetLog().Output) complete, err = client.Recv() require.NoError(t, err) - require.Equal(t, responses[2].GetApply().Resources[0].Name, - complete.GetApply().Resources[0].Name) + require.Equal(t, responses[2].GetGraph().Resources[0].Name, + complete.GetGraph().Resources[0].Name) }) } -func planCompleteResource(name string) []*proto.Response { - return []*proto.Response{{ - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ - Resources: []*proto.Resource{{ - Name: name, - }}, - }, - }, - }} -} - -func applyCompleteResource(name string) []*proto.Response { +func graphCompleteResource(name string) []*proto.Response { return []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: name, }}, diff --git a/provisioner/terraform/executor.go b/provisioner/terraform/executor.go index 3d9270a6ddbab..be33dbef8c17d 100644 --- a/provisioner/terraform/executor.go +++ b/provisioner/terraform/executor.go @@ -283,7 +283,7 @@ func (e *executor) init(ctx, killCtx context.Context, logr logSink) error { func checksumFileCRC32(ctx context.Context, logger slog.Logger, path string) uint32 { content, err := os.ReadFile(path) if err != nil { - logger.Debug(ctx, "file %s does not exist or can't be read, skip checksum calculation") + logger.Debug(ctx, fmt.Sprintf("file %s does not exist or can't be read, skip checksum calculation", path)) return 0 } return crc32.ChecksumIEEE(content) @@ -332,34 +332,16 @@ func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr l return nil, xerrors.Errorf("terraform plan: %w", err) } - // Capture the duration of the call to `terraform graph`. - graphTimings := newTimingAggregator(database.ProvisionerJobTimingStageGraph) - graphTimings.ingest(createGraphTimingsEvent(timingGraphStart)) - - state, plan, err := e.planResources(ctx, killCtx, planfilePath) + plan, err := e.parsePlan(ctx, killCtx, planfilePath) if err != nil { - graphTimings.ingest(createGraphTimingsEvent(timingGraphErrored)) - return nil, xerrors.Errorf("plan resources: %w", err) + return nil, xerrors.Errorf("show terraform plan file: %w", err) } + planJSON, err := json.Marshal(plan) if err != nil { return nil, xerrors.Errorf("marshal plan: %w", err) } - graphTimings.ingest(createGraphTimingsEvent(timingGraphComplete)) - - var moduleFiles []byte - // Skipping modules archiving is useful if the caller does not need it, eg during - // a workspace build. This removes some added costs of sending the modules - // payload back to coderd if coderd is just going to ignore it. - if !req.OmitModuleFiles { - moduleFiles, err = GetModulesArchive(os.DirFS(e.files.WorkDirectory())) - if err != nil { - // TODO: we probably want to persist this error or make it louder eventually - e.logger.Warn(ctx, "failed to archive terraform modules", slog.Error(err)) - } - } - // When a prebuild claim attempt is made, log a warning if a resource is due to be replaced, since this will obviate // the point of prebuilding if the expensive resource is replaced once claimed! var ( @@ -386,18 +368,19 @@ func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr l } } + state, err := ConvertPlanState(plan) + if err != nil { + return nil, xerrors.Errorf("convert plan state: %w", err) + } + + // DoneOut must be completed before we aggregate timings to ensure all logs have been processed. + <-doneOut msg := &proto.PlanComplete{ - Parameters: state.Parameters, - Resources: state.Resources, - ExternalAuthProviders: state.ExternalAuthProviders, - Timings: append(e.timings.aggregate(), graphTimings.aggregate()...), - Presets: state.Presets, - Plan: planJSON, - ResourceReplacements: resReps, - ModuleFiles: moduleFiles, - HasAiTasks: state.HasAITasks, - AiTasks: state.AITasks, - HasExternalAgents: state.HasExternalAgents, + Timings: e.timings.aggregate(), + Plan: planJSON, + DailyCost: state.DailyCost, + ResourceReplacements: resReps, + AiTaskCount: state.AITaskCount, } return msg, nil @@ -420,42 +403,6 @@ func onlyDataResources(sm tfjson.StateModule) tfjson.StateModule { return filtered } -// planResources must only be called while the lock is held. -func (e *executor) planResources(ctx, killCtx context.Context, planfilePath string) (*State, *tfjson.Plan, error) { - ctx, span := e.server.startTrace(ctx, tracing.FuncName()) - defer span.End() - - plan, err := e.parsePlan(ctx, killCtx, planfilePath) - if err != nil { - return nil, nil, xerrors.Errorf("show terraform plan file: %w", err) - } - - rawGraph, err := e.graph(ctx, killCtx) - if err != nil { - return nil, nil, xerrors.Errorf("graph: %w", err) - } - modules := []*tfjson.StateModule{} - if plan.PriorState != nil { - // We need the data resources for rich parameters. For some reason, they - // only show up in the PriorState. - // - // We don't want all prior resources, because Quotas (and - // future features) would never know which resources are getting - // deleted by a stop. - - filtered := onlyDataResources(*plan.PriorState.Values.RootModule) - modules = append(modules, &filtered) - } - modules = append(modules, plan.PlannedValues.RootModule) - - state, err := ConvertState(ctx, modules, rawGraph, e.server.logger) - if err != nil { - return nil, nil, err - } - - return state, plan, nil -} - // parsePlan must only be called while the lock is held. func (e *executor) parsePlan(ctx, killCtx context.Context, planfilePath string) (*tfjson.Plan, error) { ctx, span := e.server.startTrace(ctx, tracing.FuncName()) @@ -543,9 +490,11 @@ func (e *executor) graph(ctx, killCtx context.Context) (string, error) { // TODO: When the plan is present, we should probably use it? // "-plan=" + e.files.PlanFilePath(), } + if ver.GreaterThanOrEqual(version170) { args = append(args, "-type=plan") } + var out strings.Builder cmd := exec.CommandContext(killCtx, e.binaryPath, args...) // #nosec cmd.Stdout = &out @@ -606,55 +555,21 @@ func (e *executor) apply( return nil, xerrors.Errorf("terraform apply: %w", err) } - // `terraform show` & `terraform graph` - state, err := e.stateResources(ctx, killCtx) - if err != nil { - return nil, err - } statefilePath := e.files.StateFilePath() stateContent, err := os.ReadFile(statefilePath) if err != nil { return nil, xerrors.Errorf("read statefile %q: %w", statefilePath, err) } + // DoneOut must be completed before we aggregate timings to ensure all logs have been processed. + <-doneOut agg := e.timings.aggregate() return &proto.ApplyComplete{ - Parameters: state.Parameters, - Resources: state.Resources, - ExternalAuthProviders: state.ExternalAuthProviders, - State: stateContent, - Timings: agg, - AiTasks: state.AITasks, + State: stateContent, + Timings: agg, }, nil } -// stateResources must only be called while the lock is held. -func (e *executor) stateResources(ctx, killCtx context.Context) (*State, error) { - ctx, span := e.server.startTrace(ctx, tracing.FuncName()) - defer span.End() - - state, err := e.state(ctx, killCtx) - if err != nil { - return nil, err - } - rawGraph, err := e.graph(ctx, killCtx) - if err != nil { - return nil, xerrors.Errorf("get terraform graph: %w", err) - } - converted := &State{} - if state.Values == nil { - return converted, nil - } - - converted, err = ConvertState(ctx, []*tfjson.StateModule{ - state.Values.RootModule, - }, rawGraph, e.server.logger) - if err != nil { - return nil, err - } - return converted, nil -} - // state must only be called while the lock is held. func (e *executor) state(ctx, killCtx context.Context) (*tfjson.State, error) { ctx, span := e.server.startTrace(ctx, tracing.FuncName()) diff --git a/provisioner/terraform/parse_test.go b/provisioner/terraform/parse_test.go index d2a505235f688..cc2f5346690d9 100644 --- a/provisioner/terraform/parse_test.go +++ b/provisioner/terraform/parse_test.go @@ -21,8 +21,9 @@ func TestParse(t *testing.T) { Name string Files map[string]string Response *proto.ParseComplete - // If ErrorContains is not empty, then the ParseComplete should have an Error containing the given string - ErrorContains string + // If ErrorContains is not empty, then the InitComplete should have an Error containing the given string + ErrorContains string + ParseErrorContains string }{ { Name: "single-variable", @@ -63,6 +64,7 @@ func TestParse(t *testing.T) { "main.tf": `variable "A" { validation { condition = var.A == "value" + error_message = "A must be 'value'" } }`, }, @@ -80,7 +82,7 @@ func TestParse(t *testing.T) { Files: map[string]string{ "main.tf": "a;sd;ajsd;lajsd;lasjdf;a", }, - ErrorContains: `The ";" character is not valid.`, + ErrorContains: `initialize terraform: exit status 1`, }, { Name: "multiple-variables", @@ -205,6 +207,15 @@ func TestParse(t *testing.T) { { Name: "workspace-tags", Files: map[string]string{ + `main.tf`: ` + terraform { + required_providers { + coder = { + source = "coder/coder" + } + } + } + `, "parameters.tf": `data "coder_parameter" "os_selector" { name = "os_selector" display_name = "Operating System" @@ -266,7 +277,13 @@ func TestParse(t *testing.T) { Name: "workspace-tags-in-a-single-file", Files: map[string]string{ "main.tf": ` - + terraform { + required_providers { + coder = { + source = "coder/coder" + } + } + } data "coder_parameter" "os_selector" { name = "os_selector" display_name = "Operating System" @@ -330,7 +347,13 @@ func TestParse(t *testing.T) { Name: "workspace-tags-duplicate-tag", Files: map[string]string{ "main.tf": ` - + terraform { + required_providers { + coder = { + source = "coder/coder" + } + } + } data "coder_workspace_tags" "custom_workspace_tags" { tags = { "cluster" = "developers" @@ -341,23 +364,30 @@ func TestParse(t *testing.T) { } `, }, - ErrorContains: `workspace tag "debug" is defined multiple times`, + ParseErrorContains: `workspace tag "debug" is defined multiple times`, }, { Name: "workspace-tags-wrong-tag-format", Files: map[string]string{ "main.tf": ` + terraform { + required_providers { + coder = { + source = "coder/coder" + } + } + } - data "coder_workspace_tags" "custom_workspace_tags" { - tags { - cluster = "developers" - debug = "yes" - cache = "no-cache" + data "coder_workspace_tags" "custom_workspace_tags" { + tags { + cluster = "developers" + debug = "yes" + cache = "no-cache" + } } - } `, }, - ErrorContains: `"tags" attribute is required by coder_workspace_tags`, + ParseErrorContains: `"tags" attribute is required by coder_workspace_tags`, }, { Name: "empty-main", @@ -379,27 +409,43 @@ func TestParse(t *testing.T) { t.Run(testCase.Name, func(t *testing.T) { t.Parallel() - session := configure(ctx, t, api, &proto.Config{ - TemplateSourceArchive: testutil.CreateTar(t, testCase.Files), - }) - - err := session.Send(&proto.Request{Type: &proto.Request_Parse{Parse: &proto.ParseRequest{}}}) + session := configure(ctx, t, api, &proto.Config{}) + err := sendInit(session, testutil.CreateTar(t, testCase.Files)) require.NoError(t, err) + // Init stage for { msg, err := session.Recv() require.NoError(t, err) + if msgLog, ok := msg.Type.(*proto.Response_Log); ok { + t.Logf("init log: %s", msgLog.Log.Output) + continue + } if testCase.ErrorContains != "" { - require.Contains(t, msg.GetParse().GetError(), testCase.ErrorContains) - break + require.Contains(t, msg.GetInit().GetError(), testCase.ErrorContains) + return // Stop test at this point } + break + } + + err = session.Send(&proto.Request{Type: &proto.Request_Parse{Parse: &proto.ParseRequest{}}}) + require.NoError(t, err) + + for { + msg, err := session.Recv() + require.NoError(t, err) // Ignore logs in this test if msg.GetLog() != nil { continue } + if testCase.ParseErrorContains != "" { + require.Contains(t, msg.GetParse().GetError(), testCase.ParseErrorContains) + return // Stop test at this point + } + // Ensure the want and got are equivalent! want, err := json.Marshal(testCase.Response) require.NoError(t, err) diff --git a/provisioner/terraform/planresources.go b/provisioner/terraform/planresources.go new file mode 100644 index 0000000000000..3c3758df1d373 --- /dev/null +++ b/provisioner/terraform/planresources.go @@ -0,0 +1,80 @@ +package terraform + +import ( + tfjson "github.com/hashicorp/terraform-json" + "github.com/mitchellh/mapstructure" + "golang.org/x/xerrors" +) + +type PlanState struct { + DailyCost int32 + AITaskCount int32 +} + +func planModules(plan *tfjson.Plan) []*tfjson.StateModule { + modules := []*tfjson.StateModule{} + if plan.PriorState != nil { + // We need the data resources for rich parameters. For some reason, they + // only show up in the PriorState. + // + // We don't want all prior resources, because Quotas (and + // future features) would never know which resources are getting + // deleted by a stop. + + filtered := onlyDataResources(*plan.PriorState.Values.RootModule) + modules = append(modules, &filtered) + } + modules = append(modules, plan.PlannedValues.RootModule) + return modules +} + +// ConvertPlanState consumes a terraform plan json output and produces a thinner +// version of `State` to be used before `terraform apply`. `ConvertState` +// requires `terraform graph`, this does not. +func ConvertPlanState(plan *tfjson.Plan) (*PlanState, error) { + modules := planModules(plan) + + var dailyCost int32 + var aiTaskCount int32 + for _, mod := range modules { + err := forEachResource(mod, func(res *tfjson.StateResource) error { + switch res.Type { + case "coder_metadata": + var attrs resourceMetadataAttributes + err := mapstructure.Decode(res.AttributeValues, &attrs) + if err != nil { + return xerrors.Errorf("decode metadata attributes: %w", err) + } + dailyCost += attrs.DailyCost + case "coder_ai_task": + aiTaskCount++ + } + return nil + }) + if err != nil { + return nil, xerrors.Errorf("parse plan: %w", err) + } + } + + return &PlanState{ + DailyCost: dailyCost, + AITaskCount: aiTaskCount, + }, nil +} + +func forEachResource(input *tfjson.StateModule, do func(res *tfjson.StateResource) error) error { + for _, res := range input.Resources { + err := do(res) + if err != nil { + return xerrors.Errorf("in module %s: %w", input.Address, err) + } + } + + for _, mod := range input.ChildModules { + err := forEachResource(mod, do) + if err != nil { + return xerrors.Errorf("in module %s: %w", mod.Address, err) + } + } + return nil +} diff --git a/provisioner/terraform/provision.go b/provisioner/terraform/provision.go index c99ee55ad8cc6..229d6771579a5 100644 --- a/provisioner/terraform/provision.go +++ b/provisioner/terraform/provision.go @@ -12,6 +12,7 @@ import ( "strings" "time" + tfjson "github.com/hashicorp/terraform-json" "github.com/spf13/afero" "golang.org/x/xerrors" @@ -67,51 +68,34 @@ func (s *server) setupContexts(parent context.Context, canceledOrComplete <-chan return ctx, cancel, killCtx, kill } -func (s *server) Plan( - sess *provisionersdk.Session, request *proto.PlanRequest, canceledOrComplete <-chan struct{}, -) *proto.PlanComplete { +func (s *server) Init( + sess *provisionersdk.Session, request *proto.InitRequest, canceledOrComplete <-chan struct{}, +) *proto.InitComplete { ctx, span := s.startTrace(sess.Context(), tracing.FuncName()) defer span.End() ctx, cancel, killCtx, kill := s.setupContexts(ctx, canceledOrComplete) defer cancel() defer kill() - e := s.executor(sess.Files, database.ProvisionerJobTimingStagePlan) + e := s.executor(sess.Files, database.ProvisionerJobTimingStageInit) if err := e.checkMinVersion(ctx); err != nil { - return provisionersdk.PlanErrorf("%s", err.Error()) + return provisionersdk.InitErrorf("%s", err.Error()) } logTerraformEnvVars(sess) - // If we're destroying, exit early if there's no state. This is necessary to - // avoid any cases where a workspace is "locked out" of terraform due to - // e.g. bad template param values and cannot be deleted. This is just for - // contingency, in the future we will try harder to prevent workspaces being - // broken this hard. - if request.Metadata.GetWorkspaceTransition() == proto.WorkspaceTransition_DESTROY && len(sess.Config.State) == 0 { - sess.ProvisionLog(proto.LogLevel_INFO, "The terraform state does not exist, there is nothing to do") - return &proto.PlanComplete{} - } - - statefilePath := sess.Files.StateFilePath() - if len(sess.Config.State) > 0 { - err := os.WriteFile(statefilePath, sess.Config.State, 0o600) - if err != nil { - return provisionersdk.PlanErrorf("write statefile %q: %s", statefilePath, err) - } + // TODO: These logs should probably be streamed back to the provisioner runner. + err := sess.Files.ExtractArchive(ctx, s.logger, afero.NewOsFs(), request.GetTemplateSourceArchive()) + if err != nil { + return provisionersdk.InitErrorf("extract template archive: %s", err) } - err := CleanStaleTerraformPlugins(sess.Context(), s.cachePath, afero.NewOsFs(), time.Now(), s.logger) + err = CleanStaleTerraformPlugins(sess.Context(), s.cachePath, afero.NewOsFs(), time.Now(), s.logger) if err != nil { - return provisionersdk.PlanErrorf("unable to clean stale Terraform plugins: %s", err) + return provisionersdk.InitErrorf("unable to clean stale Terraform plugins: %s", err) } - s.logger.Debug(ctx, "running initialization") - - // The JSON output of `terraform init` doesn't include discrete fields for capturing timings of each plugin, - // so we capture the whole init process. - initTimings := newTimingAggregator(database.ProvisionerJobTimingStageInit) - endStage := initTimings.startStage(database.ProvisionerJobTimingStageInit) - + s.logger.Debug(ctx, "running terraform initialization") + endStage := e.timings.startStage(database.ProvisionerJobTimingStageInit) err = e.init(ctx, killCtx, sess) endStage(err) if err != nil { @@ -137,7 +121,7 @@ func (s *server) Plan( slog.F("provider_coder_stacktrace", stacktrace), ) } - return provisionersdk.PlanErrorf("initialize terraform: %s", err) + return provisionersdk.InitErrorf("initialize terraform: %s", err) } modules, err := getModules(sess.Files) @@ -147,8 +131,61 @@ func (s *server) Plan( s.logger.Error(ctx, "failed to get modules from disk", slog.Error(err)) } + var moduleFiles []byte + // Skipping modules archiving is useful if the caller does not need it, eg during + // a workspace build. This removes some added costs of sending the modules + // payload back to coderd if coderd is just going to ignore it. + if !request.OmitModuleFiles { + moduleFiles, err = GetModulesArchive(os.DirFS(e.files.WorkDirectory())) + if err != nil { + // TODO: we probably want to persist this error or make it louder eventually + e.logger.Warn(ctx, "failed to archive terraform modules", slog.Error(err)) + } + } + s.logger.Debug(ctx, "ran initialization") + return &proto.InitComplete{ + Timings: e.timings.aggregate(), + Modules: modules, + ModuleFiles: moduleFiles, + ModuleFilesHash: nil, + } +} + +func (s *server) Plan( + sess *provisionersdk.Session, request *proto.PlanRequest, canceledOrComplete <-chan struct{}, +) *proto.PlanComplete { + ctx, span := s.startTrace(sess.Context(), tracing.FuncName()) + defer span.End() + ctx, cancel, killCtx, kill := s.setupContexts(ctx, canceledOrComplete) + defer cancel() + defer kill() + + e := s.executor(sess.Files, database.ProvisionerJobTimingStagePlan) + if err := e.checkMinVersion(ctx); err != nil { + return provisionersdk.PlanErrorf("%s", err.Error()) + } + logTerraformEnvVars(sess) + + // If we're destroying, exit early if there's no state. This is necessary to + // avoid any cases where a workspace is "locked out" of terraform due to + // e.g. bad template param values and cannot be deleted. This is just for + // contingency, in the future we will try harder to prevent workspaces being + // broken this hard. + if request.Metadata.GetWorkspaceTransition() == proto.WorkspaceTransition_DESTROY && len(request.GetState()) == 0 { + sess.ProvisionLog(proto.LogLevel_INFO, "The terraform state does not exist, there is nothing to do") + return &proto.PlanComplete{} + } + + statefilePath := sess.Files.StateFilePath() + if len(request.GetState()) > 0 { + err := os.WriteFile(statefilePath, request.GetState(), 0o600) + if err != nil { + return provisionersdk.PlanErrorf("write statefile %q: %s", statefilePath, err) + } + } + env, err := provisionEnv(sess.Config, request.Metadata, request.PreviousParameterValues, request.RichParameterValues, request.ExternalAuthProviders) if err != nil { return provisionersdk.PlanErrorf("setup env: %s", err) @@ -165,13 +202,70 @@ func (s *server) Plan( return provisionersdk.PlanErrorf("%s", err.Error()) } - // Prepend init timings since they occur prior to plan timings. - // Order is irrelevant; this is merely indicative. - resp.Timings = append(initTimings.aggregate(), resp.Timings...) // mergeInitTimings(initTimings.aggregate(), resp.Timings) - resp.Modules = modules return resp } +func (s *server) Graph( + sess *provisionersdk.Session, request *proto.GraphRequest, canceledOrComplete <-chan struct{}, +) *proto.GraphComplete { + ctx, span := s.startTrace(sess.Context(), tracing.FuncName()) + defer span.End() + ctx, cancel, killCtx, kill := s.setupContexts(ctx, canceledOrComplete) + defer cancel() + defer kill() + + e := s.executor(sess.Files, database.ProvisionerJobTimingStageGraph) + if err := e.checkMinVersion(ctx); err != nil { + return provisionersdk.GraphError("%s", err.Error()) + } + logTerraformEnvVars(sess) + + modules := []*tfjson.StateModule{} + switch request.Source { + case proto.GraphSource_SOURCE_PLAN: + plan, err := e.parsePlan(ctx, killCtx, e.files.PlanFilePath()) + if err != nil { + return provisionersdk.GraphError("parse plan for graph: %s", err) + } + + modules = planModules(plan) + case proto.GraphSource_SOURCE_STATE: + tfState, err := e.state(ctx, killCtx) + if err != nil { + return provisionersdk.GraphError("load tfstate for graph: %s", err) + } + if tfState.Values != nil { + modules = []*tfjson.StateModule{tfState.Values.RootModule} + } + default: + return provisionersdk.GraphError("unknown graph source: %q", request.Source.String()) + } + + endStage := e.timings.startStage(database.ProvisionerJobTimingStageGraph) + rawGraph, err := e.graph(ctx, killCtx) + endStage(err) + if err != nil { + return provisionersdk.GraphError("generate graph: %s", err) + } + + state, err := ConvertState(ctx, modules, rawGraph, e.server.logger) + if err != nil { + return provisionersdk.GraphError("convert state for graph: %s", err) + } + + return &proto.GraphComplete{ + Error: "", + Timings: e.timings.aggregate(), + Resources: state.Resources, + Parameters: state.Parameters, + ExternalAuthProviders: state.ExternalAuthProviders, + Presets: state.Presets, + HasAiTasks: state.HasAITasks, + AiTasks: state.AITasks, + HasExternalAgents: state.HasExternalAgents, + } +} + func (s *server) Apply( sess *provisionersdk.Session, request *proto.ApplyRequest, canceledOrComplete <-chan struct{}, ) *proto.ApplyComplete { @@ -192,7 +286,7 @@ func (s *server) Apply( // e.g. bad template param values and cannot be deleted. This is just for // contingency, in the future we will try harder to prevent workspaces being // broken this hard. - if request.Metadata.GetWorkspaceTransition() == proto.WorkspaceTransition_DESTROY && len(sess.Config.State) == 0 { + if request.Metadata.GetWorkspaceTransition() == proto.WorkspaceTransition_DESTROY && len(request.GetState()) == 0 { sess.ProvisionLog(proto.LogLevel_INFO, "The terraform plan does not exist, there is nothing to do") return &proto.ApplyComplete{} } diff --git a/provisioner/terraform/provision_test.go b/provisioner/terraform/provision_test.go index 9a8a49c29b9ab..7236f0e1b60db 100644 --- a/provisioner/terraform/provision_test.go +++ b/provisioner/terraform/provision_test.go @@ -13,6 +13,7 @@ import ( "path/filepath" "sort" "strings" + "sync" "testing" "time" @@ -81,6 +82,31 @@ func setupProvisioner(t *testing.T, opts *provisionerServeOptions) (context.Cont return ctx, api } +func initDo(t *testing.T, sess proto.DRPCProvisioner_SessionClient, archive []byte, onLog ...func(log string)) *proto.InitComplete { + t.Helper() + err := sess.Send(&proto.Request{Type: &proto.Request_Init{ + Init: &proto.InitRequest{ + TemplateSourceArchive: archive, + OmitModuleFiles: false, + }, + }}) + require.NoError(t, err) + for { + msg, err := sess.Recv() + require.NoError(t, err) + if logMsg, ok := msg.Type.(*proto.Response_Log); ok { + for _, do := range onLog { + do(logMsg.Log.Output) + } + continue + } + + init := msg.GetInit() + require.NotNil(t, init) + return init + } +} + func configure(ctx context.Context, t *testing.T, client proto.DRPCProvisionerClient, config *proto.Config) proto.DRPCProvisioner_SessionClient { t.Helper() sess, err := client.Session(ctx) @@ -107,6 +133,18 @@ func readProvisionLog(t *testing.T, response proto.DRPCProvisioner_SessionClient return logBuf.String() } +func sendInit(sess proto.DRPCProvisioner_SessionClient, archive []byte) error { + return sess.Send(&proto.Request{Type: &proto.Request_Init{Init: &proto.InitRequest{ + TemplateSourceArchive: archive, + }}}) +} + +func sendGraph(sess proto.DRPCProvisioner_SessionClient, source proto.GraphSource) error { + return sess.Send(&proto.Request{Type: &proto.Request_Graph{Graph: &proto.GraphRequest{ + Source: source, + }}}) +} + func sendPlan(sess proto.DRPCProvisioner_SessionClient, transition proto.WorkspaceTransition) error { return sess.Send(&proto.Request{Type: &proto.Request_Plan{Plan: &proto.PlanRequest{ Metadata: &proto.Metadata{WorkspaceTransition: transition}, @@ -156,35 +194,51 @@ func TestProvision_Cancel(t *testing.T) { binPath := filepath.Join(dir, "terraform") // Example: exec /path/to/terrafork_fake_cancel.sh 1.2.1 apply "$@" - content := fmt.Sprintf("#!/bin/sh\nexec %q %s %s \"$@\"\n", fakeBin, terraform.TerraformVersion.String(), tt.mode) + content := fmt.Sprintf("#!/usr/bin/env sh\nexec %q %s %s \"$@\"\n", fakeBin, terraform.TerraformVersion.String(), tt.mode) err := os.WriteFile(binPath, []byte(content), 0o755) //#nosec require.NoError(t, err) t.Logf("wrote fake terraform script to %s", binPath) + logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}). + With(slog.F("source", "provisioner")). + Leveled(slog.LevelDebug) + ctx, api := setupProvisioner(t, &provisionerServeOptions{ binaryPath: binPath, + logger: &logger, }) - sess := configure(ctx, t, api, &proto.Config{ - TemplateSourceArchive: testutil.CreateTar(t, nil), - }) + sess := configure(ctx, t, api, &proto.Config{}) - err = sendPlan(sess, proto.WorkspaceTransition_START) + err = sendInit(sess, testutil.CreateTar(t, nil)) require.NoError(t, err) + var planOnce sync.Once + for _, line := range tt.startSequence { LoopStart: msg, err := sess.Recv() require.NoError(t, err) t.Log(msg.Type) + if msg.GetInit() != nil && msg.GetInit().GetError() == "" { + planOnce.Do(func() { + t.Log("Sending terraform plan request") + // Send plan after init + err = sendPlan(sess, proto.WorkspaceTransition_START) + require.NoError(t, err) + }) + goto LoopStart + } log := msg.GetLog() if log == nil { goto LoopStart } + require.Equal(t, line, log.Output) } + t.Log("Sending the cancel request") err = sess.Send(&proto.Request{ Type: &proto.Request_Cancel{ Cancel: &proto.CancelRequest{}, @@ -199,10 +253,14 @@ func TestProvision_Cancel(t *testing.T) { if log := msg.GetLog(); log != nil { gotLog = append(gotLog, log.Output) - } - if c := msg.GetPlan(); c != nil { + } else if c := msg.GetPlan(); c != nil { + require.Contains(t, c.Error, "exit status 1") + break + } else if c := msg.GetInit(); c != nil { require.Contains(t, c.Error, "exit status 1") break + } else { + t.Fatalf("unexpected message: %v", msg) } } require.Equal(t, tt.wantLog, gotLog) @@ -231,15 +289,14 @@ func TestProvision_CancelTimeout(t *testing.T) { exitTimeout: time.Second, }) - sess := configure(ctx, t, api, &proto.Config{ - TemplateSourceArchive: testutil.CreateTar(t, nil), - }) + sess := configure(ctx, t, api, &proto.Config{}) + initDo(t, sess, testutil.CreateTar(t, nil)) // provisioner requires plan before apply, so test cancel with plan. err = sendPlan(sess, proto.WorkspaceTransition_START) require.NoError(t, err) - for _, line := range []string{"init", "plan_start"} { + for _, line := range []string{"plan_start"} { LoopStart: msg, err := sess.Recv() require.NoError(t, err) @@ -316,11 +373,9 @@ func TestProvision_TextFileBusy(t *testing.T) { logger: &logger, }) - sess := configure(ctx, t, api, &proto.Config{ - TemplateSourceArchive: testutil.CreateTar(t, nil), - }) + sess := configure(ctx, t, api, &proto.Config{}) - err = sendPlan(sess, proto.WorkspaceTransition_START) + err = sendInit(sess, testutil.CreateTar(t, nil)) require.NoError(t, err) found := false @@ -328,7 +383,7 @@ func TestProvision_TextFileBusy(t *testing.T) { msg, err := sess.Recv() require.NoError(t, err) - if c := msg.GetPlan(); c != nil { + if c := msg.GetInit(); c != nil { require.Contains(t, c.Error, "exit status 1") found = true break @@ -347,11 +402,14 @@ func TestProvision(t *testing.T) { Metadata *proto.Metadata Request *proto.PlanRequest // Response may be nil to not check the response. - Response *proto.PlanComplete + Response *proto.GraphComplete + InitResponse *proto.InitComplete + InitErrorContains string + InitExpectLogContains string // If ErrorContains is not empty, PlanComplete should have an Error containing the given string - ErrorContains string - // If ExpectLogContains is not empty, then the logs should contain it. - ExpectLogContains string + PlanErrorContains string + // If PlanExpectLogContains is not empty, then the logs should contain it. + PlanExpectLogContains string // If Apply is true, then send an Apply request and check we get the same Resources as in Response. Apply bool // Some tests may need to be skipped until the relevant provider version is released. @@ -365,8 +423,8 @@ func TestProvision(t *testing.T) { "main.tf": `variable "A" { }`, }, - ErrorContains: "terraform plan:", - ExpectLogContains: "No value for required variable", + PlanErrorContains: "terraform plan:", + PlanExpectLogContains: "No value for required variable", }, { Name: "missing-variable-dry-run", @@ -374,15 +432,15 @@ func TestProvision(t *testing.T) { "main.tf": `variable "A" { }`, }, - ErrorContains: "terraform plan:", - ExpectLogContains: "No value for required variable", + PlanErrorContains: "terraform plan:", + PlanExpectLogContains: "No value for required variable", }, { Name: "single-resource-dry-run", Files: map[string]string{ "main.tf": `resource "null_resource" "A" {}`, }, - Response: &proto.PlanComplete{ + Response: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "A", Type: "null_resource", @@ -394,7 +452,7 @@ func TestProvision(t *testing.T) { Files: map[string]string{ "main.tf": `resource "null_resource" "A" {}`, }, - Response: &proto.PlanComplete{ + Response: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "A", Type: "null_resource", @@ -415,7 +473,7 @@ func TestProvision(t *testing.T) { } }`, }, - Response: &proto.PlanComplete{ + Response: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "A", Type: "null_resource", @@ -428,18 +486,18 @@ func TestProvision(t *testing.T) { Files: map[string]string{ "main.tf": `a`, }, - ErrorContains: "initialize terraform", - ExpectLogContains: "Argument or block definition required", - SkipCacheProviders: true, + InitErrorContains: "initialize terraform", + InitExpectLogContains: "Argument or block definition required", + SkipCacheProviders: true, }, { Name: "bad-syntax-2", Files: map[string]string{ "main.tf": `;asdf;`, }, - ErrorContains: "initialize terraform", - ExpectLogContains: `The ";" character is not valid.`, - SkipCacheProviders: true, + InitErrorContains: "initialize terraform", + InitExpectLogContains: `The ";" character is not valid.`, + SkipCacheProviders: true, }, { Name: "destroy-no-state", @@ -449,7 +507,7 @@ func TestProvision(t *testing.T) { Metadata: &proto.Metadata{ WorkspaceTransition: proto.WorkspaceTransition_DESTROY, }, - ExpectLogContains: "nothing to do", + PlanExpectLogContains: "nothing to do", }, { Name: "rich-parameter-with-value", @@ -493,7 +551,7 @@ func TestProvision(t *testing.T) { }, }, }, - Response: &proto.PlanComplete{ + Response: &proto.GraphComplete{ Parameters: []*proto.RichParameter{ { Name: "Example", @@ -571,7 +629,7 @@ func TestProvision(t *testing.T) { }, }, }, - Response: &proto.PlanComplete{ + Response: &proto.GraphComplete{ Parameters: []*proto.RichParameter{ { Name: "Example", @@ -623,7 +681,7 @@ func TestProvision(t *testing.T) { AccessToken: "some-value", }}, }, - Response: &proto.PlanComplete{ + Response: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "null_resource", @@ -666,7 +724,7 @@ func TestProvision(t *testing.T) { WorkspaceOwnerSshPrivateKey: "fake private key", }, }, - Response: &proto.PlanComplete{ + Response: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "null_resource", @@ -709,7 +767,7 @@ func TestProvision(t *testing.T) { WorkspaceOwnerLoginType: "github", }, }, - Response: &proto.PlanComplete{ + Response: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "null_resource", @@ -738,16 +796,7 @@ func TestProvision(t *testing.T) { `, }, Request: &proto.PlanRequest{}, - Response: &proto.PlanComplete{ - Resources: []*proto.Resource{{ - Name: "example", - Type: "null_resource", - ModulePath: "module.hello", - }, { - Name: "inner_example", - Type: "null_resource", - ModulePath: "module.hello.module.there", - }}, + InitResponse: &proto.InitComplete{ Modules: []*proto.Module{{ Key: "hello", Version: "", @@ -758,6 +807,17 @@ func TestProvision(t *testing.T) { Source: "./inner_module", }}, }, + Response: &proto.GraphComplete{ + Resources: []*proto.Resource{{ + Name: "example", + Type: "null_resource", + ModulePath: "module.hello", + }, { + Name: "inner_example", + Type: "null_resource", + ModulePath: "module.hello.module.there", + }}, + }, }, { Name: "workspace-owner-rbac-roles", @@ -792,7 +852,7 @@ func TestProvision(t *testing.T) { WorkspaceOwnerRbacRoles: []*proto.Role{{Name: "member", OrgId: ""}}, }, }, - Response: &proto.PlanComplete{ + Response: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "null_resource", @@ -833,7 +893,7 @@ func TestProvision(t *testing.T) { PrebuiltWorkspaceBuildStage: proto.PrebuiltWorkspaceBuildStage_CREATE, }, }, - Response: &proto.PlanComplete{ + Response: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "null_resource", @@ -871,7 +931,7 @@ func TestProvision(t *testing.T) { PrebuiltWorkspaceBuildStage: proto.PrebuiltWorkspaceBuildStage_CLAIM, }, }, - Response: &proto.PlanComplete{ + Response: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "null_resource", @@ -910,7 +970,7 @@ func TestProvision(t *testing.T) { `, provider.TaskPromptParameterName), }, Request: &proto.PlanRequest{}, - Response: &proto.PlanComplete{ + Response: &proto.GraphComplete{ Resources: []*proto.Resource{ { Name: "a", @@ -962,7 +1022,7 @@ func TestProvision(t *testing.T) { } `, }, - Response: &proto.PlanComplete{ + Response: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "coder_external_agent", @@ -987,7 +1047,7 @@ func TestProvision(t *testing.T) { } `, }, - Response: &proto.PlanComplete{ + Response: &proto.GraphComplete{ Resources: []*proto.Resource{ { Name: "my-task", @@ -1043,9 +1103,18 @@ func TestProvision(t *testing.T) { ctx, api := setupProvisioner(t, &provisionerServeOptions{ cliConfigPath: cliConfigPath, }) - sess := configure(ctx, t, api, &proto.Config{ - TemplateSourceArchive: testutil.CreateTar(t, testCase.Files), + sess := configure(ctx, t, api, &proto.Config{}) + initLogGot := testCase.InitExpectLogContains == "" + initComplete := initDo(t, sess, testutil.CreateTar(t, testCase.Files), func(log string) { + if strings.Contains(log, testCase.InitExpectLogContains) { + initLogGot = true + } }) + require.Truef(t, initLogGot, "did not get expected init log substring %q", testCase.InitExpectLogContains) + if testCase.InitErrorContains != "" { + require.Contains(t, initComplete.Error, testCase.InitErrorContains) + return + } planRequest := &proto.Request{Type: &proto.Request_Plan{Plan: &proto.PlanRequest{ Metadata: testCase.Metadata, @@ -1054,7 +1123,7 @@ func TestProvision(t *testing.T) { planRequest = &proto.Request{Type: &proto.Request_Plan{Plan: testCase.Request}} } - gotExpectedLog := testCase.ExpectLogContains == "" + gotExpectedLog := testCase.PlanExpectLogContains == "" provision := func(req *proto.Request) *proto.Response { err := sess.Send(req) @@ -1063,7 +1132,7 @@ func TestProvision(t *testing.T) { msg, err := sess.Recv() require.NoError(t, err) if msg.GetLog() != nil { - if testCase.ExpectLogContains != "" && strings.Contains(msg.GetLog().Output, testCase.ExpectLogContains) { + if testCase.PlanExpectLogContains != "" && strings.Contains(msg.GetLog().Output, testCase.PlanExpectLogContains) { gotExpectedLog = true } @@ -1078,35 +1147,43 @@ func TestProvision(t *testing.T) { planComplete := resp.GetPlan() require.NotNil(t, planComplete) - if testCase.ErrorContains != "" { - require.Contains(t, planComplete.GetError(), testCase.ErrorContains) + if testCase.PlanErrorContains != "" { + require.Contains(t, planComplete.GetError(), testCase.PlanErrorContains) } + graphCompleteResp := provision(&proto.Request{Type: &proto.Request_Graph{Graph: &proto.GraphRequest{ + Source: proto.GraphSource_SOURCE_PLAN, + }}}) + graphComplete := graphCompleteResp.GetGraph() + require.NotNil(t, graphCompleteResp) + if testCase.Response != nil { - require.Equal(t, testCase.Response.Error, planComplete.Error) + require.Equal(t, testCase.Response.Error, graphComplete.Error) // Remove randomly generated data and sort by name. - normalizeResources(planComplete.Resources) - resourcesGot, err := json.Marshal(planComplete.Resources) + normalizeResources(graphComplete.Resources) + resourcesGot, err := json.Marshal(graphComplete.Resources) require.NoError(t, err) resourcesWant, err := json.Marshal(testCase.Response.Resources) require.NoError(t, err) require.Equal(t, string(resourcesWant), string(resourcesGot)) - parametersGot, err := json.Marshal(planComplete.Parameters) + parametersGot, err := json.Marshal(graphComplete.Parameters) require.NoError(t, err) parametersWant, err := json.Marshal(testCase.Response.Parameters) require.NoError(t, err) require.Equal(t, string(parametersWant), string(parametersGot)) - modulesGot, err := json.Marshal(planComplete.Modules) + modulesGot, err := json.Marshal(initComplete.Modules) require.NoError(t, err) - modulesWant, err := json.Marshal(testCase.Response.Modules) - require.NoError(t, err) - require.Equal(t, string(modulesWant), string(modulesGot)) + if testCase.InitResponse != nil { + modulesWant, err := json.Marshal(testCase.InitResponse.Modules) + require.NoError(t, err) + require.Equal(t, string(modulesWant), string(modulesGot)) + } - require.Equal(t, planComplete.HasAiTasks, testCase.Response.HasAiTasks) - require.Equal(t, planComplete.HasExternalAgents, testCase.Response.HasExternalAgents) + require.Equal(t, graphComplete.HasAiTasks, testCase.Response.HasAiTasks) + require.Equal(t, graphComplete.HasExternalAgents, testCase.Response.HasExternalAgents) } if testCase.Apply { @@ -1117,8 +1194,8 @@ func TestProvision(t *testing.T) { require.NotNil(t, applyComplete) if testCase.Response != nil { - normalizeResources(applyComplete.Resources) - resourcesGot, err := json.Marshal(applyComplete.Resources) + normalizeResources(graphComplete.Resources) + resourcesGot, err := json.Marshal(graphComplete.Resources) require.NoError(t, err) resourcesWant, err := json.Marshal(testCase.Response.Resources) require.NoError(t, err) @@ -1127,7 +1204,7 @@ func TestProvision(t *testing.T) { } if !gotExpectedLog { - t.Fatalf("expected log string %q but never saw it", testCase.ExpectLogContains) + t.Fatalf("expected log string %q but never saw it", testCase.PlanExpectLogContains) } }) } @@ -1160,9 +1237,8 @@ func TestProvision_ExtraEnv(t *testing.T) { t.Setenv("TF_SUPERSECRET", secretValue) ctx, api := setupProvisioner(t, nil) - sess := configure(ctx, t, api, &proto.Config{ - TemplateSourceArchive: testutil.CreateTar(t, map[string]string{"main.tf": `resource "null_resource" "A" {}`}), - }) + sess := configure(ctx, t, api, &proto.Config{}) + _ = initDo(t, sess, testutil.CreateTar(t, map[string]string{"main.tf": `resource "null_resource" "A" {}`})) err := sendPlan(sess, proto.WorkspaceTransition_START) require.NoError(t, err) @@ -1210,9 +1286,8 @@ func TestProvision_SafeEnv(t *testing.T) { ` ctx, api := setupProvisioner(t, nil) - sess := configure(ctx, t, api, &proto.Config{ - TemplateSourceArchive: testutil.CreateTar(t, map[string]string{"main.tf": echoResource}), - }) + sess := configure(ctx, t, api, &proto.Config{}) + _ = initDo(t, sess, testutil.CreateTar(t, map[string]string{"main.tf": echoResource})) err := sendPlan(sess, proto.WorkspaceTransition_START) require.NoError(t, err) @@ -1232,15 +1307,13 @@ func TestProvision_MalformedModules(t *testing.T) { t.Parallel() ctx, api := setupProvisioner(t, nil) - sess := configure(ctx, t, api, &proto.Config{ - TemplateSourceArchive: testutil.CreateTar(t, map[string]string{ - "main.tf": `module "hello" { source = "./module" }`, - "module/module.tf": `resource "null_`, - }), - }) - - err := sendPlan(sess, proto.WorkspaceTransition_START) + sess := configure(ctx, t, api, &proto.Config{}) + err := sendInit(sess, testutil.CreateTar(t, map[string]string{ + "main.tf": `module "hello" { source = "./module" }`, + "module/module.tf": `resource "null_`, + })) require.NoError(t, err) + log := readProvisionLog(t, sess) require.Contains(t, log, "Invalid block definition") } diff --git a/provisioner/terraform/testdata/fake_cancel.sh b/provisioner/terraform/testdata/fake_cancel.sh index 2ea713379cce9..574d25a71d88d 100755 --- a/provisioner/terraform/testdata/fake_cancel.sh +++ b/provisioner/terraform/testdata/fake_cancel.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env sh VERSION=$1 MODE=$2 diff --git a/provisioner/terraform/testdata/fake_cancel_hang.sh b/provisioner/terraform/testdata/fake_cancel_hang.sh index e8db67f6837cd..d1c6d4955ee1a 100755 --- a/provisioner/terraform/testdata/fake_cancel_hang.sh +++ b/provisioner/terraform/testdata/fake_cancel_hang.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env sh VERSION=$1 shift 1 diff --git a/provisioner/terraform/testdata/fake_text_file_busy.sh b/provisioner/terraform/testdata/fake_text_file_busy.sh index 6c9cf98f46bbe..7bf9d630540f8 100755 --- a/provisioner/terraform/testdata/fake_text_file_busy.sh +++ b/provisioner/terraform/testdata/fake_text_file_busy.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env sh VERSION=$1 shift 1 diff --git a/provisioner/terraform/timings.go b/provisioner/terraform/timings.go index 0b150d2eafd4d..05b4e0cf8bbc7 100644 --- a/provisioner/terraform/timings.go +++ b/provisioner/terraform/timings.go @@ -281,12 +281,3 @@ func (e *timingSpan) toProto() *proto.Timing { State: e.state, } } - -func createGraphTimingsEvent(event timingKind) (time.Time, *timingSpan) { - return dbtime.Now(), &timingSpan{ - kind: event, - action: "building terraform dependency graph", - provider: "terraform", - resource: "state file", - } -} diff --git a/provisioner/terraform/timings_test.go b/provisioner/terraform/timings_test.go index 7a9ac84220a51..f69d383dd6cc0 100644 --- a/provisioner/terraform/timings_test.go +++ b/provisioner/terraform/timings_test.go @@ -4,6 +4,7 @@ package terraform_test import ( "context" + "encoding/json" "os" "path/filepath" "strings" @@ -35,65 +36,66 @@ func TestTimingsFromProvision(t *testing.T) { ctx, api := setupProvisioner(t, &provisionerServeOptions{ binaryPath: fakeBin, }) - sess := configure(ctx, t, api, &proto.Config{ - TemplateSourceArchive: testutil.CreateTar(t, nil), - }) + sess := configure(ctx, t, api, &proto.Config{}) ctx, cancel := context.WithTimeout(ctx, testutil.WaitLong) t.Cleanup(cancel) - // When: a plan is executed in the provisioner, our fake terraform will be executed and will produce a - // state file and some log content. - err = sendPlan(sess, proto.WorkspaceTransition_START) - require.NoError(t, err) - var timings []*proto.Timing - for { - select { - case <-ctx.Done(): - t.Fatal(ctx.Err()) - default: - } - - msg, err := sess.Recv() - require.NoError(t, err) - - if log := msg.GetLog(); log != nil { - t.Logf("%s: %s: %s", "plan", log.Level.String(), log.Output) - } - if c := msg.GetPlan(); c != nil { - require.Empty(t, c.Error) - // Capture the timing information returned by the plan process. - timings = append(timings, c.GetTimings()...) + handleResponse := func(t *testing.T, stage string) { + t.Helper() + for { + select { + case <-ctx.Done(): + t.Fatal(ctx.Err()) + default: + } + + msg, err := sess.Recv() + require.NoError(t, err) + + if log := msg.GetLog(); log != nil { + t.Logf("%s: %s: %s", stage, log.Level.String(), log.Output) + continue + } + switch { + case msg.GetInit() != nil: + timings = append(timings, msg.GetInit().GetTimings()...) + case msg.GetPlan() != nil: + timings = append(timings, msg.GetPlan().GetTimings()...) + case msg.GetApply() != nil: + timings = append(timings, msg.GetApply().GetTimings()...) + case msg.GetGraph() != nil: + timings = append(timings, msg.GetGraph().GetTimings()...) + } break } } + // When: configured, our fake terraform will fake an init setup + err = sendInit(sess, testutil.CreateTar(t, nil)) + require.NoError(t, err) + handleResponse(t, "init") + + // When: a plan is executed in the provisioner, our fake terraform will be executed and will produce a + // state file and some log content. + err = sendPlan(sess, proto.WorkspaceTransition_START) + require.NoError(t, err) + + handleResponse(t, "plan") + // When: the plan has completed, let's trigger an apply. err = sendApply(sess, proto.WorkspaceTransition_START) require.NoError(t, err) - for { - select { - case <-ctx.Done(): - t.Fatal(ctx.Err()) - default: - } + handleResponse(t, "apply") - msg, err := sess.Recv() - require.NoError(t, err) + // When: the apply has completed, graph the results + err = sendGraph(sess, proto.GraphSource_SOURCE_STATE) + require.NoError(t, err) - if log := msg.GetLog(); log != nil { - t.Logf("%s: %s: %s", "apply", log.Level.String(), log.Output) - } - if c := msg.GetApply(); c != nil { - require.Empty(t, c.Error) - // Capture the timing information returned by the apply process. - timings = append(timings, c.GetTimings()...) - break - } - } + handleResponse(t, "graph") // Sort the timings stably to keep reduce flakiness. terraform_internal.StableSortTimings(t, timings) @@ -116,10 +118,19 @@ func TestTimingsFromProvision(t *testing.T) { {"start":"2024-08-15T08:26:39.626722Z", "end":"2024-08-15T08:26:39.669954Z", "action":"create", "source":"docker", "resource":"docker_image.main", "stage":"apply", "state":"COMPLETED"} {"start":"2024-08-15T08:26:39.627335Z", "end":"2024-08-15T08:26:39.660616Z", "action":"create", "source":"docker", "resource":"docker_volume.home_volume", "stage":"apply", "state":"COMPLETED"} {"start":"2024-08-15T08:26:39.682223Z", "end":"2024-08-15T08:26:40.186482Z", "action":"create", "source":"docker", "resource":"docker_container.workspace[0]", "stage":"apply", "state":"COMPLETED"}`)) - graphTimings := terraform_internal.ParseTimingLines(t, []byte(`{"start":"2000-01-01T01:01:01.123456Z", "end":"2000-01-01T01:01:01.123456Z", "action":"building terraform dependency graph", "source":"terraform", "resource":"state file", "stage":"graph", "state":"COMPLETED"}`)) - graphTiming := graphTimings[0] + // Graphing is omitted as it is captured by the stage timing, which uses now() - require.Len(t, timings, len(initTimings)+len(planTimings)+len(applyTimings)+len(graphTimings)) + totals := make(map[string]int) + for _, ti := range timings { + totals[ti.Stage]++ + data, _ := json.Marshal(ti) // for debugging + t.Logf("Timings log (%s) :: %s", ti.Stage, string(data)) + } + require.Equal(t, len(initTimings), totals["init"], "init") + require.Equal(t, len(planTimings), totals["plan"], "plan") + require.Equal(t, len(applyTimings), totals["apply"], "apply") + // Lastly total + require.Len(t, timings, len(initTimings)+len(planTimings)+len(applyTimings)) // init/graph timings are computed dynamically during provisioning whereas plan/apply come from the logs (fixtures) in // provisioner/terraform/testdata/timings-aggregation/fake-terraform.sh. @@ -134,9 +145,6 @@ func TestTimingsFromProvision(t *testing.T) { case string(database.ProvisionerJobTimingStageInit): require.True(t, terraform_internal.TimingsAreEqual(t, []*proto.Timing{initTimings[iCursor]}, []*proto.Timing{tim})) iCursor++ - case string(database.ProvisionerJobTimingStageGraph): - tim.Start, tim.End = graphTiming.Start, graphTiming.End - require.True(t, terraform_internal.TimingsAreEqual(t, []*proto.Timing{graphTiming}, []*proto.Timing{tim})) case string(database.ProvisionerJobTimingStagePlan): require.True(t, terraform_internal.TimingsAreEqual(t, []*proto.Timing{planTimings[pCursor]}, []*proto.Timing{tim})) pCursor++ diff --git a/provisionerd/provisionerd_test.go b/provisionerd/provisionerd_test.go index fc4d069a88597..e3204c01c81c2 100644 --- a/provisionerd/provisionerd_test.go +++ b/provisionerd/provisionerd_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/hashicorp/yamux" + "github.com/spf13/afero" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/atomic" @@ -131,6 +132,16 @@ func TestProvisionerd(t *testing.T) { } return c }, + init: func(s *provisionersdk.Session, r *sdkproto.InitRequest, canceledOrComplete <-chan struct{}) *sdkproto.InitComplete { + closerMutex.Lock() + defer closerMutex.Unlock() + err := closer.Close() + c := &sdkproto.InitComplete{} + if err != nil { + c.Error = err.Error() + } + return c + }, }), }) closerMutex.Unlock() @@ -173,7 +184,17 @@ func TestProvisionerd(t *testing.T) { }, }), nil }, provisionerd.LocalProvisioners{ - "someprovisioner": createProvisionerClient(t, done, provisionerTestServer{}), + "someprovisioner": createProvisionerClient(t, done, provisionerTestServer{ + init: func(s *provisionersdk.Session, r *sdkproto.InitRequest, canceledOrComplete <-chan struct{}) *sdkproto.InitComplete { + // TODO: This comes from ExtractArchive which is moved to the `init` step. + if !filepath.IsLocal("../../../etc/passwd") { + return &sdkproto.InitComplete{ + Error: "refusing to extract to non-local path", + } + } + return &sdkproto.InitComplete{} + }, + }), }) require.Condition(t, closedWithin(completeChan, testutil.WaitMedium)) require.NoError(t, closer.Close()) @@ -227,14 +248,16 @@ func TestProvisionerd(t *testing.T) { Readme: make([]byte, largeSize), } }, + init: func(s *provisionersdk.Session, r *sdkproto.InitRequest, canceledOrComplete <-chan struct{}) *sdkproto.InitComplete { + return &sdkproto.InitComplete{} + }, plan: func( _ *provisionersdk.Session, _ *sdkproto.PlanRequest, _ <-chan struct{}, ) *sdkproto.PlanComplete { return &sdkproto.PlanComplete{ - Resources: []*sdkproto.Resource{}, - Plan: make([]byte, largeSize), + Plan: make([]byte, largeSize), } }, apply: func( @@ -246,6 +269,11 @@ func TestProvisionerd(t *testing.T) { State: make([]byte, largeSize), } }, + graph: func(s *provisionersdk.Session, r *sdkproto.GraphRequest, canceledOrComplete <-chan struct{}) *sdkproto.GraphComplete { + return &sdkproto.GraphComplete{ + Resources: []*sdkproto.Resource{}, + } + }, }), }) require.Condition(t, closedWithin(completeChan, testutil.WaitShort)) @@ -299,6 +327,9 @@ func TestProvisionerd(t *testing.T) { <-cancelOrComplete return &sdkproto.ParseComplete{} }, + init: func(s *provisionersdk.Session, r *sdkproto.InitRequest, canceledOrComplete <-chan struct{}) *sdkproto.InitComplete { + return &sdkproto.InitComplete{} + }, }), }) require.Condition(t, closedWithin(completeChan, testutil.WaitShort)) @@ -349,6 +380,7 @@ func TestProvisionerd(t *testing.T) { }), nil }, provisionerd.LocalProvisioners{ "someprovisioner": createProvisionerClient(t, done, provisionerTestServer{ + init: extractInit(t), parse: func( s *provisionersdk.Session, _ *sdkproto.ParseRequest, @@ -366,9 +398,7 @@ func TestProvisionerd(t *testing.T) { cancelOrComplete <-chan struct{}, ) *sdkproto.PlanComplete { s.ProvisionLog(sdkproto.LogLevel_INFO, "hello") - return &sdkproto.PlanComplete{ - Resources: []*sdkproto.Resource{}, - } + return &sdkproto.PlanComplete{} }, apply: func( _ *provisionersdk.Session, @@ -378,6 +408,11 @@ func TestProvisionerd(t *testing.T) { t.Error("dry run should not apply") return &sdkproto.ApplyComplete{} }, + graph: func(s *provisionersdk.Session, r *sdkproto.GraphRequest, canceledOrComplete <-chan struct{}) *sdkproto.GraphComplete { + return &sdkproto.GraphComplete{ + Resources: []*sdkproto.Resource{}, + } + }, }), }) @@ -433,14 +468,15 @@ func TestProvisionerd(t *testing.T) { }), nil }, provisionerd.LocalProvisioners{ "someprovisioner": createProvisionerClient(t, done, provisionerTestServer{ + init: func(s *provisionersdk.Session, r *sdkproto.InitRequest, canceledOrComplete <-chan struct{}) *sdkproto.InitComplete { + return &sdkproto.InitComplete{} + }, plan: func( _ *provisionersdk.Session, _ *sdkproto.PlanRequest, _ <-chan struct{}, ) *sdkproto.PlanComplete { - return &sdkproto.PlanComplete{ - Resources: []*sdkproto.Resource{}, - } + return &sdkproto.PlanComplete{} }, apply: func( _ *provisionersdk.Session, @@ -450,6 +486,11 @@ func TestProvisionerd(t *testing.T) { t.Error("dry run should not apply") return &sdkproto.ApplyComplete{} }, + graph: func(s *provisionersdk.Session, r *sdkproto.GraphRequest, canceledOrComplete <-chan struct{}) *sdkproto.GraphComplete { + return &sdkproto.GraphComplete{ + Resources: []*sdkproto.Resource{}, + } + }, }), }) @@ -498,6 +539,9 @@ func TestProvisionerd(t *testing.T) { }), nil }, provisionerd.LocalProvisioners{ "someprovisioner": createProvisionerClient(t, done, provisionerTestServer{ + init: func(s *provisionersdk.Session, r *sdkproto.InitRequest, canceledOrComplete <-chan struct{}) *sdkproto.InitComplete { + return &sdkproto.InitComplete{} + }, plan: func( s *provisionersdk.Session, _ *sdkproto.PlanRequest, @@ -513,6 +557,9 @@ func TestProvisionerd(t *testing.T) { ) *sdkproto.ApplyComplete { return &sdkproto.ApplyComplete{} }, + graph: func(s *provisionersdk.Session, r *sdkproto.GraphRequest, canceledOrComplete <-chan struct{}) *sdkproto.GraphComplete { + return &sdkproto.GraphComplete{} + }, }), }) require.Condition(t, closedWithin(acq.complete, testutil.WaitShort)) @@ -570,6 +617,9 @@ func TestProvisionerd(t *testing.T) { }), nil }, provisionerd.LocalProvisioners{ "someprovisioner": createProvisionerClient(t, done, provisionerTestServer{ + init: func(s *provisionersdk.Session, r *sdkproto.InitRequest, canceledOrComplete <-chan struct{}) *sdkproto.InitComplete { + return &sdkproto.InitComplete{} + }, plan: func( s *provisionersdk.Session, _ *sdkproto.PlanRequest, @@ -577,14 +627,7 @@ func TestProvisionerd(t *testing.T) { ) *sdkproto.PlanComplete { s.ProvisionLog(sdkproto.LogLevel_DEBUG, "wow") return &sdkproto.PlanComplete{ - Resources: []*sdkproto.Resource{ - { - DailyCost: 10, - }, - { - DailyCost: 15, - }, - }, + DailyCost: 25, } }, apply: func( @@ -593,7 +636,10 @@ func TestProvisionerd(t *testing.T) { _ <-chan struct{}, ) *sdkproto.ApplyComplete { t.Error("should not apply when resources exceed quota") - return &sdkproto.ApplyComplete{ + return &sdkproto.ApplyComplete{} + }, + graph: func(s *provisionersdk.Session, r *sdkproto.GraphRequest, canceledOrComplete <-chan struct{}) *sdkproto.GraphComplete { + return &sdkproto.GraphComplete{ Resources: []*sdkproto.Resource{ { DailyCost: 10, @@ -646,6 +692,12 @@ func TestProvisionerd(t *testing.T) { }), nil }, provisionerd.LocalProvisioners{ "someprovisioner": createProvisionerClient(t, done, provisionerTestServer{ + init: func(s *provisionersdk.Session, r *sdkproto.InitRequest, canceledOrComplete <-chan struct{}) *sdkproto.InitComplete { + return &sdkproto.InitComplete{} + }, + graph: func(s *provisionersdk.Session, r *sdkproto.GraphRequest, canceledOrComplete <-chan struct{}) *sdkproto.GraphComplete { + return &sdkproto.GraphComplete{} + }, plan: func( s *provisionersdk.Session, _ *sdkproto.PlanRequest, @@ -756,6 +808,9 @@ func TestProvisionerd(t *testing.T) { }), nil }, provisionerd.LocalProvisioners{ "someprovisioner": createProvisionerClient(t, done, provisionerTestServer{ + init: func(s *provisionersdk.Session, r *sdkproto.InitRequest, canceledOrComplete <-chan struct{}) *sdkproto.InitComplete { + return &sdkproto.InitComplete{} + }, plan: func( s *provisionersdk.Session, _ *sdkproto.PlanRequest, @@ -844,6 +899,9 @@ func TestProvisionerd(t *testing.T) { }), nil }, provisionerd.LocalProvisioners{ "someprovisioner": createProvisionerClient(t, done, provisionerTestServer{ + init: func(s *provisionersdk.Session, r *sdkproto.InitRequest, canceledOrComplete <-chan struct{}) *sdkproto.InitComplete { + return &sdkproto.InitComplete{} + }, plan: func( s *provisionersdk.Session, _ *sdkproto.PlanRequest, @@ -938,6 +996,9 @@ func TestProvisionerd(t *testing.T) { return client, nil }, provisionerd.LocalProvisioners{ "someprovisioner": createProvisionerClient(t, done, provisionerTestServer{ + init: func(s *provisionersdk.Session, r *sdkproto.InitRequest, canceledOrComplete <-chan struct{}) *sdkproto.InitComplete { + return &sdkproto.InitComplete{} + }, plan: func( _ *provisionersdk.Session, _ *sdkproto.PlanRequest, @@ -1031,6 +1092,9 @@ func TestProvisionerd(t *testing.T) { return client, nil }, provisionerd.LocalProvisioners{ "someprovisioner": createProvisionerClient(t, done, provisionerTestServer{ + init: func(s *provisionersdk.Session, r *sdkproto.InitRequest, canceledOrComplete <-chan struct{}) *sdkproto.InitComplete { + return &sdkproto.InitComplete{} + }, plan: func( _ *provisionersdk.Session, _ *sdkproto.PlanRequest, @@ -1045,6 +1109,9 @@ func TestProvisionerd(t *testing.T) { ) *sdkproto.ApplyComplete { return &sdkproto.ApplyComplete{} }, + graph: func(s *provisionersdk.Session, r *sdkproto.GraphRequest, canceledOrComplete <-chan struct{}) *sdkproto.GraphComplete { + return &sdkproto.GraphComplete{} + }, }), }) require.Condition(t, closedWithin(completeChan, testutil.WaitShort)) @@ -1125,6 +1192,12 @@ func TestProvisionerd(t *testing.T) { }), nil }, provisionerd.LocalProvisioners{ "someprovisioner": createProvisionerClient(t, done, provisionerTestServer{ + init: func(s *provisionersdk.Session, r *sdkproto.InitRequest, canceledOrComplete <-chan struct{}) *sdkproto.InitComplete { + return &sdkproto.InitComplete{} + }, + graph: func(s *provisionersdk.Session, r *sdkproto.GraphRequest, canceledOrComplete <-chan struct{}) *sdkproto.GraphComplete { + return &sdkproto.GraphComplete{} + }, plan: func( s *provisionersdk.Session, _ *sdkproto.PlanRequest, @@ -1253,9 +1326,15 @@ func createProvisionerClient(t *testing.T, done <-chan struct{}, server provisio } type provisionerTestServer struct { + init func(s *provisionersdk.Session, r *sdkproto.InitRequest, canceledOrComplete <-chan struct{}) *sdkproto.InitComplete parse func(s *provisionersdk.Session, r *sdkproto.ParseRequest, canceledOrComplete <-chan struct{}) *sdkproto.ParseComplete plan func(s *provisionersdk.Session, r *sdkproto.PlanRequest, canceledOrComplete <-chan struct{}) *sdkproto.PlanComplete apply func(s *provisionersdk.Session, r *sdkproto.ApplyRequest, canceledOrComplete <-chan struct{}) *sdkproto.ApplyComplete + graph func(s *provisionersdk.Session, r *sdkproto.GraphRequest, canceledOrComplete <-chan struct{}) *sdkproto.GraphComplete +} + +func (p *provisionerTestServer) Init(s *provisionersdk.Session, r *sdkproto.InitRequest, canceledOrComplete <-chan struct{}) *sdkproto.InitComplete { + return p.init(s, r, canceledOrComplete) } func (p *provisionerTestServer) Parse(s *provisionersdk.Session, r *sdkproto.ParseRequest, canceledOrComplete <-chan struct{}) *sdkproto.ParseComplete { @@ -1270,6 +1349,10 @@ func (p *provisionerTestServer) Apply(s *provisionersdk.Session, r *sdkproto.App return p.apply(s, r, canceledOrComplete) } +func (p *provisionerTestServer) Graph(s *provisionersdk.Session, r *sdkproto.GraphRequest, canceledOrComplete <-chan struct{}) *sdkproto.GraphComplete { + return p.graph(s, r, canceledOrComplete) +} + func (p *provisionerDaemonTestServer) UploadFile(stream proto.DRPCProvisionerDaemon_UploadFileStream) error { return p.uploadFile(stream) } @@ -1359,3 +1442,16 @@ func (a *acquireOne) acquireWithCancel(stream proto.DRPCProvisionerDaemon_Acquir } return nil } + +func extractInit(t *testing.T) func(s *provisionersdk.Session, r *sdkproto.InitRequest, canceledOrComplete <-chan struct{}) *sdkproto.InitComplete { + logger := slogtest.Make(t, nil) + return func(s *provisionersdk.Session, r *sdkproto.InitRequest, canceledOrComplete <-chan struct{}) *sdkproto.InitComplete { + err := s.Files.ExtractArchive(s.Context(), logger, afero.NewOsFs(), r.TemplateSourceArchive) + if err != nil { + return &sdkproto.InitComplete{ + Error: fmt.Sprintf("failed to extract template source archive: %v", err), + } + } + return &sdkproto.InitComplete{} + } +} diff --git a/provisionerd/runner/graph.go b/provisionerd/runner/graph.go new file mode 100644 index 0000000000000..53461f35f72c7 --- /dev/null +++ b/provisionerd/runner/graph.go @@ -0,0 +1,64 @@ +package runner + +import ( + "context" + "time" + + "cdr.dev/slog" + "github.com/coder/coder/v2/coderd/tracing" + "github.com/coder/coder/v2/provisionerd/proto" + sdkproto "github.com/coder/coder/v2/provisionersdk/proto" +) + +func (r *Runner) graph(ctx context.Context, req *sdkproto.GraphRequest) (*sdkproto.GraphComplete, *proto.FailedJob) { + ctx, span := r.startTrace(ctx, tracing.FuncName()) + defer span.End() + + err := r.session.Send(&sdkproto.Request{Type: &sdkproto.Request_Graph{Graph: req}}) + if err != nil { + return nil, r.failedJobf("send graph request: %v", err) + } + + nevermind := make(chan struct{}) + defer close(nevermind) + go func() { + select { + case <-nevermind: + return + case <-r.notStopped.Done(): + return + case <-r.notCanceled.Done(): + _ = r.session.Send(&sdkproto.Request{ + Type: &sdkproto.Request_Cancel{ + Cancel: &sdkproto.CancelRequest{}, + }, + }) + } + }() + + for { + msg, err := r.session.Recv() + if err != nil { + return nil, r.failedJobf("receive graph response: %v", err) + } + switch msgType := msg.Type.(type) { + case *sdkproto.Response_Log: + r.logProvisionerJobLog(context.Background(), msgType.Log.Level, "terraform graphing", + slog.F("level", msgType.Log.Level), + slog.F("output", msgType.Log.Output), + ) + + r.queueLog(ctx, &proto.Log{ + Source: proto.LogSource_PROVISIONER, + Level: msgType.Log.Level, + CreatedAt: time.Now().UnixMilli(), + Output: msgType.Log.Output, + Stage: "Graphing Infrastructure", + }) + case *sdkproto.Response_Graph: + return msgType.Graph, nil + default: + return nil, r.failedJobf("unexpected graph response type %T", msg.Type) + } + } +} diff --git a/provisionerd/runner/init.go b/provisionerd/runner/init.go new file mode 100644 index 0000000000000..975d37708e23e --- /dev/null +++ b/provisionerd/runner/init.go @@ -0,0 +1,113 @@ +package runner + +import ( + "bytes" + "context" + "time" + + "cdr.dev/slog" + "github.com/coder/coder/v2/coderd/tracing" + "github.com/coder/coder/v2/provisionerd/proto" + sdkproto "github.com/coder/coder/v2/provisionersdk/proto" +) + +//nolint:revive +func (r *Runner) init(ctx context.Context, omitModules bool, templateArchive []byte) (*sdkproto.InitComplete, *proto.FailedJob) { + ctx, span := r.startTrace(ctx, tracing.FuncName()) + defer span.End() + + err := r.session.Send(&sdkproto.Request{Type: &sdkproto.Request_Init{Init: &sdkproto.InitRequest{ + TemplateSourceArchive: templateArchive, + OmitModuleFiles: omitModules, + }}}) + if err != nil { + return nil, r.failedJobf("send init request: %v", err) + } + + nevermind := make(chan struct{}) + defer close(nevermind) + go func() { + select { + case <-nevermind: + return + case <-r.notStopped.Done(): + return + case <-r.notCanceled.Done(): + _ = r.session.Send(&sdkproto.Request{ + Type: &sdkproto.Request_Cancel{ + Cancel: &sdkproto.CancelRequest{}, + }, + }) + } + }() + + var moduleFilesUpload *sdkproto.DataBuilder + for { + msg, err := r.session.Recv() + if err != nil { + return nil, r.failedJobf("receive init response: %v", err) + } + switch msgType := msg.Type.(type) { + case *sdkproto.Response_Log: + r.logProvisionerJobLog(context.Background(), msgType.Log.Level, "terraform initialization", + slog.F("level", msgType.Log.Level), + slog.F("output", msgType.Log.Output), + ) + + r.queueLog(ctx, &proto.Log{ + Source: proto.LogSource_PROVISIONER, + Level: msgType.Log.Level, + CreatedAt: time.Now().UnixMilli(), + Output: msgType.Log.Output, + Stage: "Initializing Terraform Directory", + }) + case *sdkproto.Response_DataUpload: + if omitModules { + return nil, r.failedJobf("received unexpected module files data upload when omitModules is true") + } + c := msgType.DataUpload + if c.UploadType != sdkproto.DataUploadType_UPLOAD_TYPE_MODULE_FILES { + return nil, r.failedJobf("invalid data upload type: %q", c.UploadType) + } + + if moduleFilesUpload != nil { + return nil, r.failedJobf("multiple module data uploads received, only expect 1") + } + + moduleFilesUpload, err = sdkproto.NewDataBuilder(c) + if err != nil { + return nil, r.failedJobf("create data builder: %s", err.Error()) + } + case *sdkproto.Response_ChunkPiece: + if omitModules { + return nil, r.failedJobf("received unexpected module files data upload when omitModules is true") + } + c := msgType.ChunkPiece + if moduleFilesUpload == nil { + return nil, r.failedJobf("received chunk piece before module files data upload") + } + + _, err := moduleFilesUpload.Add(c) + if err != nil { + return nil, r.failedJobf("module files, add chunk piece: %s", err.Error()) + } + case *sdkproto.Response_Init: + if moduleFilesUpload != nil { + // If files were uploaded in multiple chunks, put them back together. + moduleFilesData, err := moduleFilesUpload.Complete() + if err != nil { + return nil, r.failedJobf("complete module files data upload: %s", err.Error()) + } + + if !bytes.Equal(msgType.Init.ModuleFilesHash, moduleFilesUpload.Hash) { + return nil, r.failedJobf("module files hash mismatch, uploaded: %x, expected: %x", moduleFilesUpload.Hash, msgType.Init.ModuleFilesHash) + } + msgType.Init.ModuleFiles = moduleFilesData + } + + return msgType.Init, nil + default: + return nil, r.failedJobf("unexpected init response type %T", msg.Type) + } + } +} diff --git a/provisionerd/runner/plan.go b/provisionerd/runner/plan.go new file mode 100644 index 0000000000000..b623cc5209573 --- /dev/null +++ b/provisionerd/runner/plan.go @@ -0,0 +1,64 @@ +package runner + +import ( + "context" + "time" + + "cdr.dev/slog" + "github.com/coder/coder/v2/coderd/tracing" + "github.com/coder/coder/v2/provisionerd/proto" + sdkproto "github.com/coder/coder/v2/provisionersdk/proto" +) + +func (r *Runner) plan(ctx context.Context, stage string, req *sdkproto.PlanRequest) (*sdkproto.PlanComplete, *proto.FailedJob) { + ctx, span := r.startTrace(ctx, tracing.FuncName()) + defer span.End() + + err := r.session.Send(&sdkproto.Request{Type: &sdkproto.Request_Plan{Plan: req}}) + if err != nil { + return nil, r.failedJobf("send plan request: %v", err) + } + + nevermind := make(chan struct{}) + defer close(nevermind) + go func() { + select { + case <-nevermind: + return + case <-r.notStopped.Done(): + return + case <-r.notCanceled.Done(): + _ = r.session.Send(&sdkproto.Request{ + Type: &sdkproto.Request_Cancel{ + Cancel: &sdkproto.CancelRequest{}, + }, + }) + } + }() + + for { + msg, err := r.session.Recv() + if err != nil { + return nil, r.failedJobf("receive plan response: %v", err) + } + switch msgType := msg.Type.(type) { + case *sdkproto.Response_Log: + r.logProvisionerJobLog(context.Background(), msgType.Log.Level, "terraform planning", + slog.F("level", msgType.Log.Level), + slog.F("output", msgType.Log.Output), + ) + + r.queueLog(ctx, &proto.Log{ + Source: proto.LogSource_PROVISIONER, + Level: msgType.Log.Level, + CreatedAt: time.Now().UnixMilli(), + Output: msgType.Log.Output, + Stage: stage, + }) + case *sdkproto.Response_Plan: + return msgType.Plan, nil + default: + return nil, r.failedJobf("unexpected plan response type %T", msg.Type) + } + } +} diff --git a/provisionerd/runner/quota.go b/provisionerd/runner/quota.go deleted file mode 100644 index 26c7e0478ec2c..0000000000000 --- a/provisionerd/runner/quota.go +++ /dev/null @@ -1,11 +0,0 @@ -package runner - -import "github.com/coder/coder/v2/provisionersdk/proto" - -func sumDailyCost(resources []*proto.Resource) int { - var sum int - for _, r := range resources { - sum += int(r.DailyCost) - } - return sum -} diff --git a/provisionerd/runner/runner.go b/provisionerd/runner/runner.go index 22b6403fe729d..fb39682878f08 100644 --- a/provisionerd/runner/runner.go +++ b/provisionerd/runner/runner.go @@ -1,7 +1,6 @@ package runner import ( - "bytes" "context" "encoding/json" "errors" @@ -515,7 +514,6 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p defer span.End() failedJob := r.configure(&sdkproto.Config{ - TemplateSourceArchive: r.job.GetTemplateSourceArchive(), TemplateId: strings2.EmptyToNil(r.job.GetTemplateImport().Metadata.TemplateId), TemplateVersionId: strings2.EmptyToNil(r.job.GetTemplateImport().Metadata.TemplateVersionId), ExpReuseTerraformWorkspace: ptr.Ref(false), @@ -524,6 +522,18 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p return nil, failedJob } + // Initialize the Terraform working directory + initResp, failedInit := r.init(ctx, false, r.job.GetTemplateSourceArchive()) + if failedInit != nil { + return nil, failedInit + } + if initResp == nil { + return nil, r.failedJobf("template import init returned nil response") + } + if initResp.Error != "" { + return nil, r.failedJobf("template import init error: %s", initResp.Error) + } + // Parse parameters and update the job with the parameter specs r.queueLog(ctx, &proto.Log{ Source: proto.LogSource_PROVISIONER_DAEMON, @@ -560,7 +570,7 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p CoderUrl: r.job.GetTemplateImport().Metadata.CoderUrl, WorkspaceOwnerGroups: r.job.GetTemplateImport().Metadata.WorkspaceOwnerGroups, WorkspaceTransition: sdkproto.WorkspaceTransition_START, - }, false) + }) if err != nil { return nil, r.failedJobf("template import provision for start: %s", err) } @@ -576,8 +586,7 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p CoderUrl: r.job.GetTemplateImport().Metadata.CoderUrl, WorkspaceOwnerGroups: r.job.GetTemplateImport().Metadata.WorkspaceOwnerGroups, WorkspaceTransition: sdkproto.WorkspaceTransition_STOP, - }, true, // Modules downloaded on the start provision - ) + }) if err != nil { return nil, r.failedJobf("template import provision for stop: %s", err) } @@ -597,12 +606,13 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p RichParameters: startProvision.Parameters, ExternalAuthProvidersNames: externalAuthProviderNames, ExternalAuthProviders: startProvision.ExternalAuthProviders, - StartModules: startProvision.Modules, - StopModules: stopProvision.Modules, - Presets: startProvision.Presets, - Plan: startProvision.Plan, - // ModuleFiles are not on the stopProvision. So grab from the startProvision. - ModuleFiles: startProvision.ModuleFiles, + // TODO: These are defined as different, but can they be? + // Terraform downloads modules regardless of `count`, so this should be the same + StartModules: initResp.Modules, + StopModules: initResp.Modules, + Presets: startProvision.Presets, + Plan: startProvision.Plan, + ModuleFiles: initResp.ModuleFiles, // ModuleFileHash will be populated if the file is uploaded async ModuleFilesHash: []byte{}, HasAiTasks: startProvision.HasAITasks, @@ -666,10 +676,9 @@ type templateImportProvision struct { Resources []*sdkproto.Resource Parameters []*sdkproto.RichParameter ExternalAuthProviders []*sdkproto.ExternalAuthProviderResource - Modules []*sdkproto.Module Presets []*sdkproto.Preset Plan json.RawMessage - ModuleFiles []byte + DailyCost int32 HasAITasks bool HasExternalAgents bool } @@ -677,8 +686,8 @@ type templateImportProvision struct { // Performs a dry-run provision when importing a template. // This is used to detect resources that would be provisioned for a workspace in various states. // It doesn't define values for rich parameters as they're unknown during template import. -func (r *Runner) runTemplateImportProvision(ctx context.Context, variableValues []*sdkproto.VariableValue, metadata *sdkproto.Metadata, omitModules bool) (*templateImportProvision, error) { - return r.runTemplateImportProvisionWithRichParameters(ctx, variableValues, nil, metadata, omitModules) +func (r *Runner) runTemplateImportProvision(ctx context.Context, variableValues []*sdkproto.VariableValue, metadata *sdkproto.Metadata) (*templateImportProvision, error) { + return r.runTemplateImportProvisionWithRichParameters(ctx, variableValues, nil, metadata) } // Performs a dry-run provision with provided rich parameters. @@ -688,7 +697,6 @@ func (r *Runner) runTemplateImportProvisionWithRichParameters( variableValues []*sdkproto.VariableValue, richParameterValues []*sdkproto.RichParameterValue, metadata *sdkproto.Metadata, - omitModules bool, ) (*templateImportProvision, error) { ctx, span := r.startTrace(ctx, tracing.FuncName()) defer span.End() @@ -700,19 +708,9 @@ func (r *Runner) runTemplateImportProvisionWithRichParameters( case sdkproto.WorkspaceTransition_STOP: stage = "Detecting ephemeral resources" } + // use the notStopped so that if we attempt to gracefully cancel, the stream will still be available for us // to send the cancel to the provisioner - err := r.session.Send(&sdkproto.Request{Type: &sdkproto.Request_Plan{Plan: &sdkproto.PlanRequest{ - Metadata: metadata, - RichParameterValues: richParameterValues, - // Template import has no previous values - PreviousParameterValues: make([]*sdkproto.RichParameterValue, 0), - VariableValues: variableValues, - OmitModuleFiles: omitModules, - }}}) - if err != nil { - return nil, xerrors.Errorf("start provision: %w", err) - } nevermind := make(chan struct{}) defer close(nevermind) go func() { @@ -730,96 +728,48 @@ func (r *Runner) runTemplateImportProvisionWithRichParameters( } }() - var moduleFilesUpload *sdkproto.DataBuilder - for { - msg, err := r.session.Recv() - if err != nil { - return nil, xerrors.Errorf("recv import provision: %w", err) - } - - switch msgType := msg.Type.(type) { - case *sdkproto.Response_Log: - r.logProvisionerJobLog(context.Background(), msgType.Log.Level, "template import provision job logged", - slog.F("level", msgType.Log.Level), - slog.F("output", msgType.Log.Output), - ) - r.queueLog(ctx, &proto.Log{ - Source: proto.LogSource_PROVISIONER, - Level: msgType.Log.Level, - CreatedAt: time.Now().UnixMilli(), - Output: msgType.Log.Output, - Stage: stage, - }) - case *sdkproto.Response_DataUpload: - c := msgType.DataUpload - if c.UploadType != sdkproto.DataUploadType_UPLOAD_TYPE_MODULE_FILES { - return nil, xerrors.Errorf("invalid data upload type: %q", c.UploadType) - } - - if moduleFilesUpload != nil { - return nil, xerrors.New("multiple module data uploads received, only expect 1") - } - - moduleFilesUpload, err = sdkproto.NewDataBuilder(c) - if err != nil { - return nil, xerrors.Errorf("create data builder: %w", err) - } - case *sdkproto.Response_ChunkPiece: - c := msgType.ChunkPiece - if moduleFilesUpload == nil { - return nil, xerrors.New("received chunk piece before module files data upload") - } - - _, err := moduleFilesUpload.Add(c) - if err != nil { - return nil, xerrors.Errorf("module files, add chunk piece: %w", err) - } - case *sdkproto.Response_Plan: - c := msgType.Plan - if c.Error != "" { - r.logger.Info(context.Background(), "dry-run provision failure", - slog.F("error", c.Error), - ) - - return nil, xerrors.New(c.Error) - } - - if moduleFilesUpload != nil && len(c.ModuleFiles) > 0 { - return nil, xerrors.New("module files were uploaded and module files were returned in the plan response. Only one of these should be set") - } - - r.logger.Info(context.Background(), "parse dry-run provision successful", - slog.F("resource_count", len(c.Resources)), - slog.F("resources", resourceNames(c.Resources)), - ) + planComplete, failed := r.plan(ctx, stage, &sdkproto.PlanRequest{ + Metadata: metadata, + RichParameterValues: richParameterValues, + VariableValues: variableValues, + ExternalAuthProviders: nil, + PreviousParameterValues: nil, + State: nil, + }) + if failed != nil { + return nil, xerrors.Errorf("plan during template import provision: %w", failed) + } + if planComplete == nil { + return nil, xerrors.New("plan during template import provision returned nil response") + } + if planComplete.Error != "" { + return nil, xerrors.Errorf("plan during template import provision error: %s", planComplete.Error) + } - moduleFilesData := c.ModuleFiles - if moduleFilesUpload != nil { - uploadData, err := moduleFilesUpload.Complete() - if err != nil { - return nil, xerrors.Errorf("module files, complete upload: %w", err) - } - moduleFilesData = uploadData - if !bytes.Equal(c.ModuleFilesHash, moduleFilesUpload.Hash) { - return nil, xerrors.Errorf("module files hash mismatch, uploaded: %x, expected: %x", moduleFilesUpload.Hash, c.ModuleFilesHash) - } - } - return &templateImportProvision{ - Resources: c.Resources, - Parameters: c.Parameters, - ExternalAuthProviders: c.ExternalAuthProviders, - Modules: c.Modules, - Presets: c.Presets, - Plan: c.Plan, - ModuleFiles: moduleFilesData, - HasAITasks: c.HasAiTasks, - HasExternalAgents: c.HasExternalAgents, - }, nil - default: - return nil, xerrors.Errorf("invalid message type %q received from provisioner", - reflect.TypeOf(msg.Type).String()) - } + graphComplete, failed := r.graph(ctx, &sdkproto.GraphRequest{ + Metadata: metadata, + Source: sdkproto.GraphSource_SOURCE_PLAN, + }) + if failed != nil { + return nil, xerrors.Errorf("graph during template import provision: %w", failed) + } + if graphComplete == nil { + return nil, xerrors.New("graph during template import provision returned nil response") } + if graphComplete.Error != "" { + return nil, xerrors.Errorf("graph during template import provision error: %s", graphComplete.Error) + } + + return &templateImportProvision{ + Resources: graphComplete.Resources, + Parameters: graphComplete.Parameters, + ExternalAuthProviders: graphComplete.ExternalAuthProviders, + Presets: graphComplete.Presets, + Plan: planComplete.Plan, + DailyCost: planComplete.DailyCost, + HasAITasks: graphComplete.HasAiTasks, + HasExternalAgents: graphComplete.HasExternalAgents, + }, nil } func (r *Runner) runTemplateDryRun(ctx context.Context) (*proto.CompletedJob, *proto.FailedJob) { @@ -854,19 +804,28 @@ func (r *Runner) runTemplateDryRun(ctx context.Context) (*proto.CompletedJob, *p metadata.WorkspaceOwnerId = id.String() } - failedJob := r.configure(&sdkproto.Config{ - TemplateSourceArchive: r.job.GetTemplateSourceArchive(), - }) + failedJob := r.configure(&sdkproto.Config{}) if failedJob != nil { return nil, failedJob } + // Initialize the Terraform working directory + initResp, failedJob := r.init(ctx, false, r.job.GetTemplateSourceArchive()) + if failedJob != nil { + return nil, failedJob + } + if initResp == nil { + return nil, r.failedJobf("template dry-run init returned nil response") + } + if initResp.Error != "" { + return nil, r.failedJobf("template dry-run init error: %s", initResp.Error) + } + // Run the template import provision task since it's already a dry run. provision, err := r.runTemplateImportProvisionWithRichParameters(ctx, r.job.GetTemplateDryRun().GetVariableValues(), r.job.GetTemplateDryRun().GetRichParameterValues(), metadata, - false, ) if err != nil { return nil, r.failedJobf("run dry-run provision job: %s", err) @@ -877,7 +836,7 @@ func (r *Runner) runTemplateDryRun(ctx context.Context) (*proto.CompletedJob, *p Type: &proto.CompletedJob_TemplateDryRun_{ TemplateDryRun: &proto.CompletedJob_TemplateDryRun{ Resources: provision.Resources, - Modules: provision.Modules, + Modules: initResp.Modules, }, }, }, nil @@ -940,10 +899,8 @@ func (r *Runner) buildWorkspace(ctx context.Context, stage string, req *sdkproto } } -func (r *Runner) commitQuota(ctx context.Context, resources []*sdkproto.Resource) *proto.FailedJob { - cost := sumDailyCost(resources) +func (r *Runner) commitQuota(ctx context.Context, cost int32) *proto.FailedJob { r.logger.Debug(ctx, "committing quota", - slog.F("resources", resourceNames(resources)), slog.F("cost", cost), ) if cost == 0 { @@ -955,7 +912,7 @@ func (r *Runner) commitQuota(ctx context.Context, resources []*sdkproto.Resource resp, err := r.quotaCommitter.CommitQuota(ctx, &proto.CommitQuotaRequest{ JobId: r.job.JobId, // #nosec G115 - Safe conversion as cost is expected to be within int32 range for provisioning costs - DailyCost: int32(cost), + DailyCost: cost, }) if err != nil { r.queueLog(ctx, &proto.Log{ @@ -1014,8 +971,6 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p } failedJob := r.configure(&sdkproto.Config{ - TemplateSourceArchive: r.job.GetTemplateSourceArchive(), - State: r.job.GetWorkspaceBuild().State, ProvisionerLogLevel: r.job.GetWorkspaceBuild().LogLevel, TemplateId: strings2.EmptyToNil(r.job.GetWorkspaceBuild().Metadata.TemplateId), TemplateVersionId: strings2.EmptyToNil(r.job.GetWorkspaceBuild().Metadata.TemplateVersionId), @@ -1025,24 +980,42 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p return nil, failedJob } - resp, failed := r.buildWorkspace(ctx, "Planning infrastructure", &sdkproto.Request{ - Type: &sdkproto.Request_Plan{ - Plan: &sdkproto.PlanRequest{ - OmitModuleFiles: true, // Only useful for template imports - Metadata: r.job.GetWorkspaceBuild().Metadata, - RichParameterValues: r.job.GetWorkspaceBuild().RichParameterValues, - PreviousParameterValues: r.job.GetWorkspaceBuild().PreviousParameterValues, - VariableValues: r.job.GetWorkspaceBuild().VariableValues, - ExternalAuthProviders: r.job.GetWorkspaceBuild().ExternalAuthProviders, + // Initialize the Terraform working directory + initComplete, failedJob := r.init(ctx, true, r.job.GetTemplateSourceArchive()) + if failedJob != nil { + return nil, failedJob + } + if initComplete == nil { + return nil, r.failedWorkspaceBuildf("invalid message type received from provisioner during init") + } + if initComplete.Error != "" { + r.logger.Warn(context.Background(), "plan request failed", + slog.F("error", initComplete.Error), + ) + + return nil, &proto.FailedJob{ + JobId: r.job.JobId, + Error: initComplete.Error, + Type: &proto.FailedJob_WorkspaceBuild_{ + WorkspaceBuild: &proto.FailedJob_WorkspaceBuild{}, }, - }, + } + } + + // Run `terraform plan` + planComplete, failed := r.plan(ctx, "Planning Infrastructure", &sdkproto.PlanRequest{ + Metadata: r.job.GetWorkspaceBuild().Metadata, + RichParameterValues: r.job.GetWorkspaceBuild().RichParameterValues, + VariableValues: r.job.GetWorkspaceBuild().VariableValues, + ExternalAuthProviders: r.job.GetWorkspaceBuild().ExternalAuthProviders, + PreviousParameterValues: r.job.GetWorkspaceBuild().PreviousParameterValues, + State: r.job.GetWorkspaceBuild().State, }) if failed != nil { return nil, failed } - planComplete := resp.GetPlan() if planComplete == nil { - return nil, r.failedWorkspaceBuildf("invalid message type %T received from provisioner", resp.Type) + return nil, r.failedWorkspaceBuildf("invalid message type received from provisioner during plan") } if planComplete.Error != "" { r.logger.Warn(context.Background(), "plan request failed", @@ -1057,23 +1030,22 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p }, } } - if len(planComplete.AiTasks) > 1 { - return nil, r.failedWorkspaceBuildf("only one 'coder_ai_task' resource can be provisioned per template") + + if planComplete.AiTaskCount > 1 { + return nil, r.failedWorkspaceBuildf("only one 'coder_ai_task' resource can be provisioned per template, found %d", planComplete.AiTaskCount) } - r.logger.Info(context.Background(), "plan request successful", - slog.F("resource_count", len(planComplete.Resources)), - slog.F("resources", resourceNames(planComplete.Resources)), - ) + r.logger.Info(context.Background(), "plan request successful") r.flushQueuedLogs(ctx) if commitQuota { - failed = r.commitQuota(ctx, planComplete.Resources) + failed = r.commitQuota(ctx, planComplete.GetDailyCost()) r.flushQueuedLogs(ctx) if failed != nil { return nil, failed } } + // Run Terraform Apply r.queueLog(ctx, &proto.Log{ Source: proto.LogSource_PROVISIONER_DAEMON, Level: sdkproto.LogLevel_INFO, @@ -1081,7 +1053,7 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p CreatedAt: time.Now().UnixMilli(), }) - resp, failed = r.buildWorkspace(ctx, applyStage, &sdkproto.Request{ + resp, failed := r.buildWorkspace(ctx, applyStage, &sdkproto.Request{ Type: &sdkproto.Request_Apply{ Apply: &sdkproto.ApplyRequest{ Metadata: r.job.GetWorkspaceBuild().Metadata, @@ -1096,8 +1068,38 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p return nil, r.failedWorkspaceBuildf("invalid message type %T received from provisioner", resp.Type) } + // Run Terraform Graph + graphComplete, failed := r.graph(ctx, &sdkproto.GraphRequest{ + Metadata: r.job.GetWorkspaceBuild().Metadata, + Source: sdkproto.GraphSource_SOURCE_STATE, + }) + if failed != nil { + return nil, failed + } + if graphComplete == nil { + return nil, r.failedWorkspaceBuildf("invalid message type %T received from provisioner", resp.Type) + } + if graphComplete.Error != "" { + r.logger.Warn(context.Background(), "graph request failed", + slog.F("error", planComplete.Error), + ) + + return nil, &proto.FailedJob{ + JobId: r.job.JobId, + Error: graphComplete.Error, + Type: &proto.FailedJob_WorkspaceBuild_{ + WorkspaceBuild: &proto.FailedJob_WorkspaceBuild{}, + }, + } + } + // Prepend the plan timings (since they occurred first). - applyComplete.Timings = append(planComplete.Timings, applyComplete.Timings...) + // Build up the full timing list in chronological order. + timings := make([]*sdkproto.Timing, 0, len(initComplete.Timings)+len(planComplete.Timings)+len(applyComplete.Timings)+len(graphComplete.Timings)) + timings = append(timings, initComplete.Timings...) + timings = append(timings, planComplete.Timings...) + timings = append(timings, applyComplete.Timings...) + timings = append(timings, graphComplete.Timings...) if applyComplete.Error != "" { r.logger.Warn(context.Background(), "apply failed; updating state", @@ -1111,15 +1113,15 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p Type: &proto.FailedJob_WorkspaceBuild_{ WorkspaceBuild: &proto.FailedJob_WorkspaceBuild{ State: applyComplete.State, - Timings: applyComplete.Timings, + Timings: timings, }, }, } } r.logger.Info(context.Background(), "apply successful", - slog.F("resource_count", len(applyComplete.Resources)), - slog.F("resources", resourceNames(applyComplete.Resources)), + slog.F("resource_count", len(graphComplete.Resources)), + slog.F("resources", resourceNames(graphComplete.Resources)), slog.F("state_len", len(applyComplete.State)), ) r.flushQueuedLogs(ctx) @@ -1129,15 +1131,12 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p Type: &proto.CompletedJob_WorkspaceBuild_{ WorkspaceBuild: &proto.CompletedJob_WorkspaceBuild{ State: applyComplete.State, - Resources: applyComplete.Resources, - Timings: applyComplete.Timings, - // Modules are created on disk by `terraform init`, and that is only - // called by `plan`. `apply` does not modify them, so we can use the - // modules from the plan response. - Modules: planComplete.Modules, + Resources: graphComplete.Resources, + Timings: timings, + Modules: initComplete.Modules, // Resource replacements are discovered at plan time, only. ResourceReplacements: planComplete.ResourceReplacements, - AiTasks: applyComplete.AiTasks, + AiTasks: graphComplete.AiTasks, }, }, }, nil diff --git a/provisionersdk/errors.go b/provisionersdk/errors.go index 0dc66e6e6b301..f7820592d1dd3 100644 --- a/provisionersdk/errors.go +++ b/provisionersdk/errors.go @@ -10,6 +10,10 @@ func ParseErrorf(format string, args ...any) *proto.ParseComplete { return &proto.ParseComplete{Error: fmt.Sprintf(format, args...)} } +func InitErrorf(format string, args ...any) *proto.InitComplete { + return &proto.InitComplete{Error: fmt.Sprintf(format, args...)} +} + func PlanErrorf(format string, args ...any) *proto.PlanComplete { return &proto.PlanComplete{Error: fmt.Sprintf(format, args...)} } @@ -17,3 +21,7 @@ func PlanErrorf(format string, args ...any) *proto.PlanComplete { func ApplyErrorf(format string, args ...any) *proto.ApplyComplete { return &proto.ApplyComplete{Error: fmt.Sprintf(format, args...)} } + +func GraphError(format string, args ...any) *proto.GraphComplete { + return &proto.GraphComplete{Error: fmt.Sprintf(format, args...)} +} diff --git a/provisionersdk/proto/provisioner.pb.go b/provisionersdk/proto/provisioner.pb.go index 72741a1036b41..69f73eb8fa0f7 100644 --- a/provisionersdk/proto/provisioner.pb.go +++ b/provisionersdk/proto/provisioner.pb.go @@ -348,6 +348,55 @@ func (PrebuiltWorkspaceBuildStage) EnumDescriptor() ([]byte, []int) { return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{5} } +type GraphSource int32 + +const ( + GraphSource_SOURCE_UNKNOWN GraphSource = 0 + GraphSource_SOURCE_PLAN GraphSource = 1 + GraphSource_SOURCE_STATE GraphSource = 2 +) + +// Enum value maps for GraphSource. +var ( + GraphSource_name = map[int32]string{ + 0: "SOURCE_UNKNOWN", + 1: "SOURCE_PLAN", + 2: "SOURCE_STATE", + } + GraphSource_value = map[string]int32{ + "SOURCE_UNKNOWN": 0, + "SOURCE_PLAN": 1, + "SOURCE_STATE": 2, + } +) + +func (x GraphSource) Enum() *GraphSource { + p := new(GraphSource) + *p = x + return p +} + +func (x GraphSource) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (GraphSource) Descriptor() protoreflect.EnumDescriptor { + return file_provisionersdk_proto_provisioner_proto_enumTypes[6].Descriptor() +} + +func (GraphSource) Type() protoreflect.EnumType { + return &file_provisionersdk_proto_provisioner_proto_enumTypes[6] +} + +func (x GraphSource) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use GraphSource.Descriptor instead. +func (GraphSource) EnumDescriptor() ([]byte, []int) { + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{6} +} + type TimingState int32 const ( @@ -381,11 +430,11 @@ func (x TimingState) String() string { } func (TimingState) Descriptor() protoreflect.EnumDescriptor { - return file_provisionersdk_proto_provisioner_proto_enumTypes[6].Descriptor() + return file_provisionersdk_proto_provisioner_proto_enumTypes[7].Descriptor() } func (TimingState) Type() protoreflect.EnumType { - return &file_provisionersdk_proto_provisioner_proto_enumTypes[6] + return &file_provisionersdk_proto_provisioner_proto_enumTypes[7] } func (x TimingState) Number() protoreflect.EnumNumber { @@ -394,7 +443,7 @@ func (x TimingState) Number() protoreflect.EnumNumber { // Deprecated: Use TimingState.Descriptor instead. func (TimingState) EnumDescriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{6} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{7} } type DataUploadType int32 @@ -430,11 +479,11 @@ func (x DataUploadType) String() string { } func (DataUploadType) Descriptor() protoreflect.EnumDescriptor { - return file_provisionersdk_proto_provisioner_proto_enumTypes[7].Descriptor() + return file_provisionersdk_proto_provisioner_proto_enumTypes[8].Descriptor() } func (DataUploadType) Type() protoreflect.EnumType { - return &file_provisionersdk_proto_provisioner_proto_enumTypes[7] + return &file_provisionersdk_proto_provisioner_proto_enumTypes[8] } func (x DataUploadType) Number() protoreflect.EnumNumber { @@ -443,7 +492,7 @@ func (x DataUploadType) Number() protoreflect.EnumNumber { // Deprecated: Use DataUploadType.Descriptor instead. func (DataUploadType) EnumDescriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{7} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{8} } // Empty indicates a successful request/response. @@ -3155,16 +3204,12 @@ type Config struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // template_source_archive is a tar of the template source files - TemplateSourceArchive []byte `protobuf:"bytes,1,opt,name=template_source_archive,json=templateSourceArchive,proto3" json:"template_source_archive,omitempty"` - // state is the provisioner state (if any) - State []byte `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` - ProvisionerLogLevel string `protobuf:"bytes,3,opt,name=provisioner_log_level,json=provisionerLogLevel,proto3" json:"provisioner_log_level,omitempty"` + ProvisionerLogLevel string `protobuf:"bytes,1,opt,name=provisioner_log_level,json=provisionerLogLevel,proto3" json:"provisioner_log_level,omitempty"` // Template imports can omit template id - TemplateId *string `protobuf:"bytes,4,opt,name=template_id,json=templateId,proto3,oneof" json:"template_id,omitempty"` + TemplateId *string `protobuf:"bytes,2,opt,name=template_id,json=templateId,proto3,oneof" json:"template_id,omitempty"` // Dry runs omit version id - TemplateVersionId *string `protobuf:"bytes,5,opt,name=template_version_id,json=templateVersionId,proto3,oneof" json:"template_version_id,omitempty"` - ExpReuseTerraformWorkspace *bool `protobuf:"varint,6,opt,name=exp_reuse_terraform_workspace,json=expReuseTerraformWorkspace,proto3,oneof" json:"exp_reuse_terraform_workspace,omitempty"` // Whether to reuse existing terraform workspaces if they exist. + TemplateVersionId *string `protobuf:"bytes,3,opt,name=template_version_id,json=templateVersionId,proto3,oneof" json:"template_version_id,omitempty"` + ExpReuseTerraformWorkspace *bool `protobuf:"varint,4,opt,name=exp_reuse_terraform_workspace,json=expReuseTerraformWorkspace,proto3,oneof" json:"exp_reuse_terraform_workspace,omitempty"` // Whether to reuse existing terraform workspaces if they exist. } func (x *Config) Reset() { @@ -3199,20 +3244,6 @@ func (*Config) Descriptor() ([]byte, []int) { return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{34} } -func (x *Config) GetTemplateSourceArchive() []byte { - if x != nil { - return x.TemplateSourceArchive - } - return nil -} - -func (x *Config) GetState() []byte { - if x != nil { - return x.State - } - return nil -} - func (x *Config) GetProvisionerLogLevel() string { if x != nil { return x.ProvisionerLogLevel @@ -3352,6 +3383,145 @@ func (x *ParseComplete) GetWorkspaceTags() map[string]string { return nil } +type InitRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // template_source_archive is a tar of the template source files + TemplateSourceArchive []byte `protobuf:"bytes,1,opt,name=template_source_archive,json=templateSourceArchive,proto3" json:"template_source_archive,omitempty"` + // If true, the provisioner can safely assume the caller does not need the + // module files downloaded by the `terraform init` command. + // Ideally this boolean would be flipped in its truthy value, however since + // this is costly, the zero value omitting the module files is preferred. + OmitModuleFiles bool `protobuf:"varint,3,opt,name=omit_module_files,json=omitModuleFiles,proto3" json:"omit_module_files,omitempty"` +} + +func (x *InitRequest) Reset() { + *x = InitRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InitRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InitRequest) ProtoMessage() {} + +func (x *InitRequest) ProtoReflect() protoreflect.Message { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[37] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InitRequest.ProtoReflect.Descriptor instead. +func (*InitRequest) Descriptor() ([]byte, []int) { + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{37} +} + +func (x *InitRequest) GetTemplateSourceArchive() []byte { + if x != nil { + return x.TemplateSourceArchive + } + return nil +} + +func (x *InitRequest) GetOmitModuleFiles() bool { + if x != nil { + return x.OmitModuleFiles + } + return false +} + +type InitComplete struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` + Timings []*Timing `protobuf:"bytes,2,rep,name=timings,proto3" json:"timings,omitempty"` + Modules []*Module `protobuf:"bytes,3,rep,name=modules,proto3" json:"modules,omitempty"` + ModuleFiles []byte `protobuf:"bytes,4,opt,name=module_files,json=moduleFiles,proto3" json:"module_files,omitempty"` + ModuleFilesHash []byte `protobuf:"bytes,5,opt,name=module_files_hash,json=moduleFilesHash,proto3" json:"module_files_hash,omitempty"` +} + +func (x *InitComplete) Reset() { + *x = InitComplete{} + if protoimpl.UnsafeEnabled { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[38] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InitComplete) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InitComplete) ProtoMessage() {} + +func (x *InitComplete) ProtoReflect() protoreflect.Message { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[38] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InitComplete.ProtoReflect.Descriptor instead. +func (*InitComplete) Descriptor() ([]byte, []int) { + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{38} +} + +func (x *InitComplete) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +func (x *InitComplete) GetTimings() []*Timing { + if x != nil { + return x.Timings + } + return nil +} + +func (x *InitComplete) GetModules() []*Module { + if x != nil { + return x.Modules + } + return nil +} + +func (x *InitComplete) GetModuleFiles() []byte { + if x != nil { + return x.ModuleFiles + } + return nil +} + +func (x *InitComplete) GetModuleFilesHash() []byte { + if x != nil { + return x.ModuleFilesHash + } + return nil +} + // PlanRequest asks the provisioner to plan what resources & parameters it will create type PlanRequest struct { state protoimpl.MessageState @@ -3363,18 +3533,14 @@ type PlanRequest struct { VariableValues []*VariableValue `protobuf:"bytes,3,rep,name=variable_values,json=variableValues,proto3" json:"variable_values,omitempty"` ExternalAuthProviders []*ExternalAuthProvider `protobuf:"bytes,4,rep,name=external_auth_providers,json=externalAuthProviders,proto3" json:"external_auth_providers,omitempty"` PreviousParameterValues []*RichParameterValue `protobuf:"bytes,5,rep,name=previous_parameter_values,json=previousParameterValues,proto3" json:"previous_parameter_values,omitempty"` - // If true, the provisioner can safely assume the caller does not need the - // module files downloaded by the `terraform init` command. - // Ideally this boolean would be flipped in its truthy value, however for - // backwards compatibility reasons, the zero value should be the previous - // behavior of downloading the module files. - OmitModuleFiles bool `protobuf:"varint,6,opt,name=omit_module_files,json=omitModuleFiles,proto3" json:"omit_module_files,omitempty"` + // state is the provisioner state (if any) + State []byte `protobuf:"bytes,6,opt,name=state,proto3" json:"state,omitempty"` } func (x *PlanRequest) Reset() { *x = PlanRequest{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[37] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3387,7 +3553,7 @@ func (x *PlanRequest) String() string { func (*PlanRequest) ProtoMessage() {} func (x *PlanRequest) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[37] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3400,7 +3566,7 @@ func (x *PlanRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PlanRequest.ProtoReflect.Descriptor instead. func (*PlanRequest) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{37} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{39} } func (x *PlanRequest) GetMetadata() *Metadata { @@ -3438,11 +3604,11 @@ func (x *PlanRequest) GetPreviousParameterValues() []*RichParameterValue { return nil } -func (x *PlanRequest) GetOmitModuleFiles() bool { +func (x *PlanRequest) GetState() []byte { if x != nil { - return x.OmitModuleFiles + return x.State } - return false + return nil } // PlanComplete indicates a request to plan completed. @@ -3451,31 +3617,18 @@ type PlanComplete struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` - Resources []*Resource `protobuf:"bytes,2,rep,name=resources,proto3" json:"resources,omitempty"` - Parameters []*RichParameter `protobuf:"bytes,3,rep,name=parameters,proto3" json:"parameters,omitempty"` - ExternalAuthProviders []*ExternalAuthProviderResource `protobuf:"bytes,4,rep,name=external_auth_providers,json=externalAuthProviders,proto3" json:"external_auth_providers,omitempty"` - Timings []*Timing `protobuf:"bytes,6,rep,name=timings,proto3" json:"timings,omitempty"` - Modules []*Module `protobuf:"bytes,7,rep,name=modules,proto3" json:"modules,omitempty"` - Presets []*Preset `protobuf:"bytes,8,rep,name=presets,proto3" json:"presets,omitempty"` - Plan []byte `protobuf:"bytes,9,opt,name=plan,proto3" json:"plan,omitempty"` - ResourceReplacements []*ResourceReplacement `protobuf:"bytes,10,rep,name=resource_replacements,json=resourceReplacements,proto3" json:"resource_replacements,omitempty"` - ModuleFiles []byte `protobuf:"bytes,11,opt,name=module_files,json=moduleFiles,proto3" json:"module_files,omitempty"` - ModuleFilesHash []byte `protobuf:"bytes,12,opt,name=module_files_hash,json=moduleFilesHash,proto3" json:"module_files_hash,omitempty"` - // Whether a template has any `coder_ai_task` resources defined, even if not planned for creation. - // During a template import, a plan is run which may not yield in any `coder_ai_task` resources, but nonetheless we - // still need to know that such resources are defined. - // - // See `hasAITaskResources` in provisioner/terraform/resources.go for more details. - HasAiTasks bool `protobuf:"varint,13,opt,name=has_ai_tasks,json=hasAiTasks,proto3" json:"has_ai_tasks,omitempty"` - AiTasks []*AITask `protobuf:"bytes,14,rep,name=ai_tasks,json=aiTasks,proto3" json:"ai_tasks,omitempty"` - HasExternalAgents bool `protobuf:"varint,15,opt,name=has_external_agents,json=hasExternalAgents,proto3" json:"has_external_agents,omitempty"` + Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` + Timings []*Timing `protobuf:"bytes,2,rep,name=timings,proto3" json:"timings,omitempty"` + Plan []byte `protobuf:"bytes,3,opt,name=plan,proto3" json:"plan,omitempty"` + DailyCost int32 `protobuf:"varint,4,opt,name=dailyCost,proto3" json:"dailyCost,omitempty"` + ResourceReplacements []*ResourceReplacement `protobuf:"bytes,5,rep,name=resource_replacements,json=resourceReplacements,proto3" json:"resource_replacements,omitempty"` + AiTaskCount int32 `protobuf:"varint,6,opt,name=ai_task_count,json=aiTaskCount,proto3" json:"ai_task_count,omitempty"` } func (x *PlanComplete) Reset() { *x = PlanComplete{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[38] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3488,7 +3641,7 @@ func (x *PlanComplete) String() string { func (*PlanComplete) ProtoMessage() {} func (x *PlanComplete) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[38] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3501,7 +3654,7 @@ func (x *PlanComplete) ProtoReflect() protoreflect.Message { // Deprecated: Use PlanComplete.ProtoReflect.Descriptor instead. func (*PlanComplete) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{38} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{40} } func (x *PlanComplete) GetError() string { @@ -3511,124 +3664,189 @@ func (x *PlanComplete) GetError() string { return "" } -func (x *PlanComplete) GetResources() []*Resource { +func (x *PlanComplete) GetTimings() []*Timing { if x != nil { - return x.Resources + return x.Timings } return nil } -func (x *PlanComplete) GetParameters() []*RichParameter { +func (x *PlanComplete) GetPlan() []byte { if x != nil { - return x.Parameters + return x.Plan } return nil } -func (x *PlanComplete) GetExternalAuthProviders() []*ExternalAuthProviderResource { +func (x *PlanComplete) GetDailyCost() int32 { if x != nil { - return x.ExternalAuthProviders + return x.DailyCost } - return nil + return 0 } -func (x *PlanComplete) GetTimings() []*Timing { +func (x *PlanComplete) GetResourceReplacements() []*ResourceReplacement { if x != nil { - return x.Timings + return x.ResourceReplacements } return nil } -func (x *PlanComplete) GetModules() []*Module { +func (x *PlanComplete) GetAiTaskCount() int32 { if x != nil { - return x.Modules + return x.AiTaskCount } - return nil + return 0 } -func (x *PlanComplete) GetPresets() []*Preset { - if x != nil { - return x.Presets +// ApplyRequest asks the provisioner to apply the changes. Apply MUST be preceded by a successful plan request/response +// in the same Session. The plan data is not transmitted over the wire and is cached by the provisioner in the Session. +type ApplyRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` + // state is the provisioner state (if any) + State []byte `protobuf:"bytes,6,opt,name=state,proto3" json:"state,omitempty"` +} + +func (x *ApplyRequest) Reset() { + *x = ApplyRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[41] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (x *PlanComplete) GetPlan() []byte { - if x != nil { - return x.Plan +func (x *ApplyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApplyRequest) ProtoMessage() {} + +func (x *ApplyRequest) ProtoReflect() protoreflect.Message { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[41] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return nil + return mi.MessageOf(x) } -func (x *PlanComplete) GetResourceReplacements() []*ResourceReplacement { +// Deprecated: Use ApplyRequest.ProtoReflect.Descriptor instead. +func (*ApplyRequest) Descriptor() ([]byte, []int) { + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{41} +} + +func (x *ApplyRequest) GetMetadata() *Metadata { if x != nil { - return x.ResourceReplacements + return x.Metadata } return nil } -func (x *PlanComplete) GetModuleFiles() []byte { +func (x *ApplyRequest) GetState() []byte { if x != nil { - return x.ModuleFiles + return x.State } return nil } -func (x *PlanComplete) GetModuleFilesHash() []byte { - if x != nil { - return x.ModuleFilesHash +// ApplyComplete indicates a request to apply completed. +type ApplyComplete struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + State []byte `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"` + Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` + Timings []*Timing `protobuf:"bytes,3,rep,name=timings,proto3" json:"timings,omitempty"` +} + +func (x *ApplyComplete) Reset() { + *x = ApplyComplete{} + if protoimpl.UnsafeEnabled { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return nil } -func (x *PlanComplete) GetHasAiTasks() bool { - if x != nil { - return x.HasAiTasks +func (x *ApplyComplete) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApplyComplete) ProtoMessage() {} + +func (x *ApplyComplete) ProtoReflect() protoreflect.Message { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[42] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return false + return mi.MessageOf(x) +} + +// Deprecated: Use ApplyComplete.ProtoReflect.Descriptor instead. +func (*ApplyComplete) Descriptor() ([]byte, []int) { + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{42} } -func (x *PlanComplete) GetAiTasks() []*AITask { +func (x *ApplyComplete) GetState() []byte { if x != nil { - return x.AiTasks + return x.State } return nil } -func (x *PlanComplete) GetHasExternalAgents() bool { +func (x *ApplyComplete) GetError() string { if x != nil { - return x.HasExternalAgents + return x.Error } - return false + return "" } -// ApplyRequest asks the provisioner to apply the changes. Apply MUST be preceded by a successful plan request/response -// in the same Session. The plan data is not transmitted over the wire and is cached by the provisioner in the Session. -type ApplyRequest struct { +func (x *ApplyComplete) GetTimings() []*Timing { + if x != nil { + return x.Timings + } + return nil +} + +type GraphRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` + Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` + Source GraphSource `protobuf:"varint,2,opt,name=source,proto3,enum=provisioner.GraphSource" json:"source,omitempty"` } -func (x *ApplyRequest) Reset() { - *x = ApplyRequest{} +func (x *GraphRequest) Reset() { + *x = GraphRequest{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[39] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ApplyRequest) String() string { +func (x *GraphRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ApplyRequest) ProtoMessage() {} +func (*GraphRequest) ProtoMessage() {} -func (x *ApplyRequest) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[39] +func (x *GraphRequest) ProtoReflect() protoreflect.Message { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3639,50 +3857,63 @@ func (x *ApplyRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ApplyRequest.ProtoReflect.Descriptor instead. -func (*ApplyRequest) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{39} +// Deprecated: Use GraphRequest.ProtoReflect.Descriptor instead. +func (*GraphRequest) Descriptor() ([]byte, []int) { + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{43} } -func (x *ApplyRequest) GetMetadata() *Metadata { +func (x *GraphRequest) GetMetadata() *Metadata { if x != nil { return x.Metadata } return nil } -// ApplyComplete indicates a request to apply completed. -type ApplyComplete struct { +func (x *GraphRequest) GetSource() GraphSource { + if x != nil { + return x.Source + } + return GraphSource_SOURCE_UNKNOWN +} + +type GraphComplete struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - State []byte `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"` - Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` + Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` + Timings []*Timing `protobuf:"bytes,2,rep,name=timings,proto3" json:"timings,omitempty"` Resources []*Resource `protobuf:"bytes,3,rep,name=resources,proto3" json:"resources,omitempty"` Parameters []*RichParameter `protobuf:"bytes,4,rep,name=parameters,proto3" json:"parameters,omitempty"` ExternalAuthProviders []*ExternalAuthProviderResource `protobuf:"bytes,5,rep,name=external_auth_providers,json=externalAuthProviders,proto3" json:"external_auth_providers,omitempty"` - Timings []*Timing `protobuf:"bytes,6,rep,name=timings,proto3" json:"timings,omitempty"` - AiTasks []*AITask `protobuf:"bytes,7,rep,name=ai_tasks,json=aiTasks,proto3" json:"ai_tasks,omitempty"` + Presets []*Preset `protobuf:"bytes,6,rep,name=presets,proto3" json:"presets,omitempty"` + // Whether a template has any `coder_ai_task` resources defined, even if not planned for creation. + // During a template import, a plan is run which may not yield in any `coder_ai_task` resources, but nonetheless we + // still need to know that such resources are defined. + // + // See `hasAITaskResources` in provisioner/terraform/resources.go for more details. + HasAiTasks bool `protobuf:"varint,7,opt,name=has_ai_tasks,json=hasAiTasks,proto3" json:"has_ai_tasks,omitempty"` + AiTasks []*AITask `protobuf:"bytes,8,rep,name=ai_tasks,json=aiTasks,proto3" json:"ai_tasks,omitempty"` + HasExternalAgents bool `protobuf:"varint,9,opt,name=has_external_agents,json=hasExternalAgents,proto3" json:"has_external_agents,omitempty"` } -func (x *ApplyComplete) Reset() { - *x = ApplyComplete{} +func (x *GraphComplete) Reset() { + *x = GraphComplete{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[40] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ApplyComplete) String() string { +func (x *GraphComplete) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ApplyComplete) ProtoMessage() {} +func (*GraphComplete) ProtoMessage() {} -func (x *ApplyComplete) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[40] +func (x *GraphComplete) ProtoReflect() protoreflect.Message { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3693,60 +3924,74 @@ func (x *ApplyComplete) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ApplyComplete.ProtoReflect.Descriptor instead. -func (*ApplyComplete) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{40} +// Deprecated: Use GraphComplete.ProtoReflect.Descriptor instead. +func (*GraphComplete) Descriptor() ([]byte, []int) { + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{44} } -func (x *ApplyComplete) GetState() []byte { +func (x *GraphComplete) GetError() string { if x != nil { - return x.State + return x.Error } - return nil + return "" } -func (x *ApplyComplete) GetError() string { +func (x *GraphComplete) GetTimings() []*Timing { if x != nil { - return x.Error + return x.Timings } - return "" + return nil } -func (x *ApplyComplete) GetResources() []*Resource { +func (x *GraphComplete) GetResources() []*Resource { if x != nil { return x.Resources } return nil } -func (x *ApplyComplete) GetParameters() []*RichParameter { +func (x *GraphComplete) GetParameters() []*RichParameter { if x != nil { return x.Parameters } return nil } -func (x *ApplyComplete) GetExternalAuthProviders() []*ExternalAuthProviderResource { +func (x *GraphComplete) GetExternalAuthProviders() []*ExternalAuthProviderResource { if x != nil { return x.ExternalAuthProviders } return nil } -func (x *ApplyComplete) GetTimings() []*Timing { +func (x *GraphComplete) GetPresets() []*Preset { if x != nil { - return x.Timings + return x.Presets } return nil } -func (x *ApplyComplete) GetAiTasks() []*AITask { +func (x *GraphComplete) GetHasAiTasks() bool { + if x != nil { + return x.HasAiTasks + } + return false +} + +func (x *GraphComplete) GetAiTasks() []*AITask { if x != nil { return x.AiTasks } return nil } +func (x *GraphComplete) GetHasExternalAgents() bool { + if x != nil { + return x.HasExternalAgents + } + return false +} + type Timing struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3764,7 +4009,7 @@ type Timing struct { func (x *Timing) Reset() { *x = Timing{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[41] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3777,7 +4022,7 @@ func (x *Timing) String() string { func (*Timing) ProtoMessage() {} func (x *Timing) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[41] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3790,7 +4035,7 @@ func (x *Timing) ProtoReflect() protoreflect.Message { // Deprecated: Use Timing.ProtoReflect.Descriptor instead. func (*Timing) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{41} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{45} } func (x *Timing) GetStart() *timestamppb.Timestamp { @@ -3852,7 +4097,7 @@ type CancelRequest struct { func (x *CancelRequest) Reset() { *x = CancelRequest{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[42] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3865,7 +4110,7 @@ func (x *CancelRequest) String() string { func (*CancelRequest) ProtoMessage() {} func (x *CancelRequest) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[42] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3878,7 +4123,7 @@ func (x *CancelRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CancelRequest.ProtoReflect.Descriptor instead. func (*CancelRequest) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{42} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{46} } type Request struct { @@ -3890,8 +4135,10 @@ type Request struct { // // *Request_Config // *Request_Parse + // *Request_Init // *Request_Plan // *Request_Apply + // *Request_Graph // *Request_Cancel Type isRequest_Type `protobuf_oneof:"type"` } @@ -3899,7 +4146,7 @@ type Request struct { func (x *Request) Reset() { *x = Request{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[43] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3912,7 +4159,7 @@ func (x *Request) String() string { func (*Request) ProtoMessage() {} func (x *Request) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[43] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3925,7 +4172,7 @@ func (x *Request) ProtoReflect() protoreflect.Message { // Deprecated: Use Request.ProtoReflect.Descriptor instead. func (*Request) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{43} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{47} } func (m *Request) GetType() isRequest_Type { @@ -3949,6 +4196,13 @@ func (x *Request) GetParse() *ParseRequest { return nil } +func (x *Request) GetInit() *InitRequest { + if x, ok := x.GetType().(*Request_Init); ok { + return x.Init + } + return nil +} + func (x *Request) GetPlan() *PlanRequest { if x, ok := x.GetType().(*Request_Plan); ok { return x.Plan @@ -3963,6 +4217,13 @@ func (x *Request) GetApply() *ApplyRequest { return nil } +func (x *Request) GetGraph() *GraphRequest { + if x, ok := x.GetType().(*Request_Graph); ok { + return x.Graph + } + return nil +} + func (x *Request) GetCancel() *CancelRequest { if x, ok := x.GetType().(*Request_Cancel); ok { return x.Cancel @@ -3982,26 +4243,38 @@ type Request_Parse struct { Parse *ParseRequest `protobuf:"bytes,2,opt,name=parse,proto3,oneof"` } +type Request_Init struct { + Init *InitRequest `protobuf:"bytes,3,opt,name=init,proto3,oneof"` +} + type Request_Plan struct { - Plan *PlanRequest `protobuf:"bytes,3,opt,name=plan,proto3,oneof"` + Plan *PlanRequest `protobuf:"bytes,4,opt,name=plan,proto3,oneof"` } type Request_Apply struct { - Apply *ApplyRequest `protobuf:"bytes,4,opt,name=apply,proto3,oneof"` + Apply *ApplyRequest `protobuf:"bytes,5,opt,name=apply,proto3,oneof"` +} + +type Request_Graph struct { + Graph *GraphRequest `protobuf:"bytes,6,opt,name=graph,proto3,oneof"` } type Request_Cancel struct { - Cancel *CancelRequest `protobuf:"bytes,5,opt,name=cancel,proto3,oneof"` + Cancel *CancelRequest `protobuf:"bytes,7,opt,name=cancel,proto3,oneof"` } func (*Request_Config) isRequest_Type() {} func (*Request_Parse) isRequest_Type() {} +func (*Request_Init) isRequest_Type() {} + func (*Request_Plan) isRequest_Type() {} func (*Request_Apply) isRequest_Type() {} +func (*Request_Graph) isRequest_Type() {} + func (*Request_Cancel) isRequest_Type() {} type Response struct { @@ -4013,8 +4286,10 @@ type Response struct { // // *Response_Log // *Response_Parse + // *Response_Init // *Response_Plan // *Response_Apply + // *Response_Graph // *Response_DataUpload // *Response_ChunkPiece Type isResponse_Type `protobuf_oneof:"type"` @@ -4023,7 +4298,7 @@ type Response struct { func (x *Response) Reset() { *x = Response{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[44] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4036,7 +4311,7 @@ func (x *Response) String() string { func (*Response) ProtoMessage() {} func (x *Response) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[44] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4049,7 +4324,7 @@ func (x *Response) ProtoReflect() protoreflect.Message { // Deprecated: Use Response.ProtoReflect.Descriptor instead. func (*Response) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{44} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{48} } func (m *Response) GetType() isResponse_Type { @@ -4073,6 +4348,13 @@ func (x *Response) GetParse() *ParseComplete { return nil } +func (x *Response) GetInit() *InitComplete { + if x, ok := x.GetType().(*Response_Init); ok { + return x.Init + } + return nil +} + func (x *Response) GetPlan() *PlanComplete { if x, ok := x.GetType().(*Response_Plan); ok { return x.Plan @@ -4087,6 +4369,13 @@ func (x *Response) GetApply() *ApplyComplete { return nil } +func (x *Response) GetGraph() *GraphComplete { + if x, ok := x.GetType().(*Response_Graph); ok { + return x.Graph + } + return nil +} + func (x *Response) GetDataUpload() *DataUpload { if x, ok := x.GetType().(*Response_DataUpload); ok { return x.DataUpload @@ -4113,30 +4402,42 @@ type Response_Parse struct { Parse *ParseComplete `protobuf:"bytes,2,opt,name=parse,proto3,oneof"` } +type Response_Init struct { + Init *InitComplete `protobuf:"bytes,3,opt,name=init,proto3,oneof"` +} + type Response_Plan struct { - Plan *PlanComplete `protobuf:"bytes,3,opt,name=plan,proto3,oneof"` + Plan *PlanComplete `protobuf:"bytes,4,opt,name=plan,proto3,oneof"` } type Response_Apply struct { - Apply *ApplyComplete `protobuf:"bytes,4,opt,name=apply,proto3,oneof"` + Apply *ApplyComplete `protobuf:"bytes,5,opt,name=apply,proto3,oneof"` +} + +type Response_Graph struct { + Graph *GraphComplete `protobuf:"bytes,6,opt,name=graph,proto3,oneof"` } type Response_DataUpload struct { - DataUpload *DataUpload `protobuf:"bytes,5,opt,name=data_upload,json=dataUpload,proto3,oneof"` + DataUpload *DataUpload `protobuf:"bytes,7,opt,name=data_upload,json=dataUpload,proto3,oneof"` } type Response_ChunkPiece struct { - ChunkPiece *ChunkPiece `protobuf:"bytes,6,opt,name=chunk_piece,json=chunkPiece,proto3,oneof"` + ChunkPiece *ChunkPiece `protobuf:"bytes,8,opt,name=chunk_piece,json=chunkPiece,proto3,oneof"` } func (*Response_Log) isResponse_Type() {} func (*Response_Parse) isResponse_Type() {} +func (*Response_Init) isResponse_Type() {} + func (*Response_Plan) isResponse_Type() {} func (*Response_Apply) isResponse_Type() {} +func (*Response_Graph) isResponse_Type() {} + func (*Response_DataUpload) isResponse_Type() {} func (*Response_ChunkPiece) isResponse_Type() {} @@ -4159,7 +4460,7 @@ type DataUpload struct { func (x *DataUpload) Reset() { *x = DataUpload{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[45] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4172,7 +4473,7 @@ func (x *DataUpload) String() string { func (*DataUpload) ProtoMessage() {} func (x *DataUpload) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[45] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4185,7 +4486,7 @@ func (x *DataUpload) ProtoReflect() protoreflect.Message { // Deprecated: Use DataUpload.ProtoReflect.Descriptor instead. func (*DataUpload) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{45} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{49} } func (x *DataUpload) GetUploadType() DataUploadType { @@ -4232,7 +4533,7 @@ type ChunkPiece struct { func (x *ChunkPiece) Reset() { *x = ChunkPiece{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[46] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4245,7 +4546,7 @@ func (x *ChunkPiece) String() string { func (*ChunkPiece) ProtoMessage() {} func (x *ChunkPiece) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[46] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4258,7 +4559,7 @@ func (x *ChunkPiece) ProtoReflect() protoreflect.Message { // Deprecated: Use ChunkPiece.ProtoReflect.Descriptor instead. func (*ChunkPiece) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{46} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{50} } func (x *ChunkPiece) GetData() []byte { @@ -4298,7 +4599,7 @@ type Agent_Metadata struct { func (x *Agent_Metadata) Reset() { *x = Agent_Metadata{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[47] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4311,7 +4612,7 @@ func (x *Agent_Metadata) String() string { func (*Agent_Metadata) ProtoMessage() {} func (x *Agent_Metadata) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[47] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4383,7 +4684,7 @@ type Resource_Metadata struct { func (x *Resource_Metadata) Reset() { *x = Resource_Metadata{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[49] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4396,7 +4697,7 @@ func (x *Resource_Metadata) String() string { func (*Resource_Metadata) ProtoMessage() {} func (x *Resource_Metadata) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[49] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4884,266 +5185,291 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{ 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x18, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0xf7, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x12, 0x36, 0x0a, 0x17, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x15, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x32, - 0x0a, 0x15, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x5f, 0x6c, 0x6f, - 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, - 0x65, 0x6c, 0x12, 0x24, 0x0a, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x69, - 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0a, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x13, 0x74, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x46, 0x0a, - 0x1d, 0x65, 0x78, 0x70, 0x5f, 0x72, 0x65, 0x75, 0x73, 0x65, 0x5f, 0x74, 0x65, 0x72, 0x72, 0x61, - 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x08, 0x48, 0x02, 0x52, 0x1a, 0x65, 0x78, 0x70, 0x52, 0x65, 0x75, 0x73, 0x65, - 0x54, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x5f, 0x69, 0x64, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x42, 0x20, 0x0a, - 0x1e, 0x5f, 0x65, 0x78, 0x70, 0x5f, 0x72, 0x65, 0x75, 0x73, 0x65, 0x5f, 0x74, 0x65, 0x72, 0x72, - 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, - 0x0e, 0x0a, 0x0c, 0x50, 0x61, 0x72, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0xa3, 0x02, 0x0a, 0x0d, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, - 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x4c, 0x0a, 0x12, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, - 0x6c, 0x65, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, - 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x12, 0x54, 0x0a, - 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, - 0x65, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x61, 0x67, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, - 0x61, 0x67, 0x73, 0x1a, 0x40, 0x0a, 0x12, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xbe, 0x03, 0x0a, 0x0b, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, - 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x53, 0x0a, 0x15, 0x72, 0x69, 0x63, 0x68, - 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, - 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x72, 0x69, 0x63, 0x68, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x43, 0x0a, - 0x0f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x0e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x12, 0x59, 0x0a, 0x17, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61, - 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x15, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x5b, 0x0a, - 0x19, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, - 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, + 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0xa9, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x5f, + 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x13, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x4c, + 0x65, 0x76, 0x65, 0x6c, 0x12, 0x24, 0x0a, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0a, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x13, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, + 0x46, 0x0a, 0x1d, 0x65, 0x78, 0x70, 0x5f, 0x72, 0x65, 0x75, 0x73, 0x65, 0x5f, 0x74, 0x65, 0x72, + 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x02, 0x52, 0x1a, 0x65, 0x78, 0x70, 0x52, 0x65, 0x75, + 0x73, 0x65, 0x54, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x57, 0x6f, 0x72, 0x6b, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x74, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x74, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x42, + 0x20, 0x0a, 0x1e, 0x5f, 0x65, 0x78, 0x70, 0x5f, 0x72, 0x65, 0x75, 0x73, 0x65, 0x5f, 0x74, 0x65, + 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x22, 0x0e, 0x0a, 0x0c, 0x50, 0x61, 0x72, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x22, 0xa3, 0x02, 0x0a, 0x0d, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, + 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x4c, 0x0a, 0x12, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, + 0x61, 0x62, 0x6c, 0x65, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, + 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x12, + 0x54, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x61, 0x67, + 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, + 0x65, 0x74, 0x65, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x61, 0x67, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x54, 0x61, 0x67, 0x73, 0x1a, 0x40, 0x0a, 0x12, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x71, 0x0a, 0x0b, 0x49, 0x6e, 0x69, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x17, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x15, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0x2a, + 0x0a, 0x11, 0x6f, 0x6d, 0x69, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x66, 0x69, + 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6f, 0x6d, 0x69, 0x74, 0x4d, + 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x22, 0xd1, 0x01, 0x0a, 0x0c, 0x49, + 0x6e, 0x69, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x12, 0x2d, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, + 0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, + 0x12, 0x2d, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, + 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, + 0x21, 0x0a, 0x0c, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x69, 0x6c, + 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x6c, + 0x65, 0x73, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x6d, + 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x48, 0x61, 0x73, 0x68, 0x22, 0xa8, + 0x03, 0x0a, 0x0b, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, + 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x53, 0x0a, 0x15, 0x72, 0x69, 0x63, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, + 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x52, 0x17, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6f, 0x6d, - 0x69, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6f, 0x6d, 0x69, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, - 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x22, 0xc1, 0x05, 0x0a, 0x0c, 0x50, 0x6c, 0x61, 0x6e, 0x43, - 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x33, 0x0a, - 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x65, 0x52, 0x13, 0x72, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, + 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, + 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x76, 0x61, 0x72, + 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x59, 0x0a, 0x17, 0x65, + 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, + 0x15, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x5b, 0x0a, 0x19, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, + 0x75, 0x73, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x17, 0x70, 0x72, 0x65, 0x76, + 0x69, 0x6f, 0x75, 0x73, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x80, 0x02, 0x0a, 0x0c, 0x50, 0x6c, + 0x61, 0x6e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x12, 0x2d, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, + 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x12, + 0x12, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x70, + 0x6c, 0x61, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x43, 0x6f, 0x73, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x43, 0x6f, 0x73, + 0x74, 0x12, 0x55, 0x0a, 0x15, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, + 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x52, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, + 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x61, 0x69, 0x5f, 0x74, + 0x61, 0x73, 0x6b, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x0b, 0x61, 0x69, 0x54, 0x61, 0x73, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x57, 0x0a, 0x0c, + 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x08, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, + 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x6a, 0x0a, 0x0d, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x6f, + 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x12, 0x2d, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x72, 0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, + 0x73, 0x22, 0x73, 0x0a, 0x0c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x12, 0x30, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x06, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0xd9, 0x03, 0x0a, 0x0d, 0x47, 0x72, 0x61, 0x70, 0x68, + 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x2d, + 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x69, + 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x33, 0x0a, + 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x61, 0x0a, 0x17, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x15, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x06, 0x20, 0x03, + 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, - 0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, - 0x12, 0x2d, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, - 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, - 0x2d, 0x0a, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, - 0x72, 0x65, 0x73, 0x65, 0x74, 0x52, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x74, 0x73, 0x12, 0x12, - 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x70, 0x6c, - 0x61, 0x6e, 0x12, 0x55, 0x0a, 0x15, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, - 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x52, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x70, - 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x11, - 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x68, 0x61, 0x73, - 0x68, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, - 0x69, 0x6c, 0x65, 0x73, 0x48, 0x61, 0x73, 0x68, 0x12, 0x20, 0x0a, 0x0c, 0x68, 0x61, 0x73, 0x5f, - 0x61, 0x69, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, - 0x68, 0x61, 0x73, 0x41, 0x69, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x2e, 0x0a, 0x08, 0x61, 0x69, - 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x49, 0x54, 0x61, 0x73, - 0x6b, 0x52, 0x07, 0x61, 0x69, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x68, 0x61, - 0x73, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x68, 0x61, 0x73, 0x45, 0x78, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x41, 0x0a, 0x0c, 0x41, 0x70, - 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0xee, 0x02, - 0x0a, 0x0d, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, - 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x33, 0x0a, 0x09, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, - 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, - 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x61, 0x0a, 0x17, - 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x45, 0x78, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x15, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, - 0x2d, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, - 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x2e, - 0x0a, 0x08, 0x61, 0x69, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, - 0x49, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x07, 0x61, 0x69, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x22, 0xfa, - 0x01, 0x0a, 0x06, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x12, 0x30, 0x0a, 0x05, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2c, 0x0a, 0x03, 0x65, - 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x12, 0x2e, 0x0a, 0x05, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x0f, 0x0a, 0x0d, 0x43, - 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8c, 0x02, 0x0a, - 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, - 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x31, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x48, 0x00, 0x52, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x70, 0x6c, - 0x61, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x31, 0x0a, 0x05, 0x61, 0x70, - 0x70, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x12, 0x34, 0x0a, - 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x43, 0x61, 0x6e, 0x63, - 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xc9, 0x02, 0x0a, 0x08, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x48, 0x00, 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x12, 0x32, - 0x0a, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, - 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x05, 0x70, 0x61, 0x72, - 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, - 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x04, 0x70, - 0x6c, 0x61, 0x6e, 0x12, 0x32, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, - 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, - 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x12, 0x3a, 0x0a, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x5f, - 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x55, - 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x00, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x6c, - 0x6f, 0x61, 0x64, 0x12, 0x3a, 0x0a, 0x0b, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x70, 0x69, 0x65, - 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x50, 0x69, 0x65, 0x63, - 0x65, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x50, 0x69, 0x65, 0x63, 0x65, 0x42, - 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x9c, 0x01, 0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, - 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x3c, 0x0a, 0x0b, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x55, 0x70, - 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x68, 0x61, 0x73, - 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x48, 0x61, 0x73, - 0x68, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x16, - 0x0a, 0x06, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, - 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x22, 0x67, 0x0a, 0x0a, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x50, - 0x69, 0x65, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x24, 0x0a, 0x0e, 0x66, 0x75, 0x6c, 0x6c, - 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x0c, 0x66, 0x75, 0x6c, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1f, - 0x0a, 0x0b, 0x70, 0x69, 0x65, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0a, 0x70, 0x69, 0x65, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x2a, - 0xa8, 0x01, 0x0a, 0x11, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x46, 0x6f, 0x72, - 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, - 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x4d, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, - 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x41, 0x44, 0x49, 0x4f, 0x10, 0x02, 0x12, 0x0c, 0x0a, - 0x08, 0x44, 0x52, 0x4f, 0x50, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x49, - 0x4e, 0x50, 0x55, 0x54, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x54, 0x45, 0x58, 0x54, 0x41, 0x52, - 0x45, 0x41, 0x10, 0x05, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x4c, 0x49, 0x44, 0x45, 0x52, 0x10, 0x06, - 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x42, 0x4f, 0x58, 0x10, 0x07, 0x12, 0x0a, - 0x0a, 0x06, 0x53, 0x57, 0x49, 0x54, 0x43, 0x48, 0x10, 0x08, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x41, - 0x47, 0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 0x10, 0x09, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x55, 0x4c, - 0x54, 0x49, 0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 0x10, 0x0a, 0x2a, 0x3f, 0x0a, 0x08, 0x4c, 0x6f, - 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, - 0x00, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, - 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x03, - 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x2a, 0x3b, 0x0a, 0x0f, 0x41, - 0x70, 0x70, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, - 0x0a, 0x05, 0x4f, 0x57, 0x4e, 0x45, 0x52, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x55, 0x54, - 0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, - 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x02, 0x2a, 0x35, 0x0a, 0x09, 0x41, 0x70, 0x70, 0x4f, - 0x70, 0x65, 0x6e, 0x49, 0x6e, 0x12, 0x0e, 0x0a, 0x06, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x10, - 0x00, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x4c, 0x49, 0x4d, 0x5f, 0x57, 0x49, - 0x4e, 0x44, 0x4f, 0x57, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x41, 0x42, 0x10, 0x02, 0x2a, - 0x37, 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, - 0x00, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x54, 0x4f, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x44, - 0x45, 0x53, 0x54, 0x52, 0x4f, 0x59, 0x10, 0x02, 0x2a, 0x3e, 0x0a, 0x1b, 0x50, 0x72, 0x65, 0x62, - 0x75, 0x69, 0x6c, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, - 0x6c, 0x64, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, - 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x01, 0x12, 0x09, 0x0a, - 0x05, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x10, 0x02, 0x2a, 0x35, 0x0a, 0x0b, 0x54, 0x69, 0x6d, 0x69, - 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x41, 0x52, 0x54, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, - 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x2a, - 0x47, 0x0a, 0x0e, 0x44, 0x61, 0x74, 0x61, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x17, 0x0a, 0x13, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x55, 0x50, - 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x55, 0x4c, 0x45, - 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x10, 0x01, 0x32, 0x49, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x3a, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, - 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, - 0x01, 0x30, 0x01, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, - 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x64, 0x6b, 0x2f, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x50, 0x72, 0x65, 0x73, 0x65, 0x74, 0x52, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x74, 0x73, + 0x12, 0x20, 0x0a, 0x0c, 0x68, 0x61, 0x73, 0x5f, 0x61, 0x69, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x73, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x68, 0x61, 0x73, 0x41, 0x69, 0x54, 0x61, 0x73, + 0x6b, 0x73, 0x12, 0x2e, 0x0a, 0x08, 0x61, 0x69, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x08, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x2e, 0x41, 0x49, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x07, 0x61, 0x69, 0x54, 0x61, 0x73, + 0x6b, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x68, 0x61, 0x73, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x11, 0x68, 0x61, 0x73, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x67, 0x65, 0x6e, + 0x74, 0x73, 0x22, 0xfa, 0x01, 0x0a, 0x06, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x12, 0x30, 0x0a, + 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, + 0x2c, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x16, 0x0a, + 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1a, 0x0a, + 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, + 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x12, + 0x2e, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x69, 0x6d, + 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, + 0x0f, 0x0a, 0x0d, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x22, 0xef, 0x02, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x06, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x48, 0x00, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x31, 0x0a, 0x05, 0x70, + 0x61, 0x72, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x12, 0x2e, + 0x0a, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x12, 0x2e, + 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x31, + 0x0a, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x6c, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, + 0x79, 0x12, 0x31, 0x0a, 0x05, 0x67, 0x72, 0x61, 0x70, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x47, + 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x67, + 0x72, 0x61, 0x70, 0x68, 0x12, 0x34, 0x0a, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x22, 0xae, 0x03, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x24, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x48, 0x00, + 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x12, 0x32, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, + 0x48, 0x00, 0x52, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x69, 0x6e, 0x69, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, + 0x74, 0x65, 0x48, 0x00, 0x52, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x12, 0x2f, 0x0a, 0x04, 0x70, 0x6c, + 0x61, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, + 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x32, 0x0a, 0x05, 0x61, + 0x70, 0x70, 0x6c, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x6f, + 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x12, + 0x32, 0x0a, 0x05, 0x67, 0x72, 0x61, 0x70, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x47, 0x72, 0x61, + 0x70, 0x68, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x05, 0x67, 0x72, + 0x61, 0x70, 0x68, 0x12, 0x3a, 0x0a, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x75, 0x70, 0x6c, 0x6f, + 0x61, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x55, 0x70, 0x6c, 0x6f, 0x61, + 0x64, 0x48, 0x00, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, + 0x3a, 0x0a, 0x0b, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x70, 0x69, 0x65, 0x63, 0x65, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x2e, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x50, 0x69, 0x65, 0x63, 0x65, 0x48, 0x00, 0x52, + 0x0a, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x50, 0x69, 0x65, 0x63, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x22, 0x9c, 0x01, 0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x55, 0x70, 0x6c, 0x6f, + 0x61, 0x64, 0x12, 0x3c, 0x0a, 0x0b, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, + 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1b, 0x0a, + 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x68, + 0x75, 0x6e, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x63, 0x68, 0x75, 0x6e, + 0x6b, 0x73, 0x22, 0x67, 0x0a, 0x0a, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x50, 0x69, 0x65, 0x63, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, + 0x64, 0x61, 0x74, 0x61, 0x12, 0x24, 0x0a, 0x0e, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x64, 0x61, 0x74, + 0x61, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x66, 0x75, + 0x6c, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x69, + 0x65, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x0a, 0x70, 0x69, 0x65, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x2a, 0xa8, 0x01, 0x0a, 0x11, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x46, 0x6f, 0x72, 0x6d, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x0e, + 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x4d, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x09, + 0x0a, 0x05, 0x52, 0x41, 0x44, 0x49, 0x4f, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x52, 0x4f, + 0x50, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x49, 0x4e, 0x50, 0x55, 0x54, + 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x54, 0x45, 0x58, 0x54, 0x41, 0x52, 0x45, 0x41, 0x10, 0x05, + 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x4c, 0x49, 0x44, 0x45, 0x52, 0x10, 0x06, 0x12, 0x0c, 0x0a, 0x08, + 0x43, 0x48, 0x45, 0x43, 0x4b, 0x42, 0x4f, 0x58, 0x10, 0x07, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x57, + 0x49, 0x54, 0x43, 0x48, 0x10, 0x08, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x41, 0x47, 0x53, 0x45, 0x4c, + 0x45, 0x43, 0x54, 0x10, 0x09, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x53, 0x45, + 0x4c, 0x45, 0x43, 0x54, 0x10, 0x0a, 0x2a, 0x3f, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, + 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x00, 0x12, 0x09, 0x0a, + 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, + 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, + 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x2a, 0x3b, 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x53, 0x68, + 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x57, + 0x4e, 0x45, 0x52, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, + 0x49, 0x43, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c, + 0x49, 0x43, 0x10, 0x02, 0x2a, 0x35, 0x0a, 0x09, 0x41, 0x70, 0x70, 0x4f, 0x70, 0x65, 0x6e, 0x49, + 0x6e, 0x12, 0x0e, 0x0a, 0x06, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x10, 0x00, 0x1a, 0x02, 0x08, + 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x4c, 0x49, 0x4d, 0x5f, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, + 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x41, 0x42, 0x10, 0x02, 0x2a, 0x37, 0x0a, 0x13, 0x57, + 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, + 0x04, 0x53, 0x54, 0x4f, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x53, 0x54, 0x52, + 0x4f, 0x59, 0x10, 0x02, 0x2a, 0x3e, 0x0a, 0x1b, 0x50, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x74, + 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, + 0x61, 0x67, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, + 0x06, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x43, 0x4c, 0x41, + 0x49, 0x4d, 0x10, 0x02, 0x2a, 0x44, 0x0a, 0x0b, 0x47, 0x72, 0x61, 0x70, 0x68, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x55, 0x4e, + 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x4f, 0x55, 0x52, 0x43, + 0x45, 0x5f, 0x50, 0x4c, 0x41, 0x4e, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x4f, 0x55, 0x52, + 0x43, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x10, 0x02, 0x2a, 0x35, 0x0a, 0x0b, 0x54, 0x69, + 0x6d, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x41, + 0x52, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, + 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, + 0x02, 0x2a, 0x47, 0x0a, 0x0e, 0x44, 0x61, 0x74, 0x61, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x13, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, + 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x55, + 0x4c, 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x10, 0x01, 0x32, 0x49, 0x0a, 0x0b, 0x50, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x3a, 0x0a, 0x07, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x28, 0x01, 0x30, 0x01, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, + 0x76, 0x32, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x64, + 0x6b, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -5158,8 +5484,8 @@ func file_provisionersdk_proto_provisioner_proto_rawDescGZIP() []byte { return file_provisionersdk_proto_provisioner_proto_rawDescData } -var file_provisionersdk_proto_provisioner_proto_enumTypes = make([]protoimpl.EnumInfo, 8) -var file_provisionersdk_proto_provisioner_proto_msgTypes = make([]protoimpl.MessageInfo, 51) +var file_provisionersdk_proto_provisioner_proto_enumTypes = make([]protoimpl.EnumInfo, 9) +var file_provisionersdk_proto_provisioner_proto_msgTypes = make([]protoimpl.MessageInfo, 55) var file_provisionersdk_proto_provisioner_proto_goTypes = []interface{}{ (ParameterFormType)(0), // 0: provisioner.ParameterFormType (LogLevel)(0), // 1: provisioner.LogLevel @@ -5167,133 +5493,142 @@ var file_provisionersdk_proto_provisioner_proto_goTypes = []interface{}{ (AppOpenIn)(0), // 3: provisioner.AppOpenIn (WorkspaceTransition)(0), // 4: provisioner.WorkspaceTransition (PrebuiltWorkspaceBuildStage)(0), // 5: provisioner.PrebuiltWorkspaceBuildStage - (TimingState)(0), // 6: provisioner.TimingState - (DataUploadType)(0), // 7: provisioner.DataUploadType - (*Empty)(nil), // 8: provisioner.Empty - (*TemplateVariable)(nil), // 9: provisioner.TemplateVariable - (*RichParameterOption)(nil), // 10: provisioner.RichParameterOption - (*RichParameter)(nil), // 11: provisioner.RichParameter - (*RichParameterValue)(nil), // 12: provisioner.RichParameterValue - (*ExpirationPolicy)(nil), // 13: provisioner.ExpirationPolicy - (*Schedule)(nil), // 14: provisioner.Schedule - (*Scheduling)(nil), // 15: provisioner.Scheduling - (*Prebuild)(nil), // 16: provisioner.Prebuild - (*Preset)(nil), // 17: provisioner.Preset - (*PresetParameter)(nil), // 18: provisioner.PresetParameter - (*ResourceReplacement)(nil), // 19: provisioner.ResourceReplacement - (*VariableValue)(nil), // 20: provisioner.VariableValue - (*Log)(nil), // 21: provisioner.Log - (*InstanceIdentityAuth)(nil), // 22: provisioner.InstanceIdentityAuth - (*ExternalAuthProviderResource)(nil), // 23: provisioner.ExternalAuthProviderResource - (*ExternalAuthProvider)(nil), // 24: provisioner.ExternalAuthProvider - (*Agent)(nil), // 25: provisioner.Agent - (*ResourcesMonitoring)(nil), // 26: provisioner.ResourcesMonitoring - (*MemoryResourceMonitor)(nil), // 27: provisioner.MemoryResourceMonitor - (*VolumeResourceMonitor)(nil), // 28: provisioner.VolumeResourceMonitor - (*DisplayApps)(nil), // 29: provisioner.DisplayApps - (*Env)(nil), // 30: provisioner.Env - (*Script)(nil), // 31: provisioner.Script - (*Devcontainer)(nil), // 32: provisioner.Devcontainer - (*App)(nil), // 33: provisioner.App - (*Healthcheck)(nil), // 34: provisioner.Healthcheck - (*Resource)(nil), // 35: provisioner.Resource - (*Module)(nil), // 36: provisioner.Module - (*Role)(nil), // 37: provisioner.Role - (*RunningAgentAuthToken)(nil), // 38: provisioner.RunningAgentAuthToken - (*AITaskSidebarApp)(nil), // 39: provisioner.AITaskSidebarApp - (*AITask)(nil), // 40: provisioner.AITask - (*Metadata)(nil), // 41: provisioner.Metadata - (*Config)(nil), // 42: provisioner.Config - (*ParseRequest)(nil), // 43: provisioner.ParseRequest - (*ParseComplete)(nil), // 44: provisioner.ParseComplete - (*PlanRequest)(nil), // 45: provisioner.PlanRequest - (*PlanComplete)(nil), // 46: provisioner.PlanComplete - (*ApplyRequest)(nil), // 47: provisioner.ApplyRequest - (*ApplyComplete)(nil), // 48: provisioner.ApplyComplete - (*Timing)(nil), // 49: provisioner.Timing - (*CancelRequest)(nil), // 50: provisioner.CancelRequest - (*Request)(nil), // 51: provisioner.Request - (*Response)(nil), // 52: provisioner.Response - (*DataUpload)(nil), // 53: provisioner.DataUpload - (*ChunkPiece)(nil), // 54: provisioner.ChunkPiece - (*Agent_Metadata)(nil), // 55: provisioner.Agent.Metadata - nil, // 56: provisioner.Agent.EnvEntry - (*Resource_Metadata)(nil), // 57: provisioner.Resource.Metadata - nil, // 58: provisioner.ParseComplete.WorkspaceTagsEntry - (*timestamppb.Timestamp)(nil), // 59: google.protobuf.Timestamp + (GraphSource)(0), // 6: provisioner.GraphSource + (TimingState)(0), // 7: provisioner.TimingState + (DataUploadType)(0), // 8: provisioner.DataUploadType + (*Empty)(nil), // 9: provisioner.Empty + (*TemplateVariable)(nil), // 10: provisioner.TemplateVariable + (*RichParameterOption)(nil), // 11: provisioner.RichParameterOption + (*RichParameter)(nil), // 12: provisioner.RichParameter + (*RichParameterValue)(nil), // 13: provisioner.RichParameterValue + (*ExpirationPolicy)(nil), // 14: provisioner.ExpirationPolicy + (*Schedule)(nil), // 15: provisioner.Schedule + (*Scheduling)(nil), // 16: provisioner.Scheduling + (*Prebuild)(nil), // 17: provisioner.Prebuild + (*Preset)(nil), // 18: provisioner.Preset + (*PresetParameter)(nil), // 19: provisioner.PresetParameter + (*ResourceReplacement)(nil), // 20: provisioner.ResourceReplacement + (*VariableValue)(nil), // 21: provisioner.VariableValue + (*Log)(nil), // 22: provisioner.Log + (*InstanceIdentityAuth)(nil), // 23: provisioner.InstanceIdentityAuth + (*ExternalAuthProviderResource)(nil), // 24: provisioner.ExternalAuthProviderResource + (*ExternalAuthProvider)(nil), // 25: provisioner.ExternalAuthProvider + (*Agent)(nil), // 26: provisioner.Agent + (*ResourcesMonitoring)(nil), // 27: provisioner.ResourcesMonitoring + (*MemoryResourceMonitor)(nil), // 28: provisioner.MemoryResourceMonitor + (*VolumeResourceMonitor)(nil), // 29: provisioner.VolumeResourceMonitor + (*DisplayApps)(nil), // 30: provisioner.DisplayApps + (*Env)(nil), // 31: provisioner.Env + (*Script)(nil), // 32: provisioner.Script + (*Devcontainer)(nil), // 33: provisioner.Devcontainer + (*App)(nil), // 34: provisioner.App + (*Healthcheck)(nil), // 35: provisioner.Healthcheck + (*Resource)(nil), // 36: provisioner.Resource + (*Module)(nil), // 37: provisioner.Module + (*Role)(nil), // 38: provisioner.Role + (*RunningAgentAuthToken)(nil), // 39: provisioner.RunningAgentAuthToken + (*AITaskSidebarApp)(nil), // 40: provisioner.AITaskSidebarApp + (*AITask)(nil), // 41: provisioner.AITask + (*Metadata)(nil), // 42: provisioner.Metadata + (*Config)(nil), // 43: provisioner.Config + (*ParseRequest)(nil), // 44: provisioner.ParseRequest + (*ParseComplete)(nil), // 45: provisioner.ParseComplete + (*InitRequest)(nil), // 46: provisioner.InitRequest + (*InitComplete)(nil), // 47: provisioner.InitComplete + (*PlanRequest)(nil), // 48: provisioner.PlanRequest + (*PlanComplete)(nil), // 49: provisioner.PlanComplete + (*ApplyRequest)(nil), // 50: provisioner.ApplyRequest + (*ApplyComplete)(nil), // 51: provisioner.ApplyComplete + (*GraphRequest)(nil), // 52: provisioner.GraphRequest + (*GraphComplete)(nil), // 53: provisioner.GraphComplete + (*Timing)(nil), // 54: provisioner.Timing + (*CancelRequest)(nil), // 55: provisioner.CancelRequest + (*Request)(nil), // 56: provisioner.Request + (*Response)(nil), // 57: provisioner.Response + (*DataUpload)(nil), // 58: provisioner.DataUpload + (*ChunkPiece)(nil), // 59: provisioner.ChunkPiece + (*Agent_Metadata)(nil), // 60: provisioner.Agent.Metadata + nil, // 61: provisioner.Agent.EnvEntry + (*Resource_Metadata)(nil), // 62: provisioner.Resource.Metadata + nil, // 63: provisioner.ParseComplete.WorkspaceTagsEntry + (*timestamppb.Timestamp)(nil), // 64: google.protobuf.Timestamp } var file_provisionersdk_proto_provisioner_proto_depIdxs = []int32{ - 10, // 0: provisioner.RichParameter.options:type_name -> provisioner.RichParameterOption + 11, // 0: provisioner.RichParameter.options:type_name -> provisioner.RichParameterOption 0, // 1: provisioner.RichParameter.form_type:type_name -> provisioner.ParameterFormType - 14, // 2: provisioner.Scheduling.schedule:type_name -> provisioner.Schedule - 13, // 3: provisioner.Prebuild.expiration_policy:type_name -> provisioner.ExpirationPolicy - 15, // 4: provisioner.Prebuild.scheduling:type_name -> provisioner.Scheduling - 18, // 5: provisioner.Preset.parameters:type_name -> provisioner.PresetParameter - 16, // 6: provisioner.Preset.prebuild:type_name -> provisioner.Prebuild + 15, // 2: provisioner.Scheduling.schedule:type_name -> provisioner.Schedule + 14, // 3: provisioner.Prebuild.expiration_policy:type_name -> provisioner.ExpirationPolicy + 16, // 4: provisioner.Prebuild.scheduling:type_name -> provisioner.Scheduling + 19, // 5: provisioner.Preset.parameters:type_name -> provisioner.PresetParameter + 17, // 6: provisioner.Preset.prebuild:type_name -> provisioner.Prebuild 1, // 7: provisioner.Log.level:type_name -> provisioner.LogLevel - 56, // 8: provisioner.Agent.env:type_name -> provisioner.Agent.EnvEntry - 33, // 9: provisioner.Agent.apps:type_name -> provisioner.App - 55, // 10: provisioner.Agent.metadata:type_name -> provisioner.Agent.Metadata - 29, // 11: provisioner.Agent.display_apps:type_name -> provisioner.DisplayApps - 31, // 12: provisioner.Agent.scripts:type_name -> provisioner.Script - 30, // 13: provisioner.Agent.extra_envs:type_name -> provisioner.Env - 26, // 14: provisioner.Agent.resources_monitoring:type_name -> provisioner.ResourcesMonitoring - 32, // 15: provisioner.Agent.devcontainers:type_name -> provisioner.Devcontainer - 27, // 16: provisioner.ResourcesMonitoring.memory:type_name -> provisioner.MemoryResourceMonitor - 28, // 17: provisioner.ResourcesMonitoring.volumes:type_name -> provisioner.VolumeResourceMonitor - 34, // 18: provisioner.App.healthcheck:type_name -> provisioner.Healthcheck + 61, // 8: provisioner.Agent.env:type_name -> provisioner.Agent.EnvEntry + 34, // 9: provisioner.Agent.apps:type_name -> provisioner.App + 60, // 10: provisioner.Agent.metadata:type_name -> provisioner.Agent.Metadata + 30, // 11: provisioner.Agent.display_apps:type_name -> provisioner.DisplayApps + 32, // 12: provisioner.Agent.scripts:type_name -> provisioner.Script + 31, // 13: provisioner.Agent.extra_envs:type_name -> provisioner.Env + 27, // 14: provisioner.Agent.resources_monitoring:type_name -> provisioner.ResourcesMonitoring + 33, // 15: provisioner.Agent.devcontainers:type_name -> provisioner.Devcontainer + 28, // 16: provisioner.ResourcesMonitoring.memory:type_name -> provisioner.MemoryResourceMonitor + 29, // 17: provisioner.ResourcesMonitoring.volumes:type_name -> provisioner.VolumeResourceMonitor + 35, // 18: provisioner.App.healthcheck:type_name -> provisioner.Healthcheck 2, // 19: provisioner.App.sharing_level:type_name -> provisioner.AppSharingLevel 3, // 20: provisioner.App.open_in:type_name -> provisioner.AppOpenIn - 25, // 21: provisioner.Resource.agents:type_name -> provisioner.Agent - 57, // 22: provisioner.Resource.metadata:type_name -> provisioner.Resource.Metadata - 39, // 23: provisioner.AITask.sidebar_app:type_name -> provisioner.AITaskSidebarApp + 26, // 21: provisioner.Resource.agents:type_name -> provisioner.Agent + 62, // 22: provisioner.Resource.metadata:type_name -> provisioner.Resource.Metadata + 40, // 23: provisioner.AITask.sidebar_app:type_name -> provisioner.AITaskSidebarApp 4, // 24: provisioner.Metadata.workspace_transition:type_name -> provisioner.WorkspaceTransition - 37, // 25: provisioner.Metadata.workspace_owner_rbac_roles:type_name -> provisioner.Role + 38, // 25: provisioner.Metadata.workspace_owner_rbac_roles:type_name -> provisioner.Role 5, // 26: provisioner.Metadata.prebuilt_workspace_build_stage:type_name -> provisioner.PrebuiltWorkspaceBuildStage - 38, // 27: provisioner.Metadata.running_agent_auth_tokens:type_name -> provisioner.RunningAgentAuthToken - 9, // 28: provisioner.ParseComplete.template_variables:type_name -> provisioner.TemplateVariable - 58, // 29: provisioner.ParseComplete.workspace_tags:type_name -> provisioner.ParseComplete.WorkspaceTagsEntry - 41, // 30: provisioner.PlanRequest.metadata:type_name -> provisioner.Metadata - 12, // 31: provisioner.PlanRequest.rich_parameter_values:type_name -> provisioner.RichParameterValue - 20, // 32: provisioner.PlanRequest.variable_values:type_name -> provisioner.VariableValue - 24, // 33: provisioner.PlanRequest.external_auth_providers:type_name -> provisioner.ExternalAuthProvider - 12, // 34: provisioner.PlanRequest.previous_parameter_values:type_name -> provisioner.RichParameterValue - 35, // 35: provisioner.PlanComplete.resources:type_name -> provisioner.Resource - 11, // 36: provisioner.PlanComplete.parameters:type_name -> provisioner.RichParameter - 23, // 37: provisioner.PlanComplete.external_auth_providers:type_name -> provisioner.ExternalAuthProviderResource - 49, // 38: provisioner.PlanComplete.timings:type_name -> provisioner.Timing - 36, // 39: provisioner.PlanComplete.modules:type_name -> provisioner.Module - 17, // 40: provisioner.PlanComplete.presets:type_name -> provisioner.Preset - 19, // 41: provisioner.PlanComplete.resource_replacements:type_name -> provisioner.ResourceReplacement - 40, // 42: provisioner.PlanComplete.ai_tasks:type_name -> provisioner.AITask - 41, // 43: provisioner.ApplyRequest.metadata:type_name -> provisioner.Metadata - 35, // 44: provisioner.ApplyComplete.resources:type_name -> provisioner.Resource - 11, // 45: provisioner.ApplyComplete.parameters:type_name -> provisioner.RichParameter - 23, // 46: provisioner.ApplyComplete.external_auth_providers:type_name -> provisioner.ExternalAuthProviderResource - 49, // 47: provisioner.ApplyComplete.timings:type_name -> provisioner.Timing - 40, // 48: provisioner.ApplyComplete.ai_tasks:type_name -> provisioner.AITask - 59, // 49: provisioner.Timing.start:type_name -> google.protobuf.Timestamp - 59, // 50: provisioner.Timing.end:type_name -> google.protobuf.Timestamp - 6, // 51: provisioner.Timing.state:type_name -> provisioner.TimingState - 42, // 52: provisioner.Request.config:type_name -> provisioner.Config - 43, // 53: provisioner.Request.parse:type_name -> provisioner.ParseRequest - 45, // 54: provisioner.Request.plan:type_name -> provisioner.PlanRequest - 47, // 55: provisioner.Request.apply:type_name -> provisioner.ApplyRequest - 50, // 56: provisioner.Request.cancel:type_name -> provisioner.CancelRequest - 21, // 57: provisioner.Response.log:type_name -> provisioner.Log - 44, // 58: provisioner.Response.parse:type_name -> provisioner.ParseComplete - 46, // 59: provisioner.Response.plan:type_name -> provisioner.PlanComplete - 48, // 60: provisioner.Response.apply:type_name -> provisioner.ApplyComplete - 53, // 61: provisioner.Response.data_upload:type_name -> provisioner.DataUpload - 54, // 62: provisioner.Response.chunk_piece:type_name -> provisioner.ChunkPiece - 7, // 63: provisioner.DataUpload.upload_type:type_name -> provisioner.DataUploadType - 51, // 64: provisioner.Provisioner.Session:input_type -> provisioner.Request - 52, // 65: provisioner.Provisioner.Session:output_type -> provisioner.Response - 65, // [65:66] is the sub-list for method output_type - 64, // [64:65] is the sub-list for method input_type - 64, // [64:64] is the sub-list for extension type_name - 64, // [64:64] is the sub-list for extension extendee - 0, // [0:64] is the sub-list for field type_name + 39, // 27: provisioner.Metadata.running_agent_auth_tokens:type_name -> provisioner.RunningAgentAuthToken + 10, // 28: provisioner.ParseComplete.template_variables:type_name -> provisioner.TemplateVariable + 63, // 29: provisioner.ParseComplete.workspace_tags:type_name -> provisioner.ParseComplete.WorkspaceTagsEntry + 54, // 30: provisioner.InitComplete.timings:type_name -> provisioner.Timing + 37, // 31: provisioner.InitComplete.modules:type_name -> provisioner.Module + 42, // 32: provisioner.PlanRequest.metadata:type_name -> provisioner.Metadata + 13, // 33: provisioner.PlanRequest.rich_parameter_values:type_name -> provisioner.RichParameterValue + 21, // 34: provisioner.PlanRequest.variable_values:type_name -> provisioner.VariableValue + 25, // 35: provisioner.PlanRequest.external_auth_providers:type_name -> provisioner.ExternalAuthProvider + 13, // 36: provisioner.PlanRequest.previous_parameter_values:type_name -> provisioner.RichParameterValue + 54, // 37: provisioner.PlanComplete.timings:type_name -> provisioner.Timing + 20, // 38: provisioner.PlanComplete.resource_replacements:type_name -> provisioner.ResourceReplacement + 42, // 39: provisioner.ApplyRequest.metadata:type_name -> provisioner.Metadata + 54, // 40: provisioner.ApplyComplete.timings:type_name -> provisioner.Timing + 42, // 41: provisioner.GraphRequest.metadata:type_name -> provisioner.Metadata + 6, // 42: provisioner.GraphRequest.source:type_name -> provisioner.GraphSource + 54, // 43: provisioner.GraphComplete.timings:type_name -> provisioner.Timing + 36, // 44: provisioner.GraphComplete.resources:type_name -> provisioner.Resource + 12, // 45: provisioner.GraphComplete.parameters:type_name -> provisioner.RichParameter + 24, // 46: provisioner.GraphComplete.external_auth_providers:type_name -> provisioner.ExternalAuthProviderResource + 18, // 47: provisioner.GraphComplete.presets:type_name -> provisioner.Preset + 41, // 48: provisioner.GraphComplete.ai_tasks:type_name -> provisioner.AITask + 64, // 49: provisioner.Timing.start:type_name -> google.protobuf.Timestamp + 64, // 50: provisioner.Timing.end:type_name -> google.protobuf.Timestamp + 7, // 51: provisioner.Timing.state:type_name -> provisioner.TimingState + 43, // 52: provisioner.Request.config:type_name -> provisioner.Config + 44, // 53: provisioner.Request.parse:type_name -> provisioner.ParseRequest + 46, // 54: provisioner.Request.init:type_name -> provisioner.InitRequest + 48, // 55: provisioner.Request.plan:type_name -> provisioner.PlanRequest + 50, // 56: provisioner.Request.apply:type_name -> provisioner.ApplyRequest + 52, // 57: provisioner.Request.graph:type_name -> provisioner.GraphRequest + 55, // 58: provisioner.Request.cancel:type_name -> provisioner.CancelRequest + 22, // 59: provisioner.Response.log:type_name -> provisioner.Log + 45, // 60: provisioner.Response.parse:type_name -> provisioner.ParseComplete + 47, // 61: provisioner.Response.init:type_name -> provisioner.InitComplete + 49, // 62: provisioner.Response.plan:type_name -> provisioner.PlanComplete + 51, // 63: provisioner.Response.apply:type_name -> provisioner.ApplyComplete + 53, // 64: provisioner.Response.graph:type_name -> provisioner.GraphComplete + 58, // 65: provisioner.Response.data_upload:type_name -> provisioner.DataUpload + 59, // 66: provisioner.Response.chunk_piece:type_name -> provisioner.ChunkPiece + 8, // 67: provisioner.DataUpload.upload_type:type_name -> provisioner.DataUploadType + 56, // 68: provisioner.Provisioner.Session:input_type -> provisioner.Request + 57, // 69: provisioner.Provisioner.Session:output_type -> provisioner.Response + 69, // [69:70] is the sub-list for method output_type + 68, // [68:69] is the sub-list for method input_type + 68, // [68:68] is the sub-list for extension type_name + 68, // [68:68] is the sub-list for extension extendee + 0, // [0:68] is the sub-list for field type_name } func init() { file_provisionersdk_proto_provisioner_proto_init() } @@ -5747,7 +6082,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PlanRequest); i { + switch v := v.(*InitRequest); i { case 0: return &v.state case 1: @@ -5759,7 +6094,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PlanComplete); i { + switch v := v.(*InitComplete); i { case 0: return &v.state case 1: @@ -5771,7 +6106,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ApplyRequest); i { + switch v := v.(*PlanRequest); i { case 0: return &v.state case 1: @@ -5783,7 +6118,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ApplyComplete); i { + switch v := v.(*PlanComplete); i { case 0: return &v.state case 1: @@ -5795,7 +6130,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Timing); i { + switch v := v.(*ApplyRequest); i { case 0: return &v.state case 1: @@ -5807,7 +6142,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CancelRequest); i { + switch v := v.(*ApplyComplete); i { case 0: return &v.state case 1: @@ -5819,7 +6154,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Request); i { + switch v := v.(*GraphRequest); i { case 0: return &v.state case 1: @@ -5831,7 +6166,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Response); i { + switch v := v.(*GraphComplete); i { case 0: return &v.state case 1: @@ -5843,7 +6178,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DataUpload); i { + switch v := v.(*Timing); i { case 0: return &v.state case 1: @@ -5855,7 +6190,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChunkPiece); i { + switch v := v.(*CancelRequest); i { case 0: return &v.state case 1: @@ -5867,7 +6202,19 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Agent_Metadata); i { + switch v := v.(*Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_provisionersdk_proto_provisioner_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Response); i { case 0: return &v.state case 1: @@ -5879,6 +6226,42 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DataUpload); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_provisionersdk_proto_provisioner_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChunkPiece); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_provisionersdk_proto_provisioner_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Agent_Metadata); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_provisionersdk_proto_provisioner_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Resource_Metadata); i { case 0: return &v.state @@ -5898,18 +6281,22 @@ func file_provisionersdk_proto_provisioner_proto_init() { } file_provisionersdk_proto_provisioner_proto_msgTypes[32].OneofWrappers = []interface{}{} file_provisionersdk_proto_provisioner_proto_msgTypes[34].OneofWrappers = []interface{}{} - file_provisionersdk_proto_provisioner_proto_msgTypes[43].OneofWrappers = []interface{}{ + file_provisionersdk_proto_provisioner_proto_msgTypes[47].OneofWrappers = []interface{}{ (*Request_Config)(nil), (*Request_Parse)(nil), + (*Request_Init)(nil), (*Request_Plan)(nil), (*Request_Apply)(nil), + (*Request_Graph)(nil), (*Request_Cancel)(nil), } - file_provisionersdk_proto_provisioner_proto_msgTypes[44].OneofWrappers = []interface{}{ + file_provisionersdk_proto_provisioner_proto_msgTypes[48].OneofWrappers = []interface{}{ (*Response_Log)(nil), (*Response_Parse)(nil), + (*Response_Init)(nil), (*Response_Plan)(nil), (*Response_Apply)(nil), + (*Response_Graph)(nil), (*Response_DataUpload)(nil), (*Response_ChunkPiece)(nil), } @@ -5918,8 +6305,8 @@ func file_provisionersdk_proto_provisioner_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_provisionersdk_proto_provisioner_proto_rawDesc, - NumEnums: 8, - NumMessages: 51, + NumEnums: 9, + NumMessages: 55, NumExtensions: 0, NumServices: 1, }, diff --git a/provisionersdk/proto/provisioner.proto b/provisionersdk/proto/provisioner.proto index 89a69ce7022ca..eb8a81b61e834 100644 --- a/provisionersdk/proto/provisioner.proto +++ b/provisionersdk/proto/provisioner.proto @@ -369,16 +369,12 @@ message Metadata { // Config represents execution configuration shared by all subsequent requests in the Session message Config { - // template_source_archive is a tar of the template source files - bytes template_source_archive = 1; - // state is the provisioner state (if any) - bytes state = 2; - string provisioner_log_level = 3; + string provisioner_log_level = 1; // Template imports can omit template id - optional string template_id = 4; + optional string template_id = 2; // Dry runs omit version id - optional string template_version_id = 5; - optional bool exp_reuse_terraform_workspace = 6; // Whether to reuse existing terraform workspaces if they exist. + optional string template_version_id = 3; + optional bool exp_reuse_terraform_workspace = 4; // Whether to reuse existing terraform workspaces if they exist. } // ParseRequest consumes source-code to produce inputs. @@ -393,6 +389,25 @@ message ParseComplete { map workspace_tags = 4; } +message InitRequest { + // template_source_archive is a tar of the template source files + bytes template_source_archive = 1; + + // If true, the provisioner can safely assume the caller does not need the + // module files downloaded by the `terraform init` command. + // Ideally this boolean would be flipped in its truthy value, however since + // this is costly, the zero value omitting the module files is preferred. + bool omit_module_files = 3; +} + +message InitComplete { + string error = 1; + repeated Timing timings = 2; + repeated Module modules = 3; + bytes module_files = 4; + bytes module_files_hash = 5; +} + // PlanRequest asks the provisioner to plan what resources & parameters it will create message PlanRequest { Metadata metadata = 1; @@ -401,52 +416,61 @@ message PlanRequest { repeated ExternalAuthProvider external_auth_providers = 4; repeated RichParameterValue previous_parameter_values = 5; - // If true, the provisioner can safely assume the caller does not need the - // module files downloaded by the `terraform init` command. - // Ideally this boolean would be flipped in its truthy value, however for - // backwards compatibility reasons, the zero value should be the previous - // behavior of downloading the module files. - bool omit_module_files = 6; + // state is the provisioner state (if any) + bytes state = 6; } // PlanComplete indicates a request to plan completed. message PlanComplete { string error = 1; - repeated Resource resources = 2; - repeated RichParameter parameters = 3; - repeated ExternalAuthProviderResource external_auth_providers = 4; - repeated Timing timings = 6; - repeated Module modules = 7; - repeated Preset presets = 8; - bytes plan = 9; - repeated ResourceReplacement resource_replacements = 10; - bytes module_files = 11; - bytes module_files_hash = 12; - // Whether a template has any `coder_ai_task` resources defined, even if not planned for creation. - // During a template import, a plan is run which may not yield in any `coder_ai_task` resources, but nonetheless we - // still need to know that such resources are defined. - // - // See `hasAITaskResources` in provisioner/terraform/resources.go for more details. - bool has_ai_tasks = 13; - repeated provisioner.AITask ai_tasks = 14; - bool has_external_agents = 15; + repeated Timing timings = 2; + bytes plan = 3; + int32 dailyCost = 4; + repeated ResourceReplacement resource_replacements = 5; + int32 ai_task_count = 6; } // ApplyRequest asks the provisioner to apply the changes. Apply MUST be preceded by a successful plan request/response // in the same Session. The plan data is not transmitted over the wire and is cached by the provisioner in the Session. message ApplyRequest { Metadata metadata = 1; + // state is the provisioner state (if any) + bytes state = 6; } // ApplyComplete indicates a request to apply completed. message ApplyComplete { bytes state = 1; string error = 2; + repeated Timing timings = 3; +} + +enum GraphSource { + SOURCE_UNKNOWN = 0; + SOURCE_PLAN = 1; + SOURCE_STATE = 2; +} + +message GraphRequest { + Metadata metadata = 1; + GraphSource source = 2; +} + +message GraphComplete { + string error = 1; + repeated Timing timings = 2; repeated Resource resources = 3; repeated RichParameter parameters = 4; repeated ExternalAuthProviderResource external_auth_providers = 5; - repeated Timing timings = 6; - repeated provisioner.AITask ai_tasks = 7; + repeated Preset presets = 6; + // Whether a template has any `coder_ai_task` resources defined, even if not planned for creation. + // During a template import, a plan is run which may not yield in any `coder_ai_task` resources, but nonetheless we + // still need to know that such resources are defined. + // + // See `hasAITaskResources` in provisioner/terraform/resources.go for more details. + bool has_ai_tasks = 7; + repeated provisioner.AITask ai_tasks = 8; + bool has_external_agents = 9; } message Timing { @@ -472,9 +496,11 @@ message Request { oneof type { Config config = 1; ParseRequest parse = 2; - PlanRequest plan = 3; - ApplyRequest apply = 4; - CancelRequest cancel = 5; + InitRequest init = 3; + PlanRequest plan = 4; + ApplyRequest apply = 5; + GraphRequest graph = 6; + CancelRequest cancel = 7; } } @@ -482,10 +508,12 @@ message Response { oneof type { Log log = 1; ParseComplete parse = 2; - PlanComplete plan = 3; - ApplyComplete apply = 4; - DataUpload data_upload = 5; - ChunkPiece chunk_piece = 6; + InitComplete init = 3; + PlanComplete plan = 4; + ApplyComplete apply = 5; + GraphComplete graph = 6; + DataUpload data_upload = 7; + ChunkPiece chunk_piece = 8; } } diff --git a/provisionersdk/serve.go b/provisionersdk/serve.go index 3bac226e58379..2deb8a0815f83 100644 --- a/provisionersdk/serve.go +++ b/provisionersdk/serve.go @@ -35,9 +35,11 @@ type ServeOptions struct { } type Server interface { + Init(s *Session, r *proto.InitRequest, canceledOrComplete <-chan struct{}) *proto.InitComplete Parse(s *Session, r *proto.ParseRequest, canceledOrComplete <-chan struct{}) *proto.ParseComplete Plan(s *Session, r *proto.PlanRequest, canceledOrComplete <-chan struct{}) *proto.PlanComplete Apply(s *Session, r *proto.ApplyRequest, canceledOrComplete <-chan struct{}) *proto.ApplyComplete + Graph(s *Session, r *proto.GraphRequest, canceledOrComplete <-chan struct{}) *proto.GraphComplete } // Serve starts a dRPC connection for the provisioner and transport provided. diff --git a/provisionersdk/serve_test.go b/provisionersdk/serve_test.go index 4fc7342b1eed2..ff79c3c16b226 100644 --- a/provisionersdk/serve_test.go +++ b/provisionersdk/serve_test.go @@ -44,6 +44,11 @@ func TestProvisionerSDK(t *testing.T) { err = s.Send(&proto.Request{Type: &proto.Request_Config{Config: &proto.Config{}}}) require.NoError(t, err) + err = s.Send(&proto.Request{Type: &proto.Request_Init{Init: &proto.InitRequest{}}}) + require.NoError(t, err) + _, err = s.Recv() + require.NoError(t, err) + err = s.Send(&proto.Request{Type: &proto.Request_Parse{Parse: &proto.ParseRequest{}}}) require.NoError(t, err) msg, err := s.Recv() @@ -102,6 +107,11 @@ func TestProvisionerSDK(t *testing.T) { err = s.Send(&proto.Request{Type: &proto.Request_Config{Config: &proto.Config{}}}) require.NoError(t, err) + err = s.Send(&proto.Request{Type: &proto.Request_Init{Init: &proto.InitRequest{}}}) + require.NoError(t, err) + _, err = s.Recv() + require.NoError(t, err) + err = s.Send(&proto.Request{Type: &proto.Request_Parse{Parse: &proto.ParseRequest{}}}) require.NoError(t, err) msg, err := s.Recv() @@ -135,8 +145,18 @@ func TestProvisionerSDK(t *testing.T) { }) } +var _ provisionersdk.Server = unimplementedServer{} + type unimplementedServer struct{} +func (unimplementedServer) Init(s *provisionersdk.Session, r *proto.InitRequest, canceledOrComplete <-chan struct{}) *proto.InitComplete { + return &proto.InitComplete{} +} + +func (unimplementedServer) Graph(s *provisionersdk.Session, r *proto.GraphRequest, canceledOrComplete <-chan struct{}) *proto.GraphComplete { + return &proto.GraphComplete{Error: "unimplemented"} +} + func (unimplementedServer) Parse(_ *provisionersdk.Session, _ *proto.ParseRequest, _ <-chan struct{}) *proto.ParseComplete { return &proto.ParseComplete{Error: "unimplemented"} } diff --git a/provisionersdk/session.go b/provisionersdk/session.go index 59034a761e09d..0a034a88266c4 100644 --- a/provisionersdk/session.go +++ b/provisionersdk/session.go @@ -66,10 +66,6 @@ func (p *protoServer) Session(stream proto.DRPCProvisioner_SessionStream) error return xerrors.Errorf("unable to clean stale sessions %q: %w", s.Files, err) } - err = s.Files.ExtractArchive(s.Context(), s.Logger, afero.NewOsFs(), s.Config) - if err != nil { - return xerrors.Errorf("extract archive: %w", err) - } return s.handleRequests() } @@ -110,6 +106,10 @@ func (s *Session) handleRequests() error { } resp := &proto.Response{} if parse := req.GetParse(); parse != nil { + if s.initialized == nil { + // Files must be initialized before parsing. + return xerrors.New("cannot parse before successful init") + } r := &request[*proto.ParseRequest, *proto.ParseComplete]{ req: parse, session: s, @@ -129,48 +129,28 @@ func (s *Session) handleRequests() error { } resp.Type = &proto.Response_Parse{Parse: complete} } - if plan := req.GetPlan(); plan != nil { - r := &request[*proto.PlanRequest, *proto.PlanComplete]{ - req: plan, - session: s, - serverFn: s.server.Plan, - cancels: requests, + if init := req.GetInit(); init != nil { + if s.initialized != nil { + return xerrors.New("cannot init more than once per session") } - complete, err := r.do() + initResp, err := s.handleInitRequest(init, requests) if err != nil { return err } - resp.Type = &proto.Response_Plan{Plan: complete} - - if protobuf.Size(resp) > drpcsdk.MaxMessageSize { - // It is likely the modules that is pushing the message size over the limit. - // Send the modules over a stream of messages instead. - s.Logger.Info(s.Context(), "plan response too large, sending modules as stream", - slog.F("size_bytes", len(complete.ModuleFiles)), - ) - dataUp, chunks := proto.BytesToDataUpload(proto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, complete.ModuleFiles) - - complete.ModuleFiles = nil // sent over the stream - complete.ModuleFilesHash = dataUp.DataHash - resp.Type = &proto.Response_Plan{Plan: complete} - - err := s.stream.Send(&proto.Response{Type: &proto.Response_DataUpload{DataUpload: dataUp}}) - if err != nil { - complete.Error = fmt.Sprintf("send data upload: %s", err.Error()) - } else { - for i, chunk := range chunks { - err := s.stream.Send(&proto.Response{Type: &proto.Response_ChunkPiece{ChunkPiece: chunk}}) - if err != nil { - complete.Error = fmt.Sprintf("send data piece upload %d/%d: %s", i, dataUp.Chunks, err.Error()) - break - } - } - } + resp.Type = &proto.Response_Init{Init: initResp} + } + if plan := req.GetPlan(); plan != nil { + if s.initialized == nil { + return xerrors.New("cannot plan before successful init") } - - if complete.Error == "" { + planResp, err := s.handlePlanRequest(plan, requests) + if err != nil { + return err + } + if planResp.Error == "" { planned = true } + resp.Type = &proto.Response_Plan{Plan: planResp} } if apply := req.GetApply(); apply != nil { if !planned { @@ -188,6 +168,23 @@ func (s *Session) handleRequests() error { } resp.Type = &proto.Response_Apply{Apply: complete} } + if graph := req.GetGraph(); graph != nil { + if s.initialized == nil { + return xerrors.New("cannot graph before successful init") + } + + r := &request[*proto.GraphRequest, *proto.GraphComplete]{ + req: graph, + session: s, + serverFn: s.server.Graph, + cancels: requests, + } + complete, err := r.do() + if err != nil { + return err + } + resp.Type = &proto.Response_Graph{Graph: complete} + } err := s.stream.Send(resp) if err != nil { return xerrors.Errorf("send response: %w", err) @@ -196,16 +193,85 @@ func (s *Session) handleRequests() error { return nil } +func (s *Session) handleInitRequest(init *proto.InitRequest, requests <-chan *proto.Request) (*proto.InitComplete, error) { + r := &request[*proto.InitRequest, *proto.InitComplete]{ + req: init, + session: s, + serverFn: s.server.Init, + cancels: requests, + } + complete, err := r.do() + if err != nil { + return nil, err + } + if complete.Error != "" { + return complete, nil + } + s.initialized = &initialized{} + + // If the size of the complete message is too large, we need to stream the module files separately. + if protobuf.Size(&proto.Response{Type: &proto.Response_Init{Init: complete}}) > drpcsdk.MaxMessageSize { + // It is likely the modules that is pushing the message size over the limit. + // Send the modules over a stream of messages instead. + s.Logger.Info(s.Context(), "plan response too large, sending modules as stream", + slog.F("size_bytes", len(complete.ModuleFiles)), + ) + dataUp, chunks := proto.BytesToDataUpload(proto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, complete.ModuleFiles) + + complete.ModuleFiles = nil // sent over the stream + complete.ModuleFilesHash = dataUp.DataHash + + err := s.stream.Send(&proto.Response{Type: &proto.Response_DataUpload{DataUpload: dataUp}}) + if err != nil { + complete.Error = fmt.Sprintf("send data upload: %s", err.Error()) + } else { + for i, chunk := range chunks { + err := s.stream.Send(&proto.Response{Type: &proto.Response_ChunkPiece{ChunkPiece: chunk}}) + if err != nil { + complete.Error = fmt.Sprintf("send data piece upload %d/%d: %s", i, dataUp.Chunks, err.Error()) + break + } + } + } + } + + return complete, nil +} + +func (s *Session) handlePlanRequest(plan *proto.PlanRequest, requests <-chan *proto.Request) (*proto.PlanComplete, error) { + r := &request[*proto.PlanRequest, *proto.PlanComplete]{ + req: plan, + session: s, + serverFn: s.server.Plan, + cancels: requests, + } + complete, err := r.do() + if err != nil { + return nil, err + } + if complete.Error != "" { + return complete, nil + } + + return complete, nil +} + type Session struct { Logger slog.Logger Files tfpath.Layouter Config *proto.Config + // initialized indicates if an init was run. + // Required for plan/apply. + initialized *initialized + server Server stream proto.DRPCProvisioner_SessionStream logLevel int32 } +type initialized struct{} + func (s *Session) Context() context.Context { return s.stream.Context() } @@ -226,11 +292,11 @@ func (s *Session) ProvisionLog(level proto.LogLevel, output string) { } type pRequest interface { - *proto.ParseRequest | *proto.PlanRequest | *proto.ApplyRequest + *proto.ParseRequest | *proto.InitRequest | *proto.PlanRequest | *proto.ApplyRequest | *proto.GraphRequest } type pComplete interface { - *proto.ParseComplete | *proto.PlanComplete | *proto.ApplyComplete + *proto.ParseComplete | *proto.InitComplete | *proto.PlanComplete | *proto.ApplyComplete | *proto.GraphComplete } // request processes a single request call to the Server and returns its complete result, while also processing cancel diff --git a/provisionersdk/tfpath/tfpath.go b/provisionersdk/tfpath/tfpath.go index 019552e48d0de..c9db7ee9a4256 100644 --- a/provisionersdk/tfpath/tfpath.go +++ b/provisionersdk/tfpath/tfpath.go @@ -16,7 +16,6 @@ import ( "golang.org/x/xerrors" "cdr.dev/slog" - "github.com/coder/coder/v2/provisionersdk/proto" ) type Layouter interface { @@ -28,7 +27,7 @@ type Layouter interface { TerraformMetadataDir() string ModulesDirectory() string ModulesFilePath() string - ExtractArchive(ctx context.Context, logger slog.Logger, fs afero.Fs, cfg *proto.Config) error + ExtractArchive(ctx context.Context, logger slog.Logger, fs afero.Fs, templateSourceArchive []byte) error Cleanup(ctx context.Context, logger slog.Logger, fs afero.Fs) CleanStaleSessions(ctx context.Context, logger slog.Logger, fs afero.Fs, now time.Time) error } @@ -93,9 +92,9 @@ func (l Layout) ModulesFilePath() string { return filepath.Join(l.ModulesDirectory(), "modules.json") } -func (l Layout) ExtractArchive(ctx context.Context, logger slog.Logger, fs afero.Fs, cfg *proto.Config) error { +func (l Layout) ExtractArchive(ctx context.Context, logger slog.Logger, fs afero.Fs, templateSourceArchive []byte) error { logger.Info(ctx, "unpacking template source archive", - slog.F("size_bytes", len(cfg.TemplateSourceArchive)), + slog.F("size_bytes", len(templateSourceArchive)), ) err := fs.MkdirAll(l.WorkDirectory(), 0o700) @@ -103,11 +102,7 @@ func (l Layout) ExtractArchive(ctx context.Context, logger slog.Logger, fs afero return xerrors.Errorf("create work directory %q: %w", l.WorkDirectory(), err) } - // TODO: Pass in cfg.TemplateSourceArchive, not the full config. - // niling out the config field is a bit hacky. - reader := tar.NewReader(bytes.NewBuffer(cfg.TemplateSourceArchive)) - // for safety, nil out the reference on Config, since the reader now owns it. - cfg.TemplateSourceArchive = nil + reader := tar.NewReader(bytes.NewBuffer(templateSourceArchive)) for { header, err := reader.Next() if err != nil { diff --git a/provisionersdk/tfpath/x/tfpath.go b/provisionersdk/tfpath/x/tfpath.go index c6b9f5d669e94..50af1bbfe149c 100644 --- a/provisionersdk/tfpath/x/tfpath.go +++ b/provisionersdk/tfpath/x/tfpath.go @@ -140,9 +140,9 @@ func (td Layout) Cleanup(ctx context.Context, logger slog.Logger, fs afero.Fs) { slog.F("path", path), slog.Error(err)) } -func (td Layout) ExtractArchive(ctx context.Context, logger slog.Logger, fs afero.Fs, cfg *proto.Config) error { +func (td Layout) ExtractArchive(ctx context.Context, logger slog.Logger, fs afero.Fs, archive []byte) error { logger.Info(ctx, "unpacking template source archive", - slog.F("size_bytes", len(cfg.TemplateSourceArchive)), + slog.F("size_bytes", len(archive)), ) err := fs.MkdirAll(td.WorkDirectory(), 0o700) @@ -163,9 +163,7 @@ func (td Layout) ExtractArchive(ctx context.Context, logger slog.Logger, fs afer return xerrors.Errorf("select terraform workspace: %w", err) } - reader := tar.NewReader(bytes.NewBuffer(cfg.TemplateSourceArchive)) - // for safety, nil out the reference on Config, since the reader now owns it. - cfg.TemplateSourceArchive = nil + reader := tar.NewReader(bytes.NewBuffer(archive)) for { header, err := reader.Next() if err != nil { diff --git a/scaletest/agentconn/run_test.go b/scaletest/agentconn/run_test.go index 2b05c0c302b00..ee856f736e4a4 100644 --- a/scaletest/agentconn/run_test.go +++ b/scaletest/agentconn/run_test.go @@ -230,9 +230,9 @@ func setupRunnerTest(t *testing.T) (client *codersdk.Client, agentID uuid.UUID) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "aws_instance", diff --git a/scaletest/autostart/run_test.go b/scaletest/autostart/run_test.go index dc0fb9fea018e..6fb23b47c9a7f 100644 --- a/scaletest/autostart/run_test.go +++ b/scaletest/autostart/run_test.go @@ -43,10 +43,10 @@ func TestRun(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{ { Name: "example", diff --git a/scaletest/createworkspaces/run_test.go b/scaletest/createworkspaces/run_test.go index 950ca7a7ea631..1fbcb0cb97cac 100644 --- a/scaletest/createworkspaces/run_test.go +++ b/scaletest/createworkspaces/run_test.go @@ -60,27 +60,11 @@ func Test_Runner(t *testing.T) { authToken := uuid.NewString() version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Parameters: testParameters, - }, - }, - }, - }, - ProvisionApply: []*proto.Response{ - { - Type: &proto.Response_Log{ - Log: &proto.Log{ - Level: proto.LogLevel_INFO, - Output: "hello from logs", - }, - }, - }, - { - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ Resources: []*proto.Resource{ { Name: "example", @@ -101,6 +85,21 @@ func Test_Runner(t *testing.T) { }, }, }, + ProvisionApply: []*proto.Response{ + { + Type: &proto.Response_Log{ + Log: &proto.Log{ + Level: proto.LogLevel_INFO, + Output: "hello from logs", + }, + }, + }, + { + Type: &proto.Response_Apply{ + Apply: &proto.ApplyComplete{}, + }, + }, + }, }) version = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) @@ -209,10 +208,10 @@ func Test_Runner(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Parameters: testParameters, }, }, @@ -341,27 +340,11 @@ func Test_Runner(t *testing.T) { authToken := uuid.NewString() version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Parameters: testParameters, - }, - }, - }, - }, - ProvisionApply: []*proto.Response{ - { - Type: &proto.Response_Log{ - Log: &proto.Log{ - Level: proto.LogLevel_INFO, - Output: "hello from logs", - }, - }, - }, - { - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ Resources: []*proto.Resource{ { Name: "example", @@ -382,6 +365,21 @@ func Test_Runner(t *testing.T) { }, }, }, + ProvisionApply: []*proto.Response{ + { + Type: &proto.Response_Log{ + Log: &proto.Log{ + Level: proto.LogLevel_INFO, + Output: "hello from logs", + }, + }, + }, + { + Type: &proto.Response_Apply{ + Apply: &proto.ApplyComplete{}, + }, + }, + }, }) version = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) @@ -484,10 +482,10 @@ func Test_Runner(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, - ProvisionPlan: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Parameters: testParameters, }, }, diff --git a/scaletest/reconnectingpty/run_test.go b/scaletest/reconnectingpty/run_test.go index 84e2b0abf828f..d10682ac7c366 100644 --- a/scaletest/reconnectingpty/run_test.go +++ b/scaletest/reconnectingpty/run_test.go @@ -257,9 +257,9 @@ func setupRunnerTest(t *testing.T) (client *codersdk.Client, agentID uuid.UUID) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "aws_instance", diff --git a/scaletest/workspacebuild/run_test.go b/scaletest/workspacebuild/run_test.go index 13b3e9b3eec78..6a42d2fef0c56 100644 --- a/scaletest/workspacebuild/run_test.go +++ b/scaletest/workspacebuild/run_test.go @@ -58,7 +58,14 @@ func Test_Runner(t *testing.T) { }, { Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Apply: &proto.ApplyComplete{}, + }, + }, + }, + ProvisionGraph: []*proto.Response{ + { + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{ { Name: "example1", @@ -245,8 +252,10 @@ func Test_Runner(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ - Parse: echo.ParseComplete, - ProvisionPlan: echo.PlanComplete, + Parse: echo.ParseComplete, + ProvisionPlan: echo.PlanComplete, + ProvisionInit: echo.InitComplete, + ProvisionGraph: echo.GraphComplete, ProvisionApply: []*proto.Response{ { Type: &proto.Response_Apply{ diff --git a/scaletest/workspacetraffic/run_test.go b/scaletest/workspacetraffic/run_test.go index dd84747886456..beda847762ea9 100644 --- a/scaletest/workspacetraffic/run_test.go +++ b/scaletest/workspacetraffic/run_test.go @@ -49,9 +49,9 @@ func TestRun(t *testing.T) { version = coderdtest.CreateTemplateVersion(t, client, firstUser.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "aws_instance", @@ -168,9 +168,9 @@ func TestRun(t *testing.T) { version = coderdtest.CreateTemplateVersion(t, client, firstUser.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + ProvisionGraph: []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "aws_instance", diff --git a/scaletest/workspaceupdates/run_test.go b/scaletest/workspaceupdates/run_test.go index b31a6050dbbad..e2146fd65836c 100644 --- a/scaletest/workspaceupdates/run_test.go +++ b/scaletest/workspaceupdates/run_test.go @@ -39,10 +39,10 @@ func TestRun(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ Parse: echo.ParseComplete, ProvisionPlan: echo.PlanComplete, - ProvisionApply: []*proto.Response{ + ProvisionGraph: []*proto.Response{ { - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ Resources: []*proto.Resource{ { Name: "example", diff --git a/site/e2e/helpers.ts b/site/e2e/helpers.ts index cda6966a56c91..5f8500550765b 100644 --- a/site/e2e/helpers.ts +++ b/site/e2e/helpers.ts @@ -32,6 +32,8 @@ import { type ApplyComplete, AppSharingLevel, type ExternalAuthProviderResource, + type GraphComplete, + type InitComplete, type ParseComplete, type PlanComplete, type Resource, @@ -540,12 +542,14 @@ type RecursivePartial = { }; interface EchoProvisionerResponses { + init?: RecursivePartial[]; // parse is for observing any Terraform variables parse?: RecursivePartial[]; // plan occurs when the template is imported plan?: RecursivePartial[]; // apply occurs when the workspace is built apply?: RecursivePartial[]; + graph?: RecursivePartial[]; // extraFiles allows the bundling of terraform files in echo provisioner tars // in order to support dynamic parameters extraFiles?: Map; @@ -560,6 +564,40 @@ const emptyPlan = new TextEncoder().encode("{}"); const createTemplateVersionTar = async ( responses: EchoProvisionerResponses = {}, ): Promise => { + if (responses.graph) { + if (!responses.apply) { + responses.apply = responses.graph.map((response) => { + if (response.log) { + return response; + } + return { + apply: { + error: response.graph?.error ?? "", + }, + }; + }); + } + if (!responses.plan) { + responses.plan = responses.graph.map((response) => { + if (response.log) { + return response; + } + return { + plan: { + error: response.graph?.error ?? "", + }, + }; + }); + } + } + + if (!responses.init) { + responses.init = [ + { + init: {}, + }, + ]; + } if (!responses.parse) { responses.parse = [ { @@ -575,25 +613,18 @@ const createTemplateVersionTar = async ( ]; } if (!responses.plan) { - responses.plan = responses.apply.map((response) => { - if (response.log) { - return response; - } - return { - plan: { - error: response.apply?.error ?? "", - resources: response.apply?.resources ?? [], - parameters: response.apply?.parameters ?? [], - externalAuthProviders: response.apply?.externalAuthProviders ?? [], - timings: response.apply?.timings ?? [], - presets: [], - resourceReplacements: [], - plan: emptyPlan, - moduleFiles: new Uint8Array(), - moduleFilesHash: new Uint8Array(), - }, - }; - }); + responses.plan = [ + { + plan: {}, + }, + ]; + } + if (!responses.graph) { + responses.graph = [ + { + graph: {}, + }, + ]; } const tar = new TarWriter(); @@ -617,6 +648,33 @@ const createTemplateVersionTar = async ( Response.encode(response as Response).finish(), ); }); + responses.init.forEach((response, index) => { + response.init = { + error: "", + timings: [], + modules: [], + moduleFiles: new Uint8Array(), + moduleFilesHash: new Uint8Array(), + ...response.init, + } as InitComplete; + tar.addFile( + `${index}.init.protobuf`, + Response.encode(response as Response).finish(), + ); + }); + responses.plan.forEach((response, index) => { + response.plan = { + error: "", + timings: [], + plan: emptyPlan, + resourceReplacements: [], + ...response.plan, + } as PlanComplete; + tar.addFile( + `${index}.plan.protobuf`, + Response.encode(response as Response).finish(), + ); + }); const fillResource = (resource: RecursivePartial) => { if (resource.agents) { @@ -701,40 +759,31 @@ const createTemplateVersionTar = async ( response.apply = { error: "", state: new Uint8Array(), - resources: [], - parameters: [], - externalAuthProviders: [], timings: [], - aiTasks: [], ...response.apply, } as ApplyComplete; - response.apply.resources = response.apply.resources?.map(fillResource); tar.addFile( `${index}.apply.protobuf`, Response.encode(response as Response).finish(), ); }); - responses.plan.forEach((response, index) => { - response.plan = { + responses.graph.forEach((response, index) => { + response.graph = { error: "", resources: [], parameters: [], externalAuthProviders: [], timings: [], - modules: [], presets: [], resourceReplacements: [], - plan: emptyPlan, - moduleFiles: new Uint8Array(), - moduleFilesHash: new Uint8Array(), aiTasks: [], - ...response.plan, - } as PlanComplete; - response.plan.resources = response.plan.resources?.map(fillResource); + ...response.graph, + } as GraphComplete; + response.graph.resources = response.graph.resources?.map(fillResource); tar.addFile( - `${index}.plan.protobuf`, + `${index}.graph.protobuf`, Response.encode(response as Response).finish(), ); }); @@ -889,16 +938,20 @@ ${options}} parse: {}, }, ], + init: [ + { + init: {}, + }, + ], plan: [ { - plan: { - parameters: richParameters, - }, + plan: {}, }, ], - apply: [ + graph: [ { - apply: { + graph: { + parameters: richParameters, resources: [ { name: "example", @@ -907,6 +960,11 @@ ${options}} }, }, ], + apply: [ + { + apply: {}, + }, + ], extraFiles: new Map([["main.tf", tf]]), }; }; @@ -915,21 +973,19 @@ export const echoResponsesWithExternalAuth = ( providers: ExternalAuthProviderResource[], ): EchoProvisionerResponses => { return { - parse: [ + init: [ { - parse: {}, + init: {}, }, ], - plan: [ + parse: [ { - plan: { - externalAuthProviders: providers, - }, + parse: {}, }, ], - apply: [ + graph: [ { - apply: { + graph: { externalAuthProviders: providers, resources: [ { @@ -939,6 +995,11 @@ export const echoResponsesWithExternalAuth = ( }, }, ], + apply: [ + { + apply: {}, + }, + ], }; }; diff --git a/site/e2e/provisionerGenerated.ts b/site/e2e/provisionerGenerated.ts index ba9071ab625e8..d148101625ce4 100644 --- a/site/e2e/provisionerGenerated.ts +++ b/site/e2e/provisionerGenerated.ts @@ -69,6 +69,13 @@ export enum PrebuiltWorkspaceBuildStage { UNRECOGNIZED = -1, } +export enum GraphSource { + SOURCE_UNKNOWN = 0, + SOURCE_PLAN = 1, + SOURCE_STATE = 2, + UNRECOGNIZED = -1, +} + export enum TimingState { STARTED = 0, COMPLETED = 1, @@ -410,10 +417,6 @@ export interface Metadata { /** Config represents execution configuration shared by all subsequent requests in the Session */ export interface Config { - /** template_source_archive is a tar of the template source files */ - templateSourceArchive: Uint8Array; - /** state is the provisioner state (if any) */ - state: Uint8Array; provisionerLogLevel: string; /** Template imports can omit template id */ templateId?: @@ -444,6 +447,26 @@ export interface ParseComplete_WorkspaceTagsEntry { value: string; } +export interface InitRequest { + /** template_source_archive is a tar of the template source files */ + templateSourceArchive: Uint8Array; + /** + * If true, the provisioner can safely assume the caller does not need the + * module files downloaded by the `terraform init` command. + * Ideally this boolean would be flipped in its truthy value, however since + * this is costly, the zero value omitting the module files is preferred. + */ + omitModuleFiles: boolean; +} + +export interface InitComplete { + error: string; + timings: Timing[]; + modules: Module[]; + moduleFiles: Uint8Array; + moduleFilesHash: Uint8Array; +} + /** PlanRequest asks the provisioner to plan what resources & parameters it will create */ export interface PlanRequest { metadata: Metadata | undefined; @@ -451,39 +474,18 @@ export interface PlanRequest { variableValues: VariableValue[]; externalAuthProviders: ExternalAuthProvider[]; previousParameterValues: RichParameterValue[]; - /** - * If true, the provisioner can safely assume the caller does not need the - * module files downloaded by the `terraform init` command. - * Ideally this boolean would be flipped in its truthy value, however for - * backwards compatibility reasons, the zero value should be the previous - * behavior of downloading the module files. - */ - omitModuleFiles: boolean; + /** state is the provisioner state (if any) */ + state: Uint8Array; } /** PlanComplete indicates a request to plan completed. */ export interface PlanComplete { error: string; - resources: Resource[]; - parameters: RichParameter[]; - externalAuthProviders: ExternalAuthProviderResource[]; timings: Timing[]; - modules: Module[]; - presets: Preset[]; plan: Uint8Array; + dailyCost: number; resourceReplacements: ResourceReplacement[]; - moduleFiles: Uint8Array; - moduleFilesHash: Uint8Array; - /** - * Whether a template has any `coder_ai_task` resources defined, even if not planned for creation. - * During a template import, a plan is run which may not yield in any `coder_ai_task` resources, but nonetheless we - * still need to know that such resources are defined. - * - * See `hasAITaskResources` in provisioner/terraform/resources.go for more details. - */ - hasAiTasks: boolean; - aiTasks: AITask[]; - hasExternalAgents: boolean; + aiTaskCount: number; } /** @@ -491,18 +493,42 @@ export interface PlanComplete { * in the same Session. The plan data is not transmitted over the wire and is cached by the provisioner in the Session. */ export interface ApplyRequest { - metadata: Metadata | undefined; + metadata: + | Metadata + | undefined; + /** state is the provisioner state (if any) */ + state: Uint8Array; } /** ApplyComplete indicates a request to apply completed. */ export interface ApplyComplete { state: Uint8Array; error: string; + timings: Timing[]; +} + +export interface GraphRequest { + metadata: Metadata | undefined; + source: GraphSource; +} + +export interface GraphComplete { + error: string; + timings: Timing[]; resources: Resource[]; parameters: RichParameter[]; externalAuthProviders: ExternalAuthProviderResource[]; - timings: Timing[]; + presets: Preset[]; + /** + * Whether a template has any `coder_ai_task` resources defined, even if not planned for creation. + * During a template import, a plan is run which may not yield in any `coder_ai_task` resources, but nonetheless we + * still need to know that such resources are defined. + * + * See `hasAITaskResources` in provisioner/terraform/resources.go for more details. + */ + hasAiTasks: boolean; aiTasks: AITask[]; + hasExternalAgents: boolean; } export interface Timing { @@ -522,16 +548,20 @@ export interface CancelRequest { export interface Request { config?: Config | undefined; parse?: ParseRequest | undefined; + init?: InitRequest | undefined; plan?: PlanRequest | undefined; apply?: ApplyRequest | undefined; + graph?: GraphRequest | undefined; cancel?: CancelRequest | undefined; } export interface Response { log?: Log | undefined; parse?: ParseComplete | undefined; + init?: InitComplete | undefined; plan?: PlanComplete | undefined; apply?: ApplyComplete | undefined; + graph?: GraphComplete | undefined; dataUpload?: DataUpload | undefined; chunkPiece?: ChunkPiece | undefined; } @@ -1318,23 +1348,17 @@ export const Metadata = { export const Config = { encode(message: Config, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.templateSourceArchive.length !== 0) { - writer.uint32(10).bytes(message.templateSourceArchive); - } - if (message.state.length !== 0) { - writer.uint32(18).bytes(message.state); - } if (message.provisionerLogLevel !== "") { - writer.uint32(26).string(message.provisionerLogLevel); + writer.uint32(10).string(message.provisionerLogLevel); } if (message.templateId !== undefined) { - writer.uint32(34).string(message.templateId); + writer.uint32(18).string(message.templateId); } if (message.templateVersionId !== undefined) { - writer.uint32(42).string(message.templateVersionId); + writer.uint32(26).string(message.templateVersionId); } if (message.expReuseTerraformWorkspace !== undefined) { - writer.uint32(48).bool(message.expReuseTerraformWorkspace); + writer.uint32(32).bool(message.expReuseTerraformWorkspace); } return writer; }, @@ -1376,6 +1400,39 @@ export const ParseComplete_WorkspaceTagsEntry = { }, }; +export const InitRequest = { + encode(message: InitRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.templateSourceArchive.length !== 0) { + writer.uint32(10).bytes(message.templateSourceArchive); + } + if (message.omitModuleFiles !== false) { + writer.uint32(24).bool(message.omitModuleFiles); + } + return writer; + }, +}; + +export const InitComplete = { + encode(message: InitComplete, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.error !== "") { + writer.uint32(10).string(message.error); + } + for (const v of message.timings) { + Timing.encode(v!, writer.uint32(18).fork()).ldelim(); + } + for (const v of message.modules) { + Module.encode(v!, writer.uint32(26).fork()).ldelim(); + } + if (message.moduleFiles.length !== 0) { + writer.uint32(34).bytes(message.moduleFiles); + } + if (message.moduleFilesHash.length !== 0) { + writer.uint32(42).bytes(message.moduleFilesHash); + } + return writer; + }, +}; + export const PlanRequest = { encode(message: PlanRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { if (message.metadata !== undefined) { @@ -1393,8 +1450,8 @@ export const PlanRequest = { for (const v of message.previousParameterValues) { RichParameterValue.encode(v!, writer.uint32(42).fork()).ldelim(); } - if (message.omitModuleFiles !== false) { - writer.uint32(48).bool(message.omitModuleFiles); + if (message.state.length !== 0) { + writer.uint32(50).bytes(message.state); } return writer; }, @@ -1405,44 +1462,20 @@ export const PlanComplete = { if (message.error !== "") { writer.uint32(10).string(message.error); } - for (const v of message.resources) { - Resource.encode(v!, writer.uint32(18).fork()).ldelim(); - } - for (const v of message.parameters) { - RichParameter.encode(v!, writer.uint32(26).fork()).ldelim(); - } - for (const v of message.externalAuthProviders) { - ExternalAuthProviderResource.encode(v!, writer.uint32(34).fork()).ldelim(); - } for (const v of message.timings) { - Timing.encode(v!, writer.uint32(50).fork()).ldelim(); - } - for (const v of message.modules) { - Module.encode(v!, writer.uint32(58).fork()).ldelim(); - } - for (const v of message.presets) { - Preset.encode(v!, writer.uint32(66).fork()).ldelim(); + Timing.encode(v!, writer.uint32(18).fork()).ldelim(); } 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(); - } - if (message.moduleFiles.length !== 0) { - writer.uint32(90).bytes(message.moduleFiles); + writer.uint32(26).bytes(message.plan); } - if (message.moduleFilesHash.length !== 0) { - writer.uint32(98).bytes(message.moduleFilesHash); - } - if (message.hasAiTasks !== false) { - writer.uint32(104).bool(message.hasAiTasks); + if (message.dailyCost !== 0) { + writer.uint32(32).int32(message.dailyCost); } - for (const v of message.aiTasks) { - AITask.encode(v!, writer.uint32(114).fork()).ldelim(); + for (const v of message.resourceReplacements) { + ResourceReplacement.encode(v!, writer.uint32(42).fork()).ldelim(); } - if (message.hasExternalAgents !== false) { - writer.uint32(120).bool(message.hasExternalAgents); + if (message.aiTaskCount !== 0) { + writer.uint32(48).int32(message.aiTaskCount); } return writer; }, @@ -1453,6 +1486,9 @@ export const ApplyRequest = { if (message.metadata !== undefined) { Metadata.encode(message.metadata, writer.uint32(10).fork()).ldelim(); } + if (message.state.length !== 0) { + writer.uint32(50).bytes(message.state); + } return writer; }, }; @@ -1465,6 +1501,33 @@ export const ApplyComplete = { if (message.error !== "") { writer.uint32(18).string(message.error); } + for (const v of message.timings) { + Timing.encode(v!, writer.uint32(26).fork()).ldelim(); + } + return writer; + }, +}; + +export const GraphRequest = { + encode(message: GraphRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.metadata !== undefined) { + Metadata.encode(message.metadata, writer.uint32(10).fork()).ldelim(); + } + if (message.source !== 0) { + writer.uint32(16).int32(message.source); + } + return writer; + }, +}; + +export const GraphComplete = { + encode(message: GraphComplete, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.error !== "") { + writer.uint32(10).string(message.error); + } + for (const v of message.timings) { + Timing.encode(v!, writer.uint32(18).fork()).ldelim(); + } for (const v of message.resources) { Resource.encode(v!, writer.uint32(26).fork()).ldelim(); } @@ -1474,11 +1537,17 @@ export const ApplyComplete = { for (const v of message.externalAuthProviders) { ExternalAuthProviderResource.encode(v!, writer.uint32(42).fork()).ldelim(); } - for (const v of message.timings) { - Timing.encode(v!, writer.uint32(50).fork()).ldelim(); + for (const v of message.presets) { + Preset.encode(v!, writer.uint32(50).fork()).ldelim(); + } + if (message.hasAiTasks !== false) { + writer.uint32(56).bool(message.hasAiTasks); } for (const v of message.aiTasks) { - AITask.encode(v!, writer.uint32(58).fork()).ldelim(); + AITask.encode(v!, writer.uint32(66).fork()).ldelim(); + } + if (message.hasExternalAgents !== false) { + writer.uint32(72).bool(message.hasExternalAgents); } return writer; }, @@ -1525,14 +1594,20 @@ export const Request = { if (message.parse !== undefined) { ParseRequest.encode(message.parse, writer.uint32(18).fork()).ldelim(); } + if (message.init !== undefined) { + InitRequest.encode(message.init, writer.uint32(26).fork()).ldelim(); + } if (message.plan !== undefined) { - PlanRequest.encode(message.plan, writer.uint32(26).fork()).ldelim(); + PlanRequest.encode(message.plan, writer.uint32(34).fork()).ldelim(); } if (message.apply !== undefined) { - ApplyRequest.encode(message.apply, writer.uint32(34).fork()).ldelim(); + ApplyRequest.encode(message.apply, writer.uint32(42).fork()).ldelim(); + } + if (message.graph !== undefined) { + GraphRequest.encode(message.graph, writer.uint32(50).fork()).ldelim(); } if (message.cancel !== undefined) { - CancelRequest.encode(message.cancel, writer.uint32(42).fork()).ldelim(); + CancelRequest.encode(message.cancel, writer.uint32(58).fork()).ldelim(); } return writer; }, @@ -1546,17 +1621,23 @@ export const Response = { if (message.parse !== undefined) { ParseComplete.encode(message.parse, writer.uint32(18).fork()).ldelim(); } + if (message.init !== undefined) { + InitComplete.encode(message.init, writer.uint32(26).fork()).ldelim(); + } if (message.plan !== undefined) { - PlanComplete.encode(message.plan, writer.uint32(26).fork()).ldelim(); + PlanComplete.encode(message.plan, writer.uint32(34).fork()).ldelim(); } if (message.apply !== undefined) { - ApplyComplete.encode(message.apply, writer.uint32(34).fork()).ldelim(); + ApplyComplete.encode(message.apply, writer.uint32(42).fork()).ldelim(); + } + if (message.graph !== undefined) { + GraphComplete.encode(message.graph, writer.uint32(50).fork()).ldelim(); } if (message.dataUpload !== undefined) { - DataUpload.encode(message.dataUpload, writer.uint32(42).fork()).ldelim(); + DataUpload.encode(message.dataUpload, writer.uint32(58).fork()).ldelim(); } if (message.chunkPiece !== undefined) { - ChunkPiece.encode(message.chunkPiece, writer.uint32(50).fork()).ldelim(); + ChunkPiece.encode(message.chunkPiece, writer.uint32(66).fork()).ldelim(); } return writer; }, diff --git a/site/e2e/tests/app.spec.ts b/site/e2e/tests/app.spec.ts index 3cb58fcc66c34..3433df6e32d29 100644 --- a/site/e2e/tests/app.spec.ts +++ b/site/e2e/tests/app.spec.ts @@ -32,9 +32,9 @@ test("app", async ({ context, page }) => { } const appName = "test-app"; const template = await createTemplate(page, { - apply: [ + graph: [ { - apply: { + graph: { resources: [ { agents: [ diff --git a/site/e2e/tests/outdatedAgent.spec.ts b/site/e2e/tests/outdatedAgent.spec.ts index 46696b36edeab..9992a5476e8ab 100644 --- a/site/e2e/tests/outdatedAgent.spec.ts +++ b/site/e2e/tests/outdatedAgent.spec.ts @@ -25,9 +25,9 @@ test.skip(`ssh with agent ${agentVersion}`, async ({ page }) => { const token = randomUUID(); const template = await createTemplate(page, { - apply: [ + graph: [ { - apply: { + graph: { resources: [ { agents: [ diff --git a/site/e2e/tests/outdatedCLI.spec.ts b/site/e2e/tests/outdatedCLI.spec.ts index 4f8472d2a019b..cad37bb05a46b 100644 --- a/site/e2e/tests/outdatedCLI.spec.ts +++ b/site/e2e/tests/outdatedCLI.spec.ts @@ -23,9 +23,9 @@ test.beforeEach(async ({ page }) => { test(`ssh with client ${clientVersion}`, async ({ page }) => { const token = randomUUID(); const template = await createTemplate(page, { - apply: [ + graph: [ { - apply: { + graph: { resources: [ { agents: [ diff --git a/site/e2e/tests/webTerminal.spec.ts b/site/e2e/tests/webTerminal.spec.ts index d03f78a8702b8..ccb3216ce8d03 100644 --- a/site/e2e/tests/webTerminal.spec.ts +++ b/site/e2e/tests/webTerminal.spec.ts @@ -18,9 +18,9 @@ test.beforeEach(async ({ page }) => { test("web terminal", async ({ context, page }) => { const token = randomUUID(); const template = await createTemplate(page, { - apply: [ + graph: [ { - apply: { + graph: { resources: [ { agents: [ diff --git a/site/e2e/tests/workspaces/createWorkspace.spec.ts b/site/e2e/tests/workspaces/createWorkspace.spec.ts index c6371c9c9a3b7..52c6b5c05857d 100644 --- a/site/e2e/tests/workspaces/createWorkspace.spec.ts +++ b/site/e2e/tests/workspaces/createWorkspace.spec.ts @@ -32,7 +32,7 @@ test.beforeEach(async ({ page }) => { test("create workspace", async ({ page }) => { await login(page, users.templateAdmin); const template = await createTemplate(page, { - apply: [{ apply: { resources: [{ name: "example" }] } }], + graph: [{ graph: { resources: [{ name: "example" }] } }], }); await login(page, users.member); diff --git a/site/src/modules/workspaces/WorkspaceTiming/StagesChart.tsx b/site/src/modules/workspaces/WorkspaceTiming/StagesChart.tsx index 103d4717f20c6..b1c8c50befbde 100644 --- a/site/src/modules/workspaces/WorkspaceTiming/StagesChart.tsx +++ b/site/src/modules/workspaces/WorkspaceTiming/StagesChart.tsx @@ -246,31 +246,31 @@ export const provisioningStages: Stage[] = [ }, }, { - name: "graph", - label: "graph", + name: "apply", + label: "apply", section: "provisioning", tooltip: { title: ( <> - Terraform graph + Terraform apply - List all resources in plan, used to update coderd database. + Execute Terraform plan to create/modify/delete resources into + desired states. ), }, }, { - name: "apply", - label: "apply", + name: "graph", + label: "graph", section: "provisioning", tooltip: { title: ( <> - Terraform apply + Terraform graph - Execute Terraform plan to create/modify/delete resources into - desired states. + List all resources in plan, used to update coderd database. ),