From 9878ce97787236c706bbee4c5008ea20c0342a04 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 2 Dec 2025 12:19:21 -0600 Subject: [PATCH 1/3] test: fixes all tests with granular provisioner steps --- cli/create_test.go | 20 +- cli/restart_test.go | 6 +- cli/ssh_test.go | 10 +- cli/start_test.go | 22 +- cli/task_test.go | 16 +- cli/templatepresets_test.go | 6 +- cli/templatepush_test.go | 27 +- cli/templateversionarchive_test.go | 1 + coderd/activitybump_test.go | 2 +- coderd/aitasks_test.go | 46 ++- coderd/audit_test.go | 33 +- coderd/autobuild/lifecycle_executor_test.go | 28 +- coderd/coderd_test.go | 2 +- coderd/coderdtest/dynamicparameters.go | 18 +- coderd/externalauth_test.go | 12 +- coderd/gitsshkey_test.go | 4 +- coderd/insights_test.go | 26 +- .../prometheusmetrics_test.go | 8 +- coderd/templates_test.go | 2 +- coderd/templateversions_test.go | 37 +-- coderd/workspaceagents_test.go | 8 +- coderd/workspaceapps/apptest/setup.go | 6 +- coderd/workspaceapps/db_test.go | 6 +- coderd/workspacebuilds_test.go | 62 ++-- coderd/workspaceresourceauth_test.go | 18 +- coderd/workspaces_test.go | 132 ++++---- codersdk/toolsdk/toolsdk_test.go | 4 +- enterprise/cli/create_test.go | 14 +- enterprise/cli/externalworkspaces_test.go | 54 +--- enterprise/coderd/coderd_test.go | 14 +- enterprise/coderd/gitsshkey_test.go | 2 +- enterprise/coderd/prebuilds/claim_test.go | 27 +- enterprise/coderd/provisionerdaemons_test.go | 31 +- enterprise/coderd/templates_test.go | 22 +- enterprise/coderd/workspaceagents_test.go | 38 +-- enterprise/coderd/workspaceproxy_test.go | 4 +- enterprise/coderd/workspacequota_test.go | 104 +++++-- enterprise/coderd/workspaces_test.go | 45 +-- .../provisionerd/remoteprovisioners_test.go | 6 +- enterprise/wsproxy/wsproxy_test.go | 4 +- provisioner/echo/serve_test.go | 163 +++++----- provisioner/terraform/parse_test.go | 86 ++++-- provisioner/terraform/provision_test.go | 246 +++++++++------ provisioner/terraform/timings_test.go | 106 ++++--- provisionerd/provisionerd_test.go | 132 ++++++-- provisionersdk/proto/provisioner.proto | 30 +- provisionersdk/session.go | 2 - scaletest/agentconn/run_test.go | 6 +- scaletest/autostart/run_test.go | 6 +- scaletest/createworkspaces/run_test.go | 86 +++--- scaletest/reconnectingpty/run_test.go | 6 +- scaletest/workspacebuild/run_test.go | 15 +- scaletest/workspacetraffic/run_test.go | 12 +- scaletest/workspaceupdates/run_test.go | 6 +- site/e2e/helpers.ts | 155 +++++++--- site/e2e/provisionerGenerated.ts | 289 +++++++++++------- site/e2e/tests/app.spec.ts | 4 +- site/e2e/tests/outdatedAgent.spec.ts | 4 +- site/e2e/tests/outdatedCLI.spec.ts | 4 +- site/e2e/tests/webTerminal.spec.ts | 4 +- .../tests/workspaces/createWorkspace.spec.ts | 2 +- 61 files changed, 1315 insertions(+), 976 deletions(-) diff --git a/cli/create_test.go b/cli/create_test.go index dd26e450d3916..6a508d149b05a 100644 --- a/cli/create_test.go +++ b/cli/create_test.go @@ -301,11 +301,13 @@ func TestCreate(t *testing.T) { func prepareEchoResponses(parameters []*proto.RichParameter, presets ...*proto.Preset) *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: parameters, Presets: presets, }, @@ -1573,11 +1575,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 2b3113a90173e..357c734a0f4e6 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 58c1b803dbb3d..55123f8890174 100644 --- a/cli/templatepush_test.go +++ b/cli/templatepush_test.go @@ -1306,31 +1306,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 8969895c32987..0ab974f17d00d 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", @@ -951,8 +943,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, }}}, }, @@ -995,8 +987,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, }}}, @@ -1097,8 +1089,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, }}}, }, @@ -1218,8 +1210,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, }}}, }, @@ -1275,8 +1267,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, }}}, }, @@ -1309,8 +1301,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, }}}, }, @@ -1359,8 +1351,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, }}}, }, @@ -1371,8 +1363,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..cf85a7867c67e 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.ProvisionGraphWithAgentAndAPIKeyScope(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..cac394ea5fbc6 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.ProvisionGraphWithAgentAndAPIKeyScope(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..1fa278120ab98 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, @@ -1488,7 +1500,15 @@ func TestPostWorkspaceBuild(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{ - ProvisionApply: []*proto.Response{{}}, + ProvisionApply: []*proto.Response{ + { + Type: &proto.Response_Apply{ + Apply: &proto.ApplyComplete{ + Error: "failed to import", + }, + }, + }, + }, }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) @@ -1642,9 +1662,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 3a9f70227a62f..d60b742cb2bd7 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", @@ -357,9 +357,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", @@ -735,9 +735,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, }, @@ -746,7 +746,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) @@ -2269,7 +2269,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) @@ -2297,7 +2297,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) @@ -2328,9 +2328,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", @@ -2420,7 +2420,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) @@ -2525,10 +2525,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"}, @@ -3382,9 +3382,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", @@ -3467,8 +3467,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{ @@ -3536,9 +3538,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", @@ -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", @@ -3679,9 +3681,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", @@ -3723,9 +3725,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", @@ -3803,10 +3805,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, @@ -3907,10 +3909,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", @@ -3986,10 +3988,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, @@ -4077,10 +4079,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, @@ -4879,8 +4881,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, }}}, }, @@ -4949,9 +4951,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", @@ -5023,9 +5025,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{ { @@ -5320,10 +5322,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 0e1078128a198..6022bc8f37201 100644 --- a/enterprise/coderd/coderd_test.go +++ b/enterprise/coderd/coderd_test.go @@ -660,21 +660,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_test.go b/provisioner/echo/serve_test.go index 9168f1be6d22e..5193c8cb5592c 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,13 +66,19 @@ func TestEcho(t *testing.T) { err := client.Close() require.NoError(t, err) }() - err = client.Send(&proto.Request{Type: &proto.Request_Config{Config: &proto.Config{ + 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{}}}) require.NoError(t, err) + log, err := client.Recv() require.NoError(t, err) require.Equal(t, responses[0].GetLog().Output, log.GetLog().Output) @@ -85,26 +92,7 @@ func TestEcho(t *testing.T) { ctx, cancel := context.WithTimeout(ctx, testutil.WaitShort) defer cancel() - planResponses := []*proto.Response{ - { - Type: &proto.Response_Log{ - Log: &proto.Log{ - Level: proto.LogLevel_INFO, - Output: "log-output", - }, - }, - }, - { - Type: &proto.Response_Plan{ - Plan: &proto.PlanComplete{ - Resources: []*proto.Resource{{ - Name: "resource", - }}, - }, - }, - }, - } - applyResponses := []*proto.Response{ + graphResponses := []*proto.Response{ { Type: &proto.Response_Log{ Log: &proto.Log{ @@ -114,8 +102,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", }}, @@ -123,9 +111,12 @@ func TestEcho(t *testing.T) { }, }, } + data, err := echo.Tar(&echo.Responses{ - ProvisionPlan: planResponses, - ProvisionApply: applyResponses, + ProvisionGraph: graphResponses, + ProvisionApply: echo.ApplyComplete, + ProvisionPlan: echo.PlanComplete, + ProvisionInit: echo.InitComplete, }) require.NoError(t, err) client, err := api.Session(ctx) @@ -134,30 +125,38 @@ func TestEcho(t *testing.T) { err := client.Close() require.NoError(t, err) }() - err = client.Send(&proto.Request{Type: &proto.Request_Config{Config: &proto.Config{ + 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() + _, 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) err = client.Send(&proto.Request{Type: &proto.Request_Apply{Apply: &proto.ApplyRequest{}}}) require.NoError(t, err) - log, err = client.Recv() + _, err = client.Recv() require.NoError(t, err) - require.Equal(t, applyResponses[0].GetLog().Output, log.GetLog().Output) - complete, err = client.Recv() + + 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 +164,11 @@ 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"), + 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,10 +179,15 @@ func TestEcho(t *testing.T) { err := client.Close() require.NoError(t, err) }() - err = client.Send(&proto.Request{Type: &proto.Request_Config{Config: &proto.Config{ + 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{ @@ -199,17 +201,32 @@ func TestEcho(t *testing.T) { }) require.NoError(t, err) + _, err = client.Recv() + require.NoError(t, err) + + err = client.Send(&proto.Request{ + Type: &proto.Request_Graph{ + Graph: &proto.GraphRequest{ + Metadata: &proto.Metadata{ + WorkspaceTransition: proto.WorkspaceTransition_STOP, + }, + Source: proto.GraphSource_SOURCE_STATE, + }, + }, + }) + require.NoError(t, err) + complete, err := client.Recv() 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 +239,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 +263,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", }}, @@ -256,7 +273,9 @@ func TestEcho(t *testing.T) { }} data, err := echo.Tar(&echo.Responses{ ProvisionPlan: echo.PlanComplete, - ProvisionApply: responses, + ProvisionApply: echo.ApplyComplete, + ProvisionInit: echo.InitComplete, + ProvisionGraph: responses, }) require.NoError(t, err) client, err := api.Session(ctx) @@ -266,10 +285,16 @@ func TestEcho(t *testing.T) { require.NoError(t, err) }() err = client.Send(&proto.Request{Type: &proto.Request_Config{Config: &proto.Config{ + ProvisionerLogLevel: "debug", + }}}) + require.NoError(t, err) + + err = client.Send(&proto.Request{Type: &proto.Request_Init{Init: &proto.InitRequest{ TemplateSourceArchive: data, - ProvisionerLogLevel: "debug", }}}) 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{}}}) @@ -280,33 +305,29 @@ 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/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/provision_test.go b/provisioner/terraform/provision_test.go index 1cdcfb067b061..eede18257c2db 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,27 @@ func setupProvisioner(t *testing.T, opts *provisionerServeOptions) (context.Cont return ctx, api } +// sendInitAndGetResp will send the init request and wait for and return the InitComplete response. +func sendInitAndGetResp(t *testing.T, sess proto.DRPCProvisioner_SessionClient, archive []byte, onLog ...func(log string)) *proto.InitComplete { + t.Helper() + err := sendInit(sess, archive) + 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 +129,12 @@ 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 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}, @@ -119,6 +147,12 @@ func sendApply(sess proto.DRPCProvisioner_SessionClient, transition proto.Worksp }}}) } +func sendGraph(sess proto.DRPCProvisioner_SessionClient, source proto.GraphSource) error { + return sess.Send(&proto.Request{Type: &proto.Request_Graph{Graph: &proto.GraphRequest{ + Source: source, + }}}) +} + // below we exec fake_cancel.sh, which causes the kernel to execute it, and if more than // one process tries to do this simultaneously, it can cause "text file busy" // nolint: paralleltest @@ -161,30 +195,46 @@ func TestProvision_Cancel(t *testing.T) { 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 +249,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 +285,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{}) + sendInitAndGetResp(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 +369,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 +379,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 +398,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 +419,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 +428,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 +448,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 +469,7 @@ func TestProvision(t *testing.T) { } }`, }, - Response: &proto.PlanComplete{ + Response: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "A", Type: "null_resource", @@ -428,18 +482,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 +503,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 +547,7 @@ func TestProvision(t *testing.T) { }, }, }, - Response: &proto.PlanComplete{ + Response: &proto.GraphComplete{ Parameters: []*proto.RichParameter{ { Name: "Example", @@ -571,7 +625,7 @@ func TestProvision(t *testing.T) { }, }, }, - Response: &proto.PlanComplete{ + Response: &proto.GraphComplete{ Parameters: []*proto.RichParameter{ { Name: "Example", @@ -623,7 +677,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 +720,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 +763,7 @@ func TestProvision(t *testing.T) { WorkspaceOwnerLoginType: "github", }, }, - Response: &proto.PlanComplete{ + Response: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "null_resource", @@ -738,16 +792,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 +803,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 +848,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 +889,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 +927,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 +966,7 @@ func TestProvision(t *testing.T) { `, provider.TaskPromptParameterName), }, Request: &proto.PlanRequest{}, - Response: &proto.PlanComplete{ + Response: &proto.GraphComplete{ Resources: []*proto.Resource{ { Name: "a", @@ -962,7 +1018,7 @@ func TestProvision(t *testing.T) { } `, }, - Response: &proto.PlanComplete{ + Response: &proto.GraphComplete{ Resources: []*proto.Resource{{ Name: "example", Type: "coder_external_agent", @@ -987,7 +1043,7 @@ func TestProvision(t *testing.T) { } `, }, - Response: &proto.PlanComplete{ + Response: &proto.GraphComplete{ Resources: []*proto.Resource{ { Name: "my-task", @@ -1043,9 +1099,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 := sendInitAndGetResp(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 +1119,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 +1128,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 +1143,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) - require.NoError(t, err) - modulesWant, err := json.Marshal(testCase.Response.Modules) + modulesGot, err := json.Marshal(initComplete.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 +1190,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 +1200,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 +1233,10 @@ 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{}) + + resp := sendInitAndGetResp(t, sess, testutil.CreateTar(t, map[string]string{"main.tf": `resource "null_resource" "A" {}`})) + require.Empty(t, resp.Error) err := sendPlan(sess, proto.WorkspaceTransition_START) require.NoError(t, err) @@ -1210,9 +1284,10 @@ 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{}) + + resp := sendInitAndGetResp(t, sess, testutil.CreateTar(t, map[string]string{"main.tf": echoResource})) + require.Empty(t, resp.Error) err := sendPlan(sess, proto.WorkspaceTransition_START) require.NoError(t, err) @@ -1232,15 +1307,14 @@ 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_`, - }), - }) + sess := configure(ctx, t, api, &proto.Config{}) - err := sendPlan(sess, proto.WorkspaceTransition_START) + 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/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/provisionersdk/proto/provisioner.proto b/provisionersdk/proto/provisioner.proto index eb8a81b61e834..2d0462b75f1be 100644 --- a/provisionersdk/proto/provisioner.proto +++ b/provisionersdk/proto/provisioner.proto @@ -547,14 +547,28 @@ message ChunkPiece { service Provisioner { // Session represents provisioning a single template import or workspace. The daemon always sends Config followed - // by one of the requests (ParseRequest, PlanRequest, ApplyRequest). The provisioner should respond with a stream - // of zero or more Logs, followed by the corresponding complete message (ParseComplete, PlanComplete, - // ApplyComplete). The daemon may then send a new request. A request to apply MUST be preceded by a request plan, - // and the provisioner should store the plan data on the Session after a successful plan, so that the daemon may - // request an apply. If the daemon closes the Session without an apply, the plan data may be safely discarded. + // by one of the requests (InitRequest, ParseRequest, PlanRequest, ApplyRequest, GraphRequest). The provisioner + // should respond with a stream of zero or more Logs, followed by the corresponding complete message + // (InitComplete, ParseComplete, PlanComplete, ApplyComplete, GraphComplete). + // The daemon may then send a new request. // - // The daemon may send a CancelRequest, asynchronously to ask the provisioner to cancel the previous ParseRequest, - // PlanRequest, or ApplyRequest. The provisioner MUST reply with a complete message corresponding to the request - // that was canceled. If the provisioner has already completed the request, it may ignore the CancelRequest. + // A request to Parse or Plan MUST be preceded by a request init. The provisioner should store the init data on + // the session after a successful init. If the daemon closes the session, the init data may be safely discarded. + // + // A request to apply MUST be preceded by a request plan, and the provisioner should store the plan data on the + // Session after a successful plan, so that the daemon may request an apply. If the daemon closes + // the Session without an apply, the plan data may be safely discarded. + // + // A request to graph MUST be preceded by a plan or an apply. + // + // The order of requests is then one of the following: + // 1. Init -> Parse + // 2. Init -> Plan -> Graph + // 3. Init -> Plan -> Apply -> Graph + // + // The daemon may send a CancelRequest, asynchronously to ask the provisioner to cancel the previous InitRequest, + // ParseRequest, PlanRequest, ApplyRequest, or GraphRequest. The provisioner MUST reply with a complete message + // corresponding to the request that was canceled. If the provisioner has already completed the request, + // it may ignore the CancelRequest. rpc Session(stream Request) returns (stream Response); } diff --git a/provisionersdk/session.go b/provisionersdk/session.go index 2640a6dcf03d0..13831ebf87c42 100644 --- a/provisionersdk/session.go +++ b/provisionersdk/session.go @@ -267,8 +267,6 @@ type Session struct { logLevel int32 } -type initialized struct{} - func (s *Session) Context() context.Context { return s.stream.Context() } 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 8f6ac8cb165ed..b96050b488551 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(); + writer.uint32(26).bytes(message.plan); } - if (message.moduleFiles.length !== 0) { - writer.uint32(90).bytes(message.moduleFiles); - } - 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; }, @@ -1599,28 +1680,28 @@ export interface Provisioner { /** * Session represents provisioning a single template import or workspace. The daemon always sends Config followed * by one of the requests (InitRequest, ParseRequest, PlanRequest, ApplyRequest, GraphRequest). The provisioner - * should respond with a stream of zero or more Logs, followed by the corresponding complete message - * (InitComplete, ParseComplete, PlanComplete, ApplyComplete, GraphComplete). + * should respond with a stream of zero or more Logs, followed by the corresponding complete message + * (InitComplete, ParseComplete, PlanComplete, ApplyComplete, GraphComplete). * The daemon may then send a new request. - * - * A request to Parse or Plan MUST be preceded by a request init. The provisioner should store the init data on - * the session after a successful init. If the daemon closes the session, the init data may be safely discarded. - * - * A request to apply MUST be preceded by a request plan, and the provisioner should store the plan data on the - * Session after a successful plan, so that the daemon may request an apply. If the daemon closes - * the Session without an apply, the plan data may be safely discarded. - * - * A request to graph MUST be preceded by a plan or an apply. - * - * The order of requests is then one of the following: - * 1. Init -> Parse - * 2. Init -> Plan -> Graph - * 3. Init -> Plan -> Apply -> Graph + * + * A request to Parse or Plan MUST be preceded by a request init. The provisioner should store the init data on + * the session after a successful init. If the daemon closes the session, the init data may be safely discarded. + * + * A request to apply MUST be preceded by a request plan, and the provisioner should store the plan data on the + * Session after a successful plan, so that the daemon may request an apply. If the daemon closes + * the Session without an apply, the plan data may be safely discarded. + * + * A request to graph MUST be preceded by a plan or an apply. + * + * The order of requests is then one of the following: + * 1. Init -> Parse + * 2. Init -> Plan -> Graph + * 3. Init -> Plan -> Apply -> Graph * * The daemon may send a CancelRequest, asynchronously to ask the provisioner to cancel the previous InitRequest, - * ParseRequest, PlanRequest, ApplyRequest, or GraphRequest. The provisioner MUST reply with a complete message - * corresponding to the request that was canceled. If the provisioner has already completed the request, - * it may ignore the CancelRequest. + * ParseRequest, PlanRequest, ApplyRequest, or GraphRequest. The provisioner MUST reply with a complete message + * corresponding to the request that was canceled. If the provisioner has already completed the request, + * it may ignore the CancelRequest. */ Session(request: Observable): Observable; } diff --git a/site/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); From afed20483cddd4eeddd9232f41d5705dbcdd4ae5 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 11 Dec 2025 08:35:22 -0600 Subject: [PATCH 2/3] teat: move malicious tar test --- provisioner/terraform/provision_test.go | 8 ++++ provisionerd/provisionerd_test.go | 51 ------------------------- 2 files changed, 8 insertions(+), 51 deletions(-) diff --git a/provisioner/terraform/provision_test.go b/provisioner/terraform/provision_test.go index eede18257c2db..ee71838d989d3 100644 --- a/provisioner/terraform/provision_test.go +++ b/provisioner/terraform/provision_test.go @@ -1060,6 +1060,14 @@ func TestProvision(t *testing.T) { }, SkipCacheProviders: true, }, + { + Name: "malicious-tar", + Files: map[string]string{ + // Non-local path outside the working directory. + "../../../etc/passwd": "content", + }, + InitErrorContains: "refusing to extract to non-local path", + }, } // Remove unused cache dirs before running tests. diff --git a/provisionerd/provisionerd_test.go b/provisionerd/provisionerd_test.go index e3204c01c81c2..d94aa17e6c2dc 100644 --- a/provisionerd/provisionerd_test.go +++ b/provisionerd/provisionerd_test.go @@ -149,57 +149,6 @@ func TestProvisionerd(t *testing.T) { require.NoError(t, closer.Close()) }) - t.Run("MaliciousTar", func(t *testing.T) { - // Ensures tars with "../../../etc/passwd" as the path - // are not allowed to run, and will fail the job. - t.Parallel() - done := make(chan struct{}) - t.Cleanup(func() { - close(done) - }) - var ( - completeChan = make(chan struct{}) - completeOnce sync.Once - acq = newAcquireOne(t, &proto.AcquiredJob{ - JobId: "test", - Provisioner: "someprovisioner", - TemplateSourceArchive: testutil.CreateTar(t, map[string]string{ - "../../../etc/passwd": "content", - }), - Type: &proto.AcquiredJob_TemplateImport_{ - TemplateImport: &proto.AcquiredJob_TemplateImport{ - Metadata: &sdkproto.Metadata{}, - }, - }, - }) - ) - - closer := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) { - return createProvisionerDaemonClient(t, done, provisionerDaemonTestServer{ - acquireJobWithCancel: acq.acquireWithCancel, - updateJob: noopUpdateJob, - failJob: func(ctx context.Context, job *proto.FailedJob) (*proto.Empty, error) { - completeOnce.Do(func() { close(completeChan) }) - return &proto.Empty{}, nil - }, - }), nil - }, provisionerd.LocalProvisioners{ - "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()) - }) - // LargePayloads sends a 3mb tar file to the provisioner. The provisioner also // returns large payload messages back. The limit should be 4mb, so all // these messages should work. From e3c3ffea0f93a6047aab99a51e9fcc94c1f082f5 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 12 Dec 2025 07:07:57 -0600 Subject: [PATCH 3/3] use fake terraform in parse tests --- provisioner/terraform/parse_test.go | 56 ++++++++--------------------- 1 file changed, 14 insertions(+), 42 deletions(-) diff --git a/provisioner/terraform/parse_test.go b/provisioner/terraform/parse_test.go index cc2f5346690d9..f9206ca0ffc16 100644 --- a/provisioner/terraform/parse_test.go +++ b/provisioner/terraform/parse_test.go @@ -4,6 +4,8 @@ package terraform_test import ( "encoding/json" + "os" + "path/filepath" "testing" "github.com/stretchr/testify/require" @@ -15,14 +17,18 @@ import ( func TestParse(t *testing.T) { t.Parallel() - ctx, api := setupProvisioner(t, nil) + cwd, err := os.Getwd() + require.NoError(t, err) + + ctx, api := setupProvisioner(t, &provisionerServeOptions{ + // Fake all actual terraform, since parse doesn't need it. + binaryPath: filepath.Join(cwd, "testdata", "timings-aggregation", "fake-terraform.sh"), + }) testCases := []struct { - Name string - Files map[string]string - Response *proto.ParseComplete - // If ErrorContains is not empty, then the InitComplete should have an Error containing the given string - ErrorContains string + Name string + Files map[string]string + Response *proto.ParseComplete ParseErrorContains string }{ { @@ -82,7 +88,7 @@ func TestParse(t *testing.T) { Files: map[string]string{ "main.tf": "a;sd;ajsd;lajsd;lasjdf;a", }, - ErrorContains: `initialize terraform: exit status 1`, + ParseErrorContains: `The ";" character is not valid.`, }, { Name: "multiple-variables", @@ -208,13 +214,6 @@ 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" @@ -277,13 +276,6 @@ 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" @@ -347,13 +339,6 @@ 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" @@ -370,14 +355,6 @@ func TestParse(t *testing.T) { 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" @@ -413,7 +390,7 @@ func TestParse(t *testing.T) { err := sendInit(session, testutil.CreateTar(t, testCase.Files)) require.NoError(t, err) - // Init stage + // Init stage -- a fake terraform, will always succeed quickly. for { msg, err := session.Recv() require.NoError(t, err) @@ -421,11 +398,6 @@ func TestParse(t *testing.T) { t.Logf("init log: %s", msgLog.Log.Output) continue } - - if testCase.ErrorContains != "" { - require.Contains(t, msg.GetInit().GetError(), testCase.ErrorContains) - return // Stop test at this point - } break }