mirror of https://github.com/coder/coder.git
refactor: change template archive extraction to be on provisioner (#9264)
* refactor provisionersdk protocol
Signed-off-by: Spike Curtis <spike@coder.com>
* refactor provisioners to use new protocol
Signed-off-by: Spike Curtis <spike@coder.com>
* refactor provisionerd to use new protocol
Signed-off-by: Spike Curtis <spike@coder.com>
* refactor tests & proto renames
* Fixes from self-review
Signed-off-by: Spike Curtis <spike@coder.com>
* appease fmt & link
Signed-off-by: Spike Curtis <spike@coder.com>
* code review fixes & e2e fixes
Signed-off-by: Spike Curtis <spike@coder.com>
* More fmt
Signed-off-by: Spike Curtis <spike@coder.com>
* Code review fixes
Signed-off-by: Spike Curtis <spike@coder.com>
* new gen; use uuid for session workdir
Signed-off-by: Spike Curtis <spike@coder.com>
* Revert nix-based gen CI task until dogfood is on nix
Signed-off-by: Spike Curtis <spike@coder.com>
* revert deleting dogfood Docker stuff
Signed-off-by: Spike Curtis <spike@coder.com>
* Revert "revert deleting dogfood Docker stuff"
This reverts commit 9762158167
.
---------
Signed-off-by: Spike Curtis <spike@coder.com>
This commit is contained in:
parent
4bed492012
commit
60d5002eb6
8
Makefile
8
Makefile
|
@ -456,10 +456,10 @@ DB_GEN_FILES := \
|
|||
|
||||
# all gen targets should be added here and to gen/mark-fresh
|
||||
gen: \
|
||||
coderd/database/dump.sql \
|
||||
$(DB_GEN_FILES) \
|
||||
provisionersdk/proto/provisioner.pb.go \
|
||||
provisionerd/proto/provisionerd.pb.go \
|
||||
coderd/database/dump.sql \
|
||||
$(DB_GEN_FILES) \
|
||||
site/src/api/typesGenerated.ts \
|
||||
coderd/rbac/object_gen.go \
|
||||
docs/admin/prometheus.md \
|
||||
|
@ -478,10 +478,10 @@ gen: \
|
|||
# used during releases so we don't run generation scripts.
|
||||
gen/mark-fresh:
|
||||
files="\
|
||||
coderd/database/dump.sql \
|
||||
$(DB_GEN_FILES) \
|
||||
provisionersdk/proto/provisioner.pb.go \
|
||||
provisionerd/proto/provisionerd.pb.go \
|
||||
coderd/database/dump.sql \
|
||||
$(DB_GEN_FILES) \
|
||||
site/src/api/typesGenerated.ts \
|
||||
coderd/rbac/object_gen.go \
|
||||
docs/admin/prometheus.md \
|
||||
|
|
|
@ -75,9 +75,9 @@ func TestWorkspaceAgent(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "somename",
|
||||
Type: "someinstance",
|
||||
|
@ -127,9 +127,9 @@ func TestWorkspaceAgent(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "somename",
|
||||
Type: "someinstance",
|
||||
|
@ -179,9 +179,9 @@ func TestWorkspaceAgent(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "somename",
|
||||
Type: "someinstance",
|
||||
|
|
|
@ -82,9 +82,9 @@ func TestConfigSSH(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: []*proto.Response{{
|
||||
Type: &proto.Response_Plan{
|
||||
Plan: &proto.PlanComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
@ -720,22 +720,11 @@ func TestConfigSSH_Hostnames(t *testing.T) {
|
|||
resources = append(resources, resource)
|
||||
}
|
||||
|
||||
provisionResponse := []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Resources: resources,
|
||||
},
|
||||
},
|
||||
}}
|
||||
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
// authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: provisionResponse,
|
||||
ProvisionApply: provisionResponse,
|
||||
})
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID,
|
||||
echo.WithResources(resources))
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
||||
|
|
|
@ -29,11 +29,7 @@ func TestCreate(t *testing.T) {
|
|||
t.Parallel()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: provisionCompleteWithAgent,
|
||||
ProvisionPlan: provisionCompleteWithAgent,
|
||||
})
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, completeWithAgent())
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
args := []string{
|
||||
|
@ -84,11 +80,7 @@ func TestCreate(t *testing.T) {
|
|||
t.Parallel()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||
owner := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: provisionCompleteWithAgent,
|
||||
ProvisionPlan: provisionCompleteWithAgent,
|
||||
})
|
||||
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, completeWithAgent())
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
|
||||
_, user := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
|
||||
|
@ -141,11 +133,7 @@ func TestCreate(t *testing.T) {
|
|||
t.Parallel()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: provisionCompleteWithAgent,
|
||||
ProvisionPlan: provisionCompleteWithAgent,
|
||||
})
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, completeWithAgent())
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||
var defaultTTLMillis int64 = 2 * 60 * 60 * 1000 // 2 hours
|
||||
|
@ -240,6 +228,22 @@ func TestCreate(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func prepareEchoResponses(parameters []*proto.RichParameter) *echo.Responses {
|
||||
return &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Response_Plan{
|
||||
Plan: &proto.PlanComplete{
|
||||
Parameters: parameters,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateWithRichParameters(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -258,27 +262,12 @@ func TestCreateWithRichParameters(t *testing.T) {
|
|||
immutableParameterValue = "4"
|
||||
)
|
||||
|
||||
echoResponses := &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: []*proto.Provision_Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Parameters: []*proto.RichParameter{
|
||||
{Name: firstParameterName, Description: firstParameterDescription, Mutable: true},
|
||||
{Name: secondParameterName, DisplayName: secondParameterDisplayName, Description: secondParameterDescription, Mutable: true},
|
||||
{Name: immutableParameterName, Description: immutableParameterDescription, Mutable: false},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
},
|
||||
}},
|
||||
}
|
||||
echoResponses := prepareEchoResponses([]*proto.RichParameter{
|
||||
{Name: firstParameterName, Description: firstParameterDescription, Mutable: true},
|
||||
{Name: secondParameterName, DisplayName: secondParameterDisplayName, Description: secondParameterDescription, Mutable: true},
|
||||
{Name: immutableParameterName, Description: immutableParameterDescription, Mutable: false},
|
||||
},
|
||||
)
|
||||
|
||||
t.Run("InputParameters", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
@ -427,28 +416,6 @@ func TestCreateValidateRichParameters(t *testing.T) {
|
|||
{Name: boolParameterName, Type: "bool", Mutable: true},
|
||||
}
|
||||
|
||||
prepareEchoResponses := func(richParameters []*proto.RichParameter) *echo.Responses {
|
||||
return &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: []*proto.Provision_Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Parameters: richParameters,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ProvisionApply: []*proto.Provision_Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("ValidateString", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -626,20 +593,16 @@ func TestCreateWithGitAuth(t *testing.T) {
|
|||
t.Parallel()
|
||||
echoResponses := &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: []*proto.Provision_Response{
|
||||
ProvisionPlan: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Type: &proto.Response_Plan{
|
||||
Plan: &proto.PlanComplete{
|
||||
GitAuthProviders: []string{"github"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
},
|
||||
}},
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
}
|
||||
|
||||
client := coderdtest.New(t, &coderdtest.Options{
|
||||
|
|
|
@ -48,7 +48,7 @@ func prepareTestGitSSH(ctx context.Context, t *testing.T) (*codersdk.Client, str
|
|||
agentToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(agentToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
|
|
@ -302,7 +302,7 @@ func runAgent(t *testing.T, client *codersdk.Client, userID uuid.UUID) codersdk.
|
|||
agentToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, orgID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(agentToken),
|
||||
})
|
||||
|
||||
|
|
|
@ -20,30 +20,14 @@ import (
|
|||
func TestRestart(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
echoResponses := &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: []*proto.Provision_Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Parameters: []*proto.RichParameter{
|
||||
{
|
||||
Name: ephemeralParameterName,
|
||||
Description: ephemeralParameterDescription,
|
||||
Mutable: true,
|
||||
Ephemeral: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
echoResponses := prepareEchoResponses([]*proto.RichParameter{
|
||||
{
|
||||
Name: ephemeralParameterName,
|
||||
Description: ephemeralParameterDescription,
|
||||
Mutable: true,
|
||||
Ephemeral: true,
|
||||
},
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
},
|
||||
}},
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
@ -187,10 +171,10 @@ func TestRestartWithParameters(t *testing.T) {
|
|||
|
||||
echoResponses := &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: []*proto.Provision_Response{
|
||||
ProvisionPlan: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Type: &proto.Response_Plan{
|
||||
Plan: &proto.PlanComplete{
|
||||
Parameters: []*proto.RichParameter{
|
||||
{
|
||||
Name: immutableParameterName,
|
||||
|
@ -202,11 +186,7 @@ func TestRestartWithParameters(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
},
|
||||
}},
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
}
|
||||
|
||||
t.Run("DoNotAskForImmutables", func(t *testing.T) {
|
||||
|
|
|
@ -41,7 +41,6 @@ import (
|
|||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/collectors"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/spf13/afero"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/mod/semver"
|
||||
"golang.org/x/oauth2"
|
||||
|
@ -1304,7 +1303,11 @@ func newProvisionerDaemon(
|
|||
defer wg.Done()
|
||||
defer cancel()
|
||||
|
||||
err := echo.Serve(ctx, afero.NewOsFs(), &provisionersdk.ServeOptions{Listener: echoServer})
|
||||
err := echo.Serve(ctx, &provisionersdk.ServeOptions{
|
||||
Listener: echoServer,
|
||||
WorkDirectory: workDir,
|
||||
Logger: logger.Named("echo"),
|
||||
})
|
||||
if err != nil {
|
||||
select {
|
||||
case errCh <- err:
|
||||
|
@ -1336,10 +1339,11 @@ func newProvisionerDaemon(
|
|||
|
||||
err := terraform.Serve(ctx, &terraform.ServeOptions{
|
||||
ServeOptions: &provisionersdk.ServeOptions{
|
||||
Listener: terraformServer,
|
||||
Listener: terraformServer,
|
||||
Logger: logger.Named("terraform"),
|
||||
WorkDirectory: workDir,
|
||||
},
|
||||
CachePath: tfDir,
|
||||
Logger: logger.Named("terraform"),
|
||||
Tracer: tracer,
|
||||
})
|
||||
if err != nil && !xerrors.Is(err, context.Canceled) {
|
||||
|
@ -1366,7 +1370,6 @@ func newProvisionerDaemon(
|
|||
UpdateInterval: time.Second,
|
||||
ForceCancelInterval: cfg.Provisioner.ForceCancelInterval.Value(),
|
||||
Provisioners: provisioners,
|
||||
WorkDirectory: workDir,
|
||||
TracerProvider: coderAPI.TracerProvider,
|
||||
Metrics: &metrics,
|
||||
}), nil
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
|
||||
"github.com/coder/coder/v2/cli/clitest"
|
||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||
"github.com/coder/coder/v2/provisioner/echo"
|
||||
"github.com/coder/coder/v2/pty/ptytest"
|
||||
)
|
||||
|
||||
|
@ -17,11 +16,7 @@ func TestShow(t *testing.T) {
|
|||
t.Parallel()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: provisionCompleteWithAgent,
|
||||
ProvisionPlan: provisionCompleteWithAgent,
|
||||
})
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, completeWithAgent())
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
||||
|
|
|
@ -56,10 +56,10 @@ func setupWorkspaceForAgent(t *testing.T, mutate func([]*proto.Agent) []*proto.A
|
|||
agentToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "dev",
|
||||
Type: "google_compute_instance",
|
||||
|
|
|
@ -33,10 +33,10 @@ func TestStart(t *testing.T) {
|
|||
|
||||
echoResponses := &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: []*proto.Provision_Response{
|
||||
ProvisionPlan: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Type: &proto.Response_Plan{
|
||||
Plan: &proto.PlanComplete{
|
||||
Parameters: []*proto.RichParameter{
|
||||
{
|
||||
Name: ephemeralParameterName,
|
||||
|
@ -49,11 +49,7 @@ func TestStart(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
},
|
||||
}},
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
}
|
||||
|
||||
t.Run("BuildOptions", func(t *testing.T) {
|
||||
|
@ -151,10 +147,10 @@ func TestStartWithParameters(t *testing.T) {
|
|||
|
||||
echoResponses := &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: []*proto.Provision_Response{
|
||||
ProvisionPlan: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Type: &proto.Response_Plan{
|
||||
Plan: &proto.PlanComplete{
|
||||
Parameters: []*proto.RichParameter{
|
||||
{
|
||||
Name: immutableParameterName,
|
||||
|
@ -166,11 +162,7 @@ func TestStartWithParameters(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
},
|
||||
}},
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
}
|
||||
|
||||
t.Run("DoNotAskForImmutables", func(t *testing.T) {
|
||||
|
|
|
@ -25,9 +25,9 @@ func TestStatePull(t *testing.T) {
|
|||
wantState := []byte("some state")
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
State: wantState,
|
||||
},
|
||||
},
|
||||
|
@ -53,9 +53,9 @@ func TestStatePull(t *testing.T) {
|
|||
wantState := []byte("some state")
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
State: wantState,
|
||||
},
|
||||
},
|
||||
|
@ -83,7 +83,7 @@ func TestStatePush(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
})
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
@ -108,7 +108,7 @@ func TestStatePush(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
})
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
|
|
@ -19,26 +19,52 @@ import (
|
|||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
|
||||
var provisionCompleteWithAgent = []*proto.Provision_Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Resources: []*proto.Resource{
|
||||
{
|
||||
Type: "compute",
|
||||
Name: "main",
|
||||
Agents: []*proto.Agent{
|
||||
func completeWithAgent() *echo.Responses {
|
||||
return &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Response_Plan{
|
||||
Plan: &proto.PlanComplete{
|
||||
Resources: []*proto.Resource{
|
||||
{
|
||||
Name: "smith",
|
||||
OperatingSystem: "linux",
|
||||
Architecture: "i386",
|
||||
Type: "compute",
|
||||
Name: "main",
|
||||
Agents: []*proto.Agent{
|
||||
{
|
||||
Name: "smith",
|
||||
OperatingSystem: "linux",
|
||||
Architecture: "i386",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ProvisionApply: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{
|
||||
{
|
||||
Type: "compute",
|
||||
Name: "main",
|
||||
Agents: []*proto.Agent{
|
||||
{
|
||||
Name: "smith",
|
||||
OperatingSystem: "linux",
|
||||
Architecture: "i386",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplateCreate(t *testing.T) {
|
||||
|
@ -47,10 +73,7 @@ func TestTemplateCreate(t *testing.T) {
|
|||
t.Parallel()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||
coderdtest.CreateFirstUser(t, client)
|
||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: provisionCompleteWithAgent,
|
||||
})
|
||||
source := clitest.CreateTemplateVersionSource(t, completeWithAgent())
|
||||
args := []string{
|
||||
"templates",
|
||||
"create",
|
||||
|
@ -85,10 +108,7 @@ func TestTemplateCreate(t *testing.T) {
|
|||
t.Parallel()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||
coderdtest.CreateFirstUser(t, client)
|
||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: provisionCompleteWithAgent,
|
||||
})
|
||||
source := clitest.CreateTemplateVersionSource(t, completeWithAgent())
|
||||
require.NoError(t, os.Remove(filepath.Join(source, ".terraform.lock.hcl")))
|
||||
args := []string{
|
||||
"templates",
|
||||
|
@ -128,10 +148,7 @@ func TestTemplateCreate(t *testing.T) {
|
|||
t.Parallel()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||
coderdtest.CreateFirstUser(t, client)
|
||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: provisionCompleteWithAgent,
|
||||
})
|
||||
source := clitest.CreateTemplateVersionSource(t, completeWithAgent())
|
||||
require.NoError(t, os.Remove(filepath.Join(source, ".terraform.lock.hcl")))
|
||||
args := []string{
|
||||
"templates",
|
||||
|
@ -167,10 +184,7 @@ func TestTemplateCreate(t *testing.T) {
|
|||
t.Parallel()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||
coderdtest.CreateFirstUser(t, client)
|
||||
source, err := echo.Tar(&echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: provisionCompleteWithAgent,
|
||||
})
|
||||
source, err := echo.Tar(completeWithAgent())
|
||||
require.NoError(t, err)
|
||||
|
||||
args := []string{
|
||||
|
@ -196,10 +210,7 @@ func TestTemplateCreate(t *testing.T) {
|
|||
coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
create := func() error {
|
||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: provisionCompleteWithAgent,
|
||||
})
|
||||
source := clitest.CreateTemplateVersionSource(t, completeWithAgent())
|
||||
args := []string{
|
||||
"templates",
|
||||
"create",
|
||||
|
|
|
@ -205,9 +205,9 @@ func TestTemplatePull(t *testing.T) {
|
|||
// a template version source.
|
||||
func genTemplateVersionSource() *echo.Responses {
|
||||
return &echo.Responses{
|
||||
Parse: []*proto.Parse_Response{
|
||||
Parse: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Parse_Response_Log{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{
|
||||
Output: uuid.NewString(),
|
||||
},
|
||||
|
@ -215,11 +215,11 @@ func genTemplateVersionSource() *echo.Responses {
|
|||
},
|
||||
|
||||
{
|
||||
Type: &proto.Parse_Response_Complete{
|
||||
Complete: &proto.Parse_Complete{},
|
||||
Type: &proto.Response_Parse{
|
||||
Parse: &proto.ParseComplete{},
|
||||
},
|
||||
},
|
||||
},
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ func TestTemplatePush(t *testing.T) {
|
|||
// Test the cli command.
|
||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
})
|
||||
inv, root := clitest.New(t, "templates", "push", template.Name, "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho), "--name", "example")
|
||||
clitest.SetupConfig(t, client, root)
|
||||
|
@ -82,7 +82,7 @@ func TestTemplatePush(t *testing.T) {
|
|||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
})
|
||||
|
||||
wantMessage := strings.Repeat("a", 72)
|
||||
|
@ -121,7 +121,7 @@ func TestTemplatePush(t *testing.T) {
|
|||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
})
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
|
@ -168,7 +168,7 @@ func TestTemplatePush(t *testing.T) {
|
|||
// Test the cli command.
|
||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
})
|
||||
require.NoError(t, os.Remove(filepath.Join(source, ".terraform.lock.hcl")))
|
||||
|
||||
|
@ -211,7 +211,7 @@ func TestTemplatePush(t *testing.T) {
|
|||
// Test the cli command.
|
||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
})
|
||||
require.NoError(t, os.Remove(filepath.Join(source, ".terraform.lock.hcl")))
|
||||
|
||||
|
@ -248,7 +248,7 @@ func TestTemplatePush(t *testing.T) {
|
|||
// Test the cli command.
|
||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
})
|
||||
inv, root := clitest.New(t, "templates", "push", template.Name, "--activate=false", "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho), "--name", "example")
|
||||
clitest.SetupConfig(t, client, root)
|
||||
|
@ -293,7 +293,7 @@ func TestTemplatePush(t *testing.T) {
|
|||
// Test the cli command.
|
||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
})
|
||||
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID,
|
||||
|
@ -340,7 +340,7 @@ func TestTemplatePush(t *testing.T) {
|
|||
|
||||
source, err := echo.Tar(&echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -619,10 +619,7 @@ func TestTemplatePush(t *testing.T) {
|
|||
t.Parallel()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: provisionCompleteWithAgent,
|
||||
})
|
||||
source := clitest.CreateTemplateVersionSource(t, completeWithAgent())
|
||||
|
||||
const templateName = "my-template"
|
||||
args := []string{
|
||||
|
@ -665,16 +662,16 @@ func TestTemplatePush(t *testing.T) {
|
|||
|
||||
func createEchoResponsesWithTemplateVariables(templateVariables []*proto.TemplateVariable) *echo.Responses {
|
||||
return &echo.Responses{
|
||||
Parse: []*proto.Parse_Response{
|
||||
Parse: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Parse_Response_Complete{
|
||||
Complete: &proto.Parse_Complete{
|
||||
Type: &proto.Response_Parse{
|
||||
Parse: &proto.ParseComplete{
|
||||
TemplateVariables: templateVariables,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,8 +57,8 @@ func TestUpdate(t *testing.T) {
|
|||
|
||||
version2 := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
}, template.ID)
|
||||
_ = coderdtest.AwaitTemplateVersionJob(t, client, version2.ID)
|
||||
|
||||
|
@ -100,28 +100,13 @@ func TestUpdateWithRichParameters(t *testing.T) {
|
|||
immutableParameterValue = "4"
|
||||
)
|
||||
|
||||
echoResponses := &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: []*proto.Provision_Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Parameters: []*proto.RichParameter{
|
||||
{Name: firstParameterName, Description: firstParameterDescription, Mutable: true},
|
||||
{Name: immutableParameterName, Description: immutableParameterDescription, Mutable: false},
|
||||
{Name: secondParameterName, Description: secondParameterDescription, Mutable: true},
|
||||
{Name: ephemeralParameterName, Description: ephemeralParameterDescription, Mutable: true, Ephemeral: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
},
|
||||
}},
|
||||
}
|
||||
echoResponses := prepareEchoResponses([]*proto.RichParameter{
|
||||
{Name: firstParameterName, Description: firstParameterDescription, Mutable: true},
|
||||
{Name: immutableParameterName, Description: immutableParameterDescription, Mutable: false},
|
||||
{Name: secondParameterName, Description: secondParameterDescription, Mutable: true},
|
||||
{Name: ephemeralParameterName, Description: ephemeralParameterDescription, Mutable: true, Ephemeral: true},
|
||||
},
|
||||
)
|
||||
|
||||
t.Run("ImmutableCannotBeCustomized", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
@ -313,28 +298,6 @@ func TestUpdateValidateRichParameters(t *testing.T) {
|
|||
{Name: boolParameterName, Type: "bool", Mutable: true},
|
||||
}
|
||||
|
||||
prepareEchoResponses := func(richParameters []*proto.RichParameter) *echo.Responses {
|
||||
return &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: []*proto.Provision_Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Parameters: richParameters,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ProvisionApply: []*proto.Provision_Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("ValidateString", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ import (
|
|||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/codersdk/agentsdk"
|
||||
"github.com/coder/coder/v2/provisioner/echo"
|
||||
"github.com/coder/coder/v2/provisionersdk/proto"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
|
||||
|
@ -60,25 +59,9 @@ func TestWorkspaceActivityBump(t *testing.T) {
|
|||
ttlMillis := int64(ttl / time.Millisecond)
|
||||
agentToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
Agents: []*proto.Agent{{
|
||||
Id: uuid.NewString(),
|
||||
Name: "agent",
|
||||
Auth: &proto.Agent_Token{
|
||||
Token: agentToken,
|
||||
},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}},
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(agentToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
|
|
|
@ -683,8 +683,8 @@ func TestExecutorFailedWorkspace(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ProvisionFailed,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ApplyFailed,
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||
ctr.FailureTTLMillis = ptr.Ref[int64](failureTTL.Milliseconds())
|
||||
|
@ -733,8 +733,8 @@ func TestExecutorInactiveWorkspace(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||
ctr.TimeTilDormantMillis = ptr.Ref[int64](inactiveTTL.Milliseconds())
|
||||
|
@ -766,22 +766,16 @@ 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.Provision_Response{
|
||||
ProvisionPlan: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Type: &proto.Response_Plan{
|
||||
Plan: &proto.PlanComplete{
|
||||
Parameters: richParameters,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ProvisionApply: []*proto.Provision_Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
},
|
||||
},
|
||||
},
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
|
|
|
@ -181,7 +181,7 @@ func TestDERPForceWebSockets(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
|
|
@ -37,7 +37,6 @@ import (
|
|||
"github.com/google/uuid"
|
||||
"github.com/moby/moby/pkg/namesgenerator"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/oauth2"
|
||||
|
@ -469,10 +468,13 @@ func NewProvisionerDaemon(t testing.TB, coderAPI *coderd.API) io.Closer {
|
|||
_ = echoServer.Close()
|
||||
cancelFunc()
|
||||
})
|
||||
fs := afero.NewMemMapFs()
|
||||
// seems t.TempDir() is not safe to call from a different goroutine
|
||||
workDir := t.TempDir()
|
||||
go func() {
|
||||
err := echo.Serve(ctx, fs, &provisionersdk.ServeOptions{
|
||||
Listener: echoServer,
|
||||
err := echo.Serve(ctx, &provisionersdk.ServeOptions{
|
||||
Listener: echoServer,
|
||||
WorkDirectory: workDir,
|
||||
Logger: coderAPI.Logger.Named("echo").Leveled(slog.LevelDebug),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
}()
|
||||
|
@ -480,7 +482,6 @@ func NewProvisionerDaemon(t testing.TB, coderAPI *coderd.API) io.Closer {
|
|||
closer := provisionerd.New(func(ctx context.Context) (provisionerdproto.DRPCProvisionerDaemonClient, error) {
|
||||
return coderAPI.CreateInMemoryProvisionerDaemon(ctx, 0)
|
||||
}, &provisionerd.Options{
|
||||
Filesystem: fs,
|
||||
Logger: coderAPI.Logger.Named("provisionerd").Leveled(slog.LevelDebug),
|
||||
JobPollInterval: 50 * time.Millisecond,
|
||||
UpdateInterval: 250 * time.Millisecond,
|
||||
|
@ -488,7 +489,6 @@ func NewProvisionerDaemon(t testing.TB, coderAPI *coderd.API) io.Closer {
|
|||
Provisioners: provisionerd.Provisioners{
|
||||
string(database.ProvisionerTypeEcho): sdkproto.NewDRPCProvisionerClient(echoClient),
|
||||
},
|
||||
WorkDirectory: t.TempDir(),
|
||||
})
|
||||
t.Cleanup(func() {
|
||||
_ = closer.Close()
|
||||
|
@ -506,11 +506,11 @@ func NewExternalProvisionerDaemon(t *testing.T, client *codersdk.Client, org uui
|
|||
cancelFunc()
|
||||
<-serveDone
|
||||
})
|
||||
fs := afero.NewMemMapFs()
|
||||
go func() {
|
||||
defer close(serveDone)
|
||||
err := echo.Serve(ctx, fs, &provisionersdk.ServeOptions{
|
||||
Listener: echoServer,
|
||||
err := echo.Serve(ctx, &provisionersdk.ServeOptions{
|
||||
Listener: echoServer,
|
||||
WorkDirectory: t.TempDir(),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
}()
|
||||
|
@ -522,7 +522,6 @@ func NewExternalProvisionerDaemon(t *testing.T, client *codersdk.Client, org uui
|
|||
Tags: tags,
|
||||
})
|
||||
}, &provisionerd.Options{
|
||||
Filesystem: fs,
|
||||
Logger: slogtest.Make(t, nil).Named("provisionerd").Leveled(slog.LevelDebug),
|
||||
JobPollInterval: 50 * time.Millisecond,
|
||||
UpdateInterval: 250 * time.Millisecond,
|
||||
|
@ -530,7 +529,6 @@ func NewExternalProvisionerDaemon(t *testing.T, client *codersdk.Client, org uui
|
|||
Provisioners: provisionerd.Provisioners{
|
||||
string(database.ProvisionerTypeEcho): sdkproto.NewDRPCProvisionerClient(echoClient),
|
||||
},
|
||||
WorkDirectory: t.TempDir(),
|
||||
})
|
||||
t.Cleanup(func() {
|
||||
_ = closer.Close()
|
||||
|
|
|
@ -23,7 +23,6 @@ import (
|
|||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/codersdk/agentsdk"
|
||||
"github.com/coder/coder/v2/provisioner/echo"
|
||||
"github.com/coder/coder/v2/provisionersdk/proto"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
|
||||
|
@ -227,7 +226,7 @@ func TestGitAuthCallback(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
@ -256,24 +255,9 @@ func TestGitAuthCallback(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
Agents: []*proto.Agent{{
|
||||
Id: uuid.NewString(),
|
||||
Auth: &proto.Agent_Token{
|
||||
Token: authToken,
|
||||
},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}},
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
|
@ -342,7 +326,7 @@ func TestGitAuthCallback(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
@ -400,7 +384,7 @@ func TestGitAuthCallback(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
@ -443,7 +427,7 @@ func TestGitAuthCallback(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
|
|
@ -108,7 +108,7 @@ func TestAgentGitSSHKey(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
project := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
|
|
@ -48,7 +48,7 @@ func TestDeploymentInsights(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
@ -134,7 +134,7 @@ func TestUserLatencyInsights(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
@ -498,18 +498,18 @@ 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.Provision_Response{
|
||||
ProvisionPlan: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Type: &proto.Response_Plan{
|
||||
Plan: &proto.PlanComplete{
|
||||
Parameters: parameters,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: resources,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -268,10 +268,10 @@ func TestAgents(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
@ -494,7 +494,7 @@ func prepareWorkspaceAndAgent(t *testing.T, client *codersdk.Client, user coders
|
|||
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
|
|
@ -280,7 +280,7 @@ func (server *Server) AcquireJob(ctx context.Context, _ *proto.Empty) (*proto.Ac
|
|||
RichParameterValues: convertRichParameterValues(workspaceBuildParameters),
|
||||
VariableValues: asVariableValues(templateVariables),
|
||||
GitAuthProviders: gitAuthProviders,
|
||||
Metadata: &sdkproto.Provision_Metadata{
|
||||
Metadata: &sdkproto.Metadata{
|
||||
CoderUrl: server.AccessURL.String(),
|
||||
WorkspaceTransition: transition,
|
||||
WorkspaceName: workspace.Name,
|
||||
|
@ -316,7 +316,7 @@ func (server *Server) AcquireJob(ctx context.Context, _ *proto.Empty) (*proto.Ac
|
|||
TemplateDryRun: &proto.AcquiredJob_TemplateDryRun{
|
||||
RichParameterValues: convertRichParameterValues(input.RichParameterValues),
|
||||
VariableValues: asVariableValues(templateVariables),
|
||||
Metadata: &sdkproto.Provision_Metadata{
|
||||
Metadata: &sdkproto.Metadata{
|
||||
CoderUrl: server.AccessURL.String(),
|
||||
WorkspaceName: input.WorkspaceName,
|
||||
},
|
||||
|
@ -337,7 +337,7 @@ func (server *Server) AcquireJob(ctx context.Context, _ *proto.Empty) (*proto.Ac
|
|||
protoJob.Type = &proto.AcquiredJob_TemplateImport_{
|
||||
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
||||
UserVariableValues: convertVariableValues(userVariableValues),
|
||||
Metadata: &sdkproto.Provision_Metadata{
|
||||
Metadata: &sdkproto.Metadata{
|
||||
CoderUrl: server.AccessURL.String(),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -267,7 +267,7 @@ func TestAcquireJob(t *testing.T) {
|
|||
Id: gitAuthProvider,
|
||||
AccessToken: "access_token",
|
||||
}},
|
||||
Metadata: &sdkproto.Provision_Metadata{
|
||||
Metadata: &sdkproto.Metadata{
|
||||
CoderUrl: srv.AccessURL.String(),
|
||||
WorkspaceTransition: sdkproto.WorkspaceTransition_START,
|
||||
WorkspaceName: workspace.Name,
|
||||
|
@ -359,7 +359,7 @@ func TestAcquireJob(t *testing.T) {
|
|||
|
||||
want, err := json.Marshal(&proto.AcquiredJob_TemplateDryRun_{
|
||||
TemplateDryRun: &proto.AcquiredJob_TemplateDryRun{
|
||||
Metadata: &sdkproto.Provision_Metadata{
|
||||
Metadata: &sdkproto.Metadata{
|
||||
CoderUrl: srv.AccessURL.String(),
|
||||
WorkspaceName: "testing",
|
||||
},
|
||||
|
@ -391,7 +391,7 @@ func TestAcquireJob(t *testing.T) {
|
|||
|
||||
want, err := json.Marshal(&proto.AcquiredJob_TemplateImport_{
|
||||
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
||||
Metadata: &sdkproto.Provision_Metadata{
|
||||
Metadata: &sdkproto.Metadata{
|
||||
CoderUrl: srv.AccessURL.String(),
|
||||
},
|
||||
},
|
||||
|
@ -434,7 +434,7 @@ func TestAcquireJob(t *testing.T) {
|
|||
UserVariableValues: []*sdkproto.VariableValue{
|
||||
{Name: "first", Sensitive: true, Value: "first_value"},
|
||||
},
|
||||
Metadata: &sdkproto.Provision_Metadata{
|
||||
Metadata: &sdkproto.Metadata{
|
||||
CoderUrl: srv.AccessURL.String(),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -20,16 +20,16 @@ func TestProvisionerJobLogs(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{
|
||||
Level: proto.LogLevel_INFO,
|
||||
Output: "log-output",
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{},
|
||||
},
|
||||
}},
|
||||
})
|
||||
|
@ -59,16 +59,16 @@ func TestProvisionerJobLogs(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{
|
||||
Level: proto.LogLevel_INFO,
|
||||
Output: "log-output",
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{},
|
||||
},
|
||||
}},
|
||||
})
|
||||
|
|
|
@ -1203,7 +1203,7 @@ func TestTemplateMetrics(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
|
|
@ -136,8 +136,8 @@ func TestPostTemplateVersionsByOrganization(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
data, err := echo.Tar(&echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -245,8 +245,8 @@ func TestPatchCancelTemplateVersion(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{},
|
||||
},
|
||||
}},
|
||||
|
@ -284,8 +284,8 @@ func TestPatchCancelTemplateVersion(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{},
|
||||
},
|
||||
}},
|
||||
|
@ -346,9 +346,9 @@ func TestTemplateVersionsGitAuth(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: []*proto.Response{{
|
||||
Type: &proto.Response_Plan{
|
||||
Plan: &proto.PlanComplete{
|
||||
GitAuthProviders: []string{"github"},
|
||||
},
|
||||
},
|
||||
|
@ -400,9 +400,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.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "some",
|
||||
Type: "example",
|
||||
|
@ -439,17 +439,17 @@ func TestTemplateVersionLogs(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{
|
||||
Level: proto.LogLevel_INFO,
|
||||
Output: "example",
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "some",
|
||||
Type: "example",
|
||||
|
@ -610,15 +610,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.Provision_Response{
|
||||
ProvisionApply: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{resource},
|
||||
},
|
||||
},
|
||||
|
@ -677,8 +677,8 @@ func TestTemplateVersionDryRun(t *testing.T) {
|
|||
// This import job will never finish
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{},
|
||||
},
|
||||
}},
|
||||
|
@ -705,15 +705,15 @@ func TestTemplateVersionDryRun(t *testing.T) {
|
|||
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{
|
||||
ProvisionApply: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -776,15 +776,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.Provision_Response{
|
||||
ProvisionApply: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1040,21 +1040,17 @@ func TestTemplateVersionVariables(t *testing.T) {
|
|||
|
||||
createEchoResponses := func(templateVariables []*proto.TemplateVariable) *echo.Responses {
|
||||
return &echo.Responses{
|
||||
Parse: []*proto.Parse_Response{
|
||||
Parse: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Parse_Response_Complete{
|
||||
Complete: &proto.Parse_Complete{
|
||||
Type: &proto.Response_Parse{
|
||||
Parse: &proto.ParseComplete{
|
||||
TemplateVariables: templateVariables,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
},
|
||||
}},
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1418,10 +1414,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.Provision_Response{
|
||||
ProvisionPlan: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Type: &proto.Response_Plan{
|
||||
Plan: &proto.PlanComplete{
|
||||
Parameters: []*proto.RichParameter{
|
||||
{
|
||||
Name: firstParameterName,
|
||||
|
@ -1453,11 +1449,7 @@ func TestTemplateVersionParameters_Order(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
},
|
||||
}},
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
})
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
|
||||
|
|
|
@ -43,10 +43,10 @@ func TestWorkspaceAgent(t *testing.T) {
|
|||
tmpDir := t.TempDir()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
@ -87,10 +87,10 @@ func TestWorkspaceAgent(t *testing.T) {
|
|||
tmpDir := t.TempDir()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
@ -132,10 +132,10 @@ func TestWorkspaceAgent(t *testing.T) {
|
|||
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
@ -188,10 +188,10 @@ func TestWorkspaceAgentStartupLogs(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
@ -252,10 +252,10 @@ func TestWorkspaceAgentStartupLogs(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
@ -319,7 +319,7 @@ func TestWorkspaceAgentListen(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
@ -360,7 +360,7 @@ func TestWorkspaceAgentListen(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
|
||||
|
@ -371,10 +371,10 @@ func TestWorkspaceAgentListen(t *testing.T) {
|
|||
|
||||
version = coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
@ -419,7 +419,7 @@ func TestWorkspaceAgentTailnet(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
@ -471,7 +471,7 @@ func TestWorkspaceAgentTailnetDirectDisabled(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
@ -548,10 +548,10 @@ func TestWorkspaceAgentListeningPorts(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
@ -806,9 +806,9 @@ func TestWorkspaceAgentAppHealth(t *testing.T) {
|
|||
}
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
@ -893,7 +893,7 @@ func TestWorkspaceAgentReportStats(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
@ -942,7 +942,7 @@ func TestWorkspaceAgent_LifecycleState(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
@ -1014,10 +1014,10 @@ func TestWorkspaceAgent_Metadata(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
@ -1184,7 +1184,7 @@ func TestWorkspaceAgent_Startup(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
@ -1238,7 +1238,7 @@ func TestWorkspaceAgent_Startup(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
@ -1293,7 +1293,7 @@ func TestWorkspaceAgent_UpdatedDERP(t *testing.T) {
|
|||
agentToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(agentToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
|
|
@ -288,10 +288,10 @@ func createWorkspaceWithApps(t *testing.T, client *codersdk.Client, orgID uuid.U
|
|||
appURL := fmt.Sprintf("%s://127.0.0.1:%d?%s", scheme, port, proxyTestAppQuery)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, orgID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
|
|
@ -94,10 +94,10 @@ func Test_ResolveRequest(t *testing.T) {
|
|||
agentAuthToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, firstUser.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
|
|
@ -376,12 +376,12 @@ func TestPatchCancelWorkspaceBuild(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{},
|
||||
},
|
||||
}},
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
})
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
@ -401,11 +401,12 @@ func TestPatchCancelWorkspaceBuild(t *testing.T) {
|
|||
require.Eventually(t, func() bool {
|
||||
var err error
|
||||
build, err = client.WorkspaceBuild(ctx, build.ID)
|
||||
// job gets marked Failed when there is an Error; in practice we never get to Status = Canceled
|
||||
// because provisioners report an Error when canceled. We check the Error string to ensure we don't mask
|
||||
// other errors in this test.
|
||||
return assert.NoError(t, err) &&
|
||||
// The job will never actually cancel successfully because it will never send a
|
||||
// provision complete response.
|
||||
assert.Empty(t, build.Job.Error) &&
|
||||
build.Job.Status == codersdk.ProvisionerJobCanceling
|
||||
build.Job.Error == "canceled" &&
|
||||
build.Job.Status == codersdk.ProvisionerJobFailed
|
||||
}, testutil.WaitShort, testutil.IntervalFast)
|
||||
})
|
||||
t.Run("User is not allowed to cancel", func(t *testing.T) {
|
||||
|
@ -415,12 +416,12 @@ func TestPatchCancelWorkspaceBuild(t *testing.T) {
|
|||
owner := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{},
|
||||
},
|
||||
}},
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
})
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
|
||||
|
@ -452,9 +453,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.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "some",
|
||||
Type: "example",
|
||||
|
@ -494,16 +495,16 @@ func TestWorkspaceBuildLogs(t *testing.T) {
|
|||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{
|
||||
Level: proto.LogLevel_INFO,
|
||||
Output: "example",
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "some",
|
||||
Type: "example",
|
||||
|
@ -548,10 +549,10 @@ func TestWorkspaceBuildState(t *testing.T) {
|
|||
wantState := []byte("some kinda state")
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
State: wantState,
|
||||
},
|
||||
},
|
||||
|
@ -764,31 +765,31 @@ func TestWorkspaceBuildDebugMode(t *testing.T) {
|
|||
// Interact as template admin
|
||||
echoResponses := &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{
|
||||
Level: proto.LogLevel_DEBUG,
|
||||
Output: "want-it",
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Type: &proto.Provision_Response_Log{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{
|
||||
Level: proto.LogLevel_TRACE,
|
||||
Output: "dont-want-it",
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Type: &proto.Provision_Response_Log{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{
|
||||
Level: proto.LogLevel_DEBUG,
|
||||
Output: "done",
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{},
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
@ -831,7 +832,10 @@ func TestWorkspaceBuildDebugMode(t *testing.T) {
|
|||
if !ok {
|
||||
break processingLogs
|
||||
}
|
||||
|
||||
t.Logf("got log: %s -- %s | %s | %s", log.Level, log.Stage, log.Source, log.Output)
|
||||
if log.Source != "provisioner" {
|
||||
continue
|
||||
}
|
||||
logsProcessed++
|
||||
|
||||
require.NotEqual(t, "dont-want-it", log.Output, "unexpected log message", "%s log message shouldn't be logged: %s")
|
||||
|
@ -841,7 +845,6 @@ func TestWorkspaceBuildDebugMode(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
require.Len(t, echoResponses.ProvisionApply, logsProcessed)
|
||||
require.Equal(t, 2, logsProcessed)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "somename",
|
||||
Type: "someinstance",
|
||||
|
@ -71,9 +71,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.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "somename",
|
||||
Type: "someinstance",
|
||||
|
@ -157,9 +157,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.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "somename",
|
||||
Type: "someinstance",
|
||||
|
|
|
@ -174,9 +174,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.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "some",
|
||||
Type: "example",
|
||||
|
@ -214,9 +214,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.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "some",
|
||||
Type: "example",
|
||||
|
@ -258,9 +258,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.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "some",
|
||||
Type: "example",
|
||||
|
@ -1248,7 +1248,7 @@ func TestWorkspaceFilterManual(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
@ -1276,7 +1276,7 @@ func TestWorkspaceFilterManual(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
@ -1316,10 +1316,10 @@ func TestWorkspaceFilterManual(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
@ -1374,7 +1374,7 @@ func TestWorkspaceFilterManual(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
@ -1418,7 +1418,7 @@ func TestWorkspaceFilterManual(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
@ -1457,7 +1457,7 @@ func TestWorkspaceFilterManual(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
@ -1575,7 +1575,7 @@ 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.Provision_Response{{}},
|
||||
ProvisionApply: []*proto.Response{{}},
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
|
@ -2138,10 +2138,10 @@ func TestWorkspaceWatcher(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
@ -2231,10 +2231,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.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Error: "test error",
|
||||
},
|
||||
},
|
||||
|
@ -2299,9 +2299,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.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "beta",
|
||||
Type: "example",
|
||||
|
@ -2367,9 +2367,9 @@ func TestWorkspaceResource(t *testing.T) {
|
|||
}
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "some",
|
||||
Type: "example",
|
||||
|
@ -2424,9 +2424,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.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "some",
|
||||
Type: "example",
|
||||
|
@ -2497,10 +2497,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.Provision_Response{
|
||||
ProvisionPlan: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Type: &proto.Response_Plan{
|
||||
Plan: &proto.PlanComplete{
|
||||
Parameters: []*proto.RichParameter{
|
||||
{
|
||||
Name: firstParameterName,
|
||||
|
@ -2521,9 +2521,9 @@ func TestWorkspaceWithRichParameters(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{},
|
||||
},
|
||||
}},
|
||||
})
|
||||
|
@ -2590,10 +2590,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.Provision_Response{
|
||||
ProvisionPlan: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Type: &proto.Response_Plan{
|
||||
Plan: &proto.PlanComplete{
|
||||
Parameters: []*proto.RichParameter{
|
||||
{
|
||||
Name: firstParameterName,
|
||||
|
@ -2612,9 +2612,9 @@ func TestWorkspaceWithOptionalRichParameters(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{},
|
||||
},
|
||||
}},
|
||||
})
|
||||
|
@ -2681,10 +2681,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.Provision_Response{
|
||||
ProvisionPlan: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Type: &proto.Response_Plan{
|
||||
Plan: &proto.PlanComplete{
|
||||
Parameters: []*proto.RichParameter{
|
||||
{
|
||||
Name: firstParameterName,
|
||||
|
@ -2706,9 +2706,9 @@ func TestWorkspaceWithEphemeralRichParameters(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{},
|
||||
},
|
||||
}},
|
||||
})
|
||||
|
|
|
@ -70,6 +70,11 @@ func (r *RootCmd) provisionerDaemonStart() *clibase.Cmd {
|
|||
return xerrors.Errorf("mkdir %q: %w", cacheDir, err)
|
||||
}
|
||||
|
||||
tempDir, err := os.MkdirTemp("", "provisionerd")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
terraformClient, terraformServer := provisionersdk.MemTransportPipe()
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
|
@ -84,10 +89,11 @@ func (r *RootCmd) provisionerDaemonStart() *clibase.Cmd {
|
|||
|
||||
err := terraform.Serve(ctx, &terraform.ServeOptions{
|
||||
ServeOptions: &provisionersdk.ServeOptions{
|
||||
Listener: terraformServer,
|
||||
Listener: terraformServer,
|
||||
Logger: logger.Named("terraform"),
|
||||
WorkDirectory: tempDir,
|
||||
},
|
||||
CachePath: cacheDir,
|
||||
Logger: logger.Named("terraform"),
|
||||
})
|
||||
if err != nil && !xerrors.Is(err, context.Canceled) {
|
||||
select {
|
||||
|
@ -97,11 +103,6 @@ func (r *RootCmd) provisionerDaemonStart() *clibase.Cmd {
|
|||
}
|
||||
}()
|
||||
|
||||
tempDir, err := os.MkdirTemp("", "provisionerd")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info(ctx, "starting provisioner daemon", slog.F("tags", tags))
|
||||
|
||||
provisioners := provisionerd.Provisioners{
|
||||
|
@ -121,7 +122,6 @@ func (r *RootCmd) provisionerDaemonStart() *clibase.Cmd {
|
|||
JobPollJitter: pollJitter,
|
||||
UpdateInterval: 500 * time.Millisecond,
|
||||
Provisioners: provisioners,
|
||||
WorkDirectory: tempDir,
|
||||
})
|
||||
|
||||
var exitErr error
|
||||
|
|
|
@ -111,7 +111,7 @@ func TestServiceBanners(t *testing.T) {
|
|||
agentClient.SetSessionToken(authToken)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
|
|
@ -139,9 +139,9 @@ func TestProvisionerDaemonServe(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
data, err := echo.Tar(&echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: []*proto.Response{{
|
||||
Type: &proto.Response_Plan{
|
||||
Plan: &proto.PlanComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
|
|
@ -73,9 +73,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.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
|
|
@ -89,9 +89,9 @@ func TestWorkspaceQuota(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
@ -183,39 +183,15 @@ func TestWorkspaceQuota(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
verifyQuota(ctx, t, client, 0, 4)
|
||||
|
||||
stopResp := []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
DailyCost: 1,
|
||||
}},
|
||||
},
|
||||
},
|
||||
}}
|
||||
|
||||
startResp := []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
DailyCost: 2,
|
||||
}},
|
||||
},
|
||||
},
|
||||
}}
|
||||
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlanMap: map[proto.WorkspaceTransition][]*proto.Provision_Response{
|
||||
proto.WorkspaceTransition_START: startResp,
|
||||
proto.WorkspaceTransition_STOP: stopResp,
|
||||
ProvisionPlanMap: map[proto.WorkspaceTransition][]*proto.Response{
|
||||
proto.WorkspaceTransition_START: planWithCost(2),
|
||||
proto.WorkspaceTransition_STOP: planWithCost(1),
|
||||
},
|
||||
ProvisionApplyMap: map[proto.WorkspaceTransition][]*proto.Provision_Response{
|
||||
proto.WorkspaceTransition_START: startResp,
|
||||
proto.WorkspaceTransition_STOP: stopResp,
|
||||
ProvisionApplyMap: map[proto.WorkspaceTransition][]*proto.Response{
|
||||
proto.WorkspaceTransition_START: applyWithCost(2),
|
||||
proto.WorkspaceTransition_STOP: applyWithCost(1),
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -258,3 +234,31 @@ func TestWorkspaceQuota(t *testing.T) {
|
|||
require.Equal(t, codersdk.WorkspaceStatusRunning, build.Status)
|
||||
})
|
||||
}
|
||||
|
||||
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,
|
||||
}},
|
||||
},
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
||||
func applyWithCost(cost int32) []*proto.Response {
|
||||
return []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
DailyCost: cost,
|
||||
}},
|
||||
},
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
|
|
@ -120,8 +120,8 @@ func TestWorkspaceAutobuild(t *testing.T) {
|
|||
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ProvisionFailed,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ApplyFailed,
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||
ctr.FailureTTLMillis = ptr.Ref[int64](failureTTL.Milliseconds())
|
||||
|
@ -166,8 +166,8 @@ func TestWorkspaceAutobuild(t *testing.T) {
|
|||
})
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ProvisionFailed,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ApplyFailed,
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||
ctr.FailureTTLMillis = ptr.Ref[int64](failureTTL.Milliseconds())
|
||||
|
@ -212,8 +212,8 @@ func TestWorkspaceAutobuild(t *testing.T) {
|
|||
})
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
})
|
||||
// Create a template without setting a failure_ttl.
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
@ -255,8 +255,8 @@ func TestWorkspaceAutobuild(t *testing.T) {
|
|||
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||
ctr.TimeTilDormantMillis = ptr.Ref[int64](inactiveTTL.Milliseconds())
|
||||
|
@ -311,8 +311,8 @@ func TestWorkspaceAutobuild(t *testing.T) {
|
|||
})
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||
ctr.TimeTilDormantMillis = ptr.Ref[int64](inactiveTTL.Milliseconds())
|
||||
|
@ -353,8 +353,8 @@ func TestWorkspaceAutobuild(t *testing.T) {
|
|||
})
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||
ctr.TimeTilDormantAutoDeleteMillis = ptr.Ref[int64](autoDeleteTTL.Milliseconds())
|
||||
|
@ -395,8 +395,8 @@ func TestWorkspaceAutobuild(t *testing.T) {
|
|||
})
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||
ctr.TimeTilDormantMillis = ptr.Ref[int64](inactiveTTL.Milliseconds())
|
||||
|
@ -447,8 +447,8 @@ func TestWorkspaceAutobuild(t *testing.T) {
|
|||
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||
ctr.TimeTilDormantMillis = ptr.Ref[int64](transitionTTL.Milliseconds())
|
||||
|
@ -516,8 +516,8 @@ func TestWorkspaceAutobuild(t *testing.T) {
|
|||
})
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
})
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||
ctr.TimeTilDormantAutoDeleteMillis = ptr.Ref[int64](dormantTTL.Milliseconds())
|
||||
|
@ -578,8 +578,8 @@ func TestWorkspaceAutobuild(t *testing.T) {
|
|||
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: echo.ProvisionComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
})
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
|
||||
|
|
|
@ -5,25 +5,24 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/xerrors"
|
||||
protobuf "google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/coder/coder/v2/provisionersdk"
|
||||
"github.com/coder/coder/v2/provisionersdk/proto"
|
||||
)
|
||||
|
||||
// ProvisionApplyWithAgent returns provision responses that will mock a fake
|
||||
// "aws_instance" resource with an agent that has the given auth token.
|
||||
func ProvisionApplyWithAgent(authToken string) []*proto.Provision_Response {
|
||||
return []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
func ProvisionApplyWithAgent(authToken string) []*proto.Response {
|
||||
return []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
@ -42,23 +41,36 @@ func ProvisionApplyWithAgent(authToken string) []*proto.Provision_Response {
|
|||
|
||||
var (
|
||||
// ParseComplete is a helper to indicate an empty parse completion.
|
||||
ParseComplete = []*proto.Parse_Response{{
|
||||
Type: &proto.Parse_Response_Complete{
|
||||
Complete: &proto.Parse_Complete{},
|
||||
ParseComplete = []*proto.Response{{
|
||||
Type: &proto.Response_Parse{
|
||||
Parse: &proto.ParseComplete{},
|
||||
},
|
||||
}}
|
||||
// ProvisionComplete is a helper to indicate an empty provision completion.
|
||||
ProvisionComplete = []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
// PlanComplete is a helper to indicate an empty provision completion.
|
||||
PlanComplete = []*proto.Response{{
|
||||
Type: &proto.Response_Plan{
|
||||
Plan: &proto.PlanComplete{},
|
||||
},
|
||||
}}
|
||||
// ApplyComplete is a helper to indicate an empty provision completion.
|
||||
ApplyComplete = []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{},
|
||||
},
|
||||
}}
|
||||
|
||||
// ProvisionFailed is a helper to convey a failed provision
|
||||
// operation.
|
||||
ProvisionFailed = []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
// PlanFailed is a helper to convey a failed plan operation
|
||||
PlanFailed = []*proto.Response{{
|
||||
Type: &proto.Response_Plan{
|
||||
Plan: &proto.PlanComplete{
|
||||
Error: "failed!",
|
||||
},
|
||||
},
|
||||
}}
|
||||
// ApplyFailed is a helper to convey a failed apply operation
|
||||
ApplyFailed = []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Error: "failed!",
|
||||
},
|
||||
},
|
||||
|
@ -66,120 +78,116 @@ var (
|
|||
)
|
||||
|
||||
// Serve starts the echo provisioner.
|
||||
func Serve(ctx context.Context, filesystem afero.Fs, options *provisionersdk.ServeOptions) error {
|
||||
return provisionersdk.Serve(ctx, &echo{
|
||||
filesystem: filesystem,
|
||||
}, options)
|
||||
func Serve(ctx context.Context, options *provisionersdk.ServeOptions) error {
|
||||
return provisionersdk.Serve(ctx, &echo{}, options)
|
||||
}
|
||||
|
||||
// The echo provisioner serves as a dummy provisioner primarily
|
||||
// used for testing. It echos responses from JSON files in the
|
||||
// format %d.protobuf. It's used for testing.
|
||||
type echo struct {
|
||||
filesystem afero.Fs
|
||||
type echo struct{}
|
||||
|
||||
func readResponses(sess *provisionersdk.Session, trans string, suffix string) ([]*proto.Response, error) {
|
||||
var responses []*proto.Response
|
||||
for i := 0; ; i++ {
|
||||
paths := []string{
|
||||
// Try more specific path first, then fallback to generic.
|
||||
filepath.Join(sess.WorkDirectory, fmt.Sprintf("%d.%s.%s", i, trans, suffix)),
|
||||
filepath.Join(sess.WorkDirectory, fmt.Sprintf("%d.%s", i, suffix)),
|
||||
}
|
||||
for pathIndex, path := range paths {
|
||||
_, err := os.Stat(path)
|
||||
if err != nil && pathIndex == (len(paths)-1) {
|
||||
// If there are zero messages, something is wrong
|
||||
if i == 0 {
|
||||
// Error if nothing is around to enable failed states.
|
||||
return nil, xerrors.Errorf("no state: %w", err)
|
||||
}
|
||||
// Otherwise, we've read all responses
|
||||
return responses, nil
|
||||
}
|
||||
if err != nil {
|
||||
// try next path
|
||||
continue
|
||||
}
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("read file %q: %w", path, err)
|
||||
}
|
||||
response := new(proto.Response)
|
||||
err = protobuf.Unmarshal(data, response)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("unmarshal: %w", err)
|
||||
}
|
||||
responses = append(responses, response)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse reads requests from the provided directory to stream responses.
|
||||
func (e *echo) Parse(request *proto.Parse_Request, stream proto.DRPCProvisioner_ParseStream) error {
|
||||
for index := 0; ; index++ {
|
||||
path := filepath.Join(request.Directory, fmt.Sprintf("%d.parse.protobuf", index))
|
||||
_, err := e.filesystem.Stat(path)
|
||||
if err != nil {
|
||||
if index == 0 {
|
||||
// Error if nothing is around to enable failed states.
|
||||
return xerrors.Errorf("no state: %w", err)
|
||||
}
|
||||
break
|
||||
func (*echo) Parse(sess *provisionersdk.Session, _ *proto.ParseRequest, _ <-chan struct{}) *proto.ParseComplete {
|
||||
responses, err := readResponses(sess, "unspecified", "parse.protobuf")
|
||||
if err != nil {
|
||||
return &proto.ParseComplete{Error: err.Error()}
|
||||
}
|
||||
for _, response := range responses {
|
||||
if log := response.GetLog(); log != nil {
|
||||
sess.ProvisionLog(log.Level, log.Output)
|
||||
}
|
||||
data, err := afero.ReadFile(e.filesystem, path)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("read file %q: %w", path, err)
|
||||
}
|
||||
var response proto.Parse_Response
|
||||
err = protobuf.Unmarshal(data, &response)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("unmarshal: %w", err)
|
||||
}
|
||||
err = stream.Send(&response)
|
||||
if err != nil {
|
||||
return err
|
||||
if complete := response.GetParse(); complete != nil {
|
||||
return complete
|
||||
}
|
||||
}
|
||||
<-stream.Context().Done()
|
||||
return stream.Context().Err()
|
||||
|
||||
// if we didn't get a complete from the filesystem, that's an error
|
||||
return provisionersdk.ParseErrorf("complete response missing")
|
||||
}
|
||||
|
||||
// Provision reads requests from the provided directory to stream responses.
|
||||
func (e *echo) Provision(stream proto.DRPCProvisioner_ProvisionStream) error {
|
||||
msg, err := stream.Recv()
|
||||
// Plan reads requests from the provided directory to stream responses.
|
||||
func (*echo) Plan(sess *provisionersdk.Session, req *proto.PlanRequest, canceledOrComplete <-chan struct{}) *proto.PlanComplete {
|
||||
responses, err := readResponses(
|
||||
sess,
|
||||
strings.ToLower(req.GetMetadata().GetWorkspaceTransition().String()),
|
||||
"plan.protobuf")
|
||||
if err != nil {
|
||||
return err
|
||||
return &proto.PlanComplete{Error: err.Error()}
|
||||
}
|
||||
for _, response := range responses {
|
||||
if log := response.GetLog(); log != nil {
|
||||
sess.ProvisionLog(log.Level, log.Output)
|
||||
}
|
||||
if complete := response.GetPlan(); complete != nil {
|
||||
return complete
|
||||
}
|
||||
}
|
||||
|
||||
var config *proto.Provision_Config
|
||||
switch {
|
||||
case msg.GetPlan() != nil:
|
||||
config = msg.GetPlan().GetConfig()
|
||||
case msg.GetApply() != nil:
|
||||
config = msg.GetApply().GetConfig()
|
||||
default:
|
||||
// Probably a cancel
|
||||
return nil
|
||||
// some tests use Echo without a complete response to test cancel
|
||||
<-canceledOrComplete
|
||||
return provisionersdk.PlanErrorf("canceled")
|
||||
}
|
||||
|
||||
// Apply reads requests from the provided directory to stream responses.
|
||||
func (*echo) Apply(sess *provisionersdk.Session, req *proto.ApplyRequest, canceledOrComplete <-chan struct{}) *proto.ApplyComplete {
|
||||
responses, err := readResponses(
|
||||
sess,
|
||||
strings.ToLower(req.GetMetadata().GetWorkspaceTransition().String()),
|
||||
"apply.protobuf")
|
||||
if err != nil {
|
||||
return &proto.ApplyComplete{Error: err.Error()}
|
||||
}
|
||||
for _, response := range responses {
|
||||
if log := response.GetLog(); log != nil {
|
||||
sess.ProvisionLog(log.Level, log.Output)
|
||||
}
|
||||
if complete := response.GetApply(); complete != nil {
|
||||
return complete
|
||||
}
|
||||
}
|
||||
|
||||
outer:
|
||||
for i := 0; ; i++ {
|
||||
var extension string
|
||||
if msg.GetPlan() != nil {
|
||||
extension = ".plan.protobuf"
|
||||
} else {
|
||||
extension = ".apply.protobuf"
|
||||
}
|
||||
var (
|
||||
path string
|
||||
pathIndex int
|
||||
)
|
||||
// Try more specific path first, then fallback to generic.
|
||||
paths := []string{
|
||||
filepath.Join(config.Directory, fmt.Sprintf("%d.%s.provision"+extension, i, strings.ToLower(config.GetMetadata().GetWorkspaceTransition().String()))),
|
||||
filepath.Join(config.Directory, fmt.Sprintf("%d.provision"+extension, i)),
|
||||
}
|
||||
for pathIndex, path = range paths {
|
||||
_, err := e.filesystem.Stat(path)
|
||||
if err != nil && pathIndex == len(paths)-1 {
|
||||
// If there are zero messages, something is wrong.
|
||||
if i == 0 {
|
||||
// Error if nothing is around to enable failed states.
|
||||
return xerrors.New("no state")
|
||||
}
|
||||
// Otherwise, we're done with the entire provision.
|
||||
break outer
|
||||
} else if err != nil {
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
data, err := afero.ReadFile(e.filesystem, path)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("read file %q: %w", path, err)
|
||||
}
|
||||
var response proto.Provision_Response
|
||||
err = protobuf.Unmarshal(data, &response)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("unmarshal: %w", err)
|
||||
}
|
||||
r, ok := filterLogResponses(config, &response)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
err = stream.Send(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
<-stream.Context().Done()
|
||||
return stream.Context().Err()
|
||||
// some tests use Echo without a complete response to test cancel
|
||||
<-canceledOrComplete
|
||||
return provisionersdk.ApplyErrorf("canceled")
|
||||
}
|
||||
|
||||
func (*echo) Shutdown(_ context.Context, _ *proto.Empty) (*proto.Empty, error) {
|
||||
|
@ -188,29 +196,42 @@ func (*echo) Shutdown(_ context.Context, _ *proto.Empty) (*proto.Empty, error) {
|
|||
|
||||
// Responses is a collection of mocked responses to Provision operations.
|
||||
type Responses struct {
|
||||
Parse []*proto.Parse_Response
|
||||
Parse []*proto.Response
|
||||
|
||||
// ProvisionApply and ProvisionPlan are used to mock ALL responses of
|
||||
// Apply and Plan, regardless of transition.
|
||||
ProvisionApply []*proto.Provision_Response
|
||||
ProvisionPlan []*proto.Provision_Response
|
||||
ProvisionApply []*proto.Response
|
||||
ProvisionPlan []*proto.Response
|
||||
|
||||
// ProvisionApplyMap and ProvisionPlanMap are used to mock specific
|
||||
// transition responses. They are prioritized over the generic responses.
|
||||
ProvisionApplyMap map[proto.WorkspaceTransition][]*proto.Provision_Response
|
||||
ProvisionPlanMap map[proto.WorkspaceTransition][]*proto.Provision_Response
|
||||
ProvisionApplyMap map[proto.WorkspaceTransition][]*proto.Response
|
||||
ProvisionPlanMap map[proto.WorkspaceTransition][]*proto.Response
|
||||
}
|
||||
|
||||
// Tar returns a tar archive of responses to provisioner operations.
|
||||
func Tar(responses *Responses) ([]byte, error) {
|
||||
if responses == nil {
|
||||
responses = &Responses{
|
||||
ParseComplete, ProvisionComplete, ProvisionComplete,
|
||||
ParseComplete, ApplyComplete, PlanComplete,
|
||||
nil, nil,
|
||||
}
|
||||
}
|
||||
if responses.ProvisionPlan == nil {
|
||||
responses.ProvisionPlan = responses.ProvisionApply
|
||||
for _, resp := range responses.ProvisionApply {
|
||||
if resp.GetLog() != nil {
|
||||
responses.ProvisionPlan = append(responses.ProvisionPlan, resp)
|
||||
continue
|
||||
}
|
||||
responses.ProvisionPlan = append(responses.ProvisionPlan, &proto.Response{
|
||||
Type: &proto.Response_Plan{Plan: &proto.PlanComplete{
|
||||
Error: resp.GetApply().GetError(),
|
||||
Resources: resp.GetApply().GetResources(),
|
||||
Parameters: resp.GetApply().GetParameters(),
|
||||
GitAuthProviders: resp.GetApply().GetGitAuthProviders(),
|
||||
}},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
|
@ -245,20 +266,20 @@ func Tar(responses *Responses) ([]byte, error) {
|
|||
}
|
||||
}
|
||||
for index, response := range responses.ProvisionApply {
|
||||
err := writeProto(fmt.Sprintf("%d.provision.apply.protobuf", index), response)
|
||||
err := writeProto(fmt.Sprintf("%d.apply.protobuf", index), response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for index, response := range responses.ProvisionPlan {
|
||||
err := writeProto(fmt.Sprintf("%d.provision.plan.protobuf", index), response)
|
||||
err := writeProto(fmt.Sprintf("%d.plan.protobuf", index), response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for trans, m := range responses.ProvisionApplyMap {
|
||||
for i, rs := range m {
|
||||
err := writeProto(fmt.Sprintf("%d.%s.provision.apply.protobuf", i, strings.ToLower(trans.String())), rs)
|
||||
err := writeProto(fmt.Sprintf("%d.%s.apply.protobuf", i, strings.ToLower(trans.String())), rs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -266,7 +287,7 @@ func Tar(responses *Responses) ([]byte, error) {
|
|||
}
|
||||
for trans, m := range responses.ProvisionPlanMap {
|
||||
for i, rs := range m {
|
||||
err := writeProto(fmt.Sprintf("%d.%s.provision.plan.protobuf", i, strings.ToLower(trans.String())), rs)
|
||||
err := writeProto(fmt.Sprintf("%d.%s.plan.protobuf", i, strings.ToLower(trans.String())), rs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -279,22 +300,14 @@ func Tar(responses *Responses) ([]byte, error) {
|
|||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
func filterLogResponses(config *proto.Provision_Config, response *proto.Provision_Response) (*proto.Provision_Response, bool) {
|
||||
responseLog, ok := response.Type.(*proto.Provision_Response_Log)
|
||||
if !ok {
|
||||
// Pass all non-log responses
|
||||
return response, true
|
||||
func WithResources(resources []*proto.Resource) *Responses {
|
||||
return &Responses{
|
||||
Parse: ParseComplete,
|
||||
ProvisionApply: []*proto.Response{{Type: &proto.Response_Apply{Apply: &proto.ApplyComplete{
|
||||
Resources: resources,
|
||||
}}}},
|
||||
ProvisionPlan: []*proto.Response{{Type: &proto.Response_Plan{Plan: &proto.PlanComplete{
|
||||
Resources: resources,
|
||||
}}}},
|
||||
}
|
||||
|
||||
if config.ProvisionerLogLevel == "" {
|
||||
// Don't change the default behavior of "echo"
|
||||
return response, true
|
||||
}
|
||||
|
||||
provisionerLogLevel := proto.LogLevel_value[strings.ToUpper(config.ProvisionerLogLevel)]
|
||||
if int32(responseLog.Log.Level) < provisionerLogLevel {
|
||||
// Log level is not enabled
|
||||
return nil, false
|
||||
}
|
||||
return response, true
|
||||
}
|
||||
|
|
|
@ -1,27 +1,23 @@
|
|||
package echo_test
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/provisioner/echo"
|
||||
"github.com/coder/coder/v2/provisionersdk"
|
||||
"github.com/coder/coder/v2/provisionersdk/proto"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
|
||||
func TestEcho(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fs := afero.NewMemMapFs()
|
||||
workdir := t.TempDir()
|
||||
|
||||
// Create an in-memory provisioner to communicate with.
|
||||
client, server := provisionersdk.MemTransportPipe()
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
|
@ -31,8 +27,9 @@ func TestEcho(t *testing.T) {
|
|||
cancelFunc()
|
||||
})
|
||||
go func() {
|
||||
err := echo.Serve(ctx, fs, &provisionersdk.ServeOptions{
|
||||
Listener: server,
|
||||
err := echo.Serve(ctx, &provisionersdk.ServeOptions{
|
||||
Listener: server,
|
||||
WorkDirectory: workdir,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
}()
|
||||
|
@ -40,25 +37,39 @@ func TestEcho(t *testing.T) {
|
|||
|
||||
t.Run("Parse", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, cancel := context.WithTimeout(ctx, testutil.WaitShort)
|
||||
defer cancel()
|
||||
|
||||
responses := []*proto.Parse_Response{{
|
||||
Type: &proto.Parse_Response_Log{
|
||||
Log: &proto.Log{
|
||||
Output: "log-output",
|
||||
responses := []*proto.Response{
|
||||
{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{
|
||||
Output: "log-output",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Type: &proto.Parse_Response_Complete{
|
||||
Complete: &proto.Parse_Complete{},
|
||||
{
|
||||
Type: &proto.Response_Parse{
|
||||
Parse: &proto.ParseComplete{},
|
||||
},
|
||||
},
|
||||
}}
|
||||
}
|
||||
data, err := echo.Tar(&echo.Responses{
|
||||
Parse: responses,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
client, err := api.Parse(ctx, &proto.Parse_Request{
|
||||
Directory: unpackTar(t, fs, data),
|
||||
})
|
||||
client, err := api.Session(ctx)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err := client.Close()
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
err = client.Send(&proto.Request{Type: &proto.Request_Config{Config: &proto.Config{
|
||||
TemplateSourceArchive: data,
|
||||
}}})
|
||||
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)
|
||||
|
@ -70,95 +81,117 @@ func TestEcho(t *testing.T) {
|
|||
|
||||
t.Run("Provision", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, cancel := context.WithTimeout(ctx, testutil.WaitShort)
|
||||
defer cancel()
|
||||
|
||||
responses := []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
Log: &proto.Log{
|
||||
Level: proto.LogLevel_INFO,
|
||||
Output: "log-output",
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "resource",
|
||||
}},
|
||||
},
|
||||
},
|
||||
}}
|
||||
data, err := echo.Tar(&echo.Responses{
|
||||
ProvisionApply: responses,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
client, err := api.Provision(ctx)
|
||||
require.NoError(t, err)
|
||||
err = client.Send(&proto.Provision_Request{
|
||||
Type: &proto.Provision_Request_Plan{
|
||||
Plan: &proto.Provision_Plan{
|
||||
Config: &proto.Provision_Config{
|
||||
Directory: unpackTar(t, fs, data),
|
||||
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{
|
||||
{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{
|
||||
Level: proto.LogLevel_INFO,
|
||||
Output: "log-output",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "resource",
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
data, err := echo.Tar(&echo.Responses{
|
||||
ProvisionPlan: planResponses,
|
||||
ProvisionApply: applyResponses,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
client, err := api.Session(ctx)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err := client.Close()
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
err = client.Send(&proto.Request{Type: &proto.Request_Config{Config: &proto.Config{
|
||||
TemplateSourceArchive: data,
|
||||
}}})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = client.Send(&proto.Request{Type: &proto.Request_Plan{Plan: &proto.PlanRequest{}}})
|
||||
require.NoError(t, err)
|
||||
log, err := client.Recv()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, responses[0].GetLog().Output, log.GetLog().Output)
|
||||
require.Equal(t, planResponses[0].GetLog().Output, log.GetLog().Output)
|
||||
complete, err := client.Recv()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, responses[1].GetComplete().Resources[0].Name,
|
||||
complete.GetComplete().Resources[0].Name)
|
||||
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()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, applyResponses[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)
|
||||
})
|
||||
|
||||
t.Run("ProvisionStop", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Stop responses should be returned when the workspace is being stopped.
|
||||
|
||||
defaultResponses := []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "DEFAULT",
|
||||
}},
|
||||
},
|
||||
},
|
||||
}}
|
||||
stopResponses := []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "STOP",
|
||||
}},
|
||||
},
|
||||
},
|
||||
}}
|
||||
data, err := echo.Tar(&echo.Responses{
|
||||
ProvisionApply: defaultResponses,
|
||||
ProvisionPlan: defaultResponses,
|
||||
ProvisionPlanMap: map[proto.WorkspaceTransition][]*proto.Provision_Response{
|
||||
proto.WorkspaceTransition_STOP: stopResponses,
|
||||
ProvisionApply: applyCompleteResource("DEFAULT"),
|
||||
ProvisionPlan: planCompleteResource("DEFAULT"),
|
||||
ProvisionPlanMap: map[proto.WorkspaceTransition][]*proto.Response{
|
||||
proto.WorkspaceTransition_STOP: planCompleteResource("STOP"),
|
||||
},
|
||||
ProvisionApplyMap: map[proto.WorkspaceTransition][]*proto.Provision_Response{
|
||||
proto.WorkspaceTransition_STOP: stopResponses,
|
||||
ProvisionApplyMap: map[proto.WorkspaceTransition][]*proto.Response{
|
||||
proto.WorkspaceTransition_STOP: applyCompleteResource("STOP"),
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
client, err := api.Provision(ctx)
|
||||
client, err := api.Session(ctx)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err := client.Close()
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
err = client.Send(&proto.Request{Type: &proto.Request_Config{Config: &proto.Config{
|
||||
TemplateSourceArchive: data,
|
||||
}}})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Do stop.
|
||||
err = client.Send(&proto.Provision_Request{
|
||||
Type: &proto.Provision_Request_Plan{
|
||||
Plan: &proto.Provision_Plan{
|
||||
Config: &proto.Provision_Config{
|
||||
Directory: unpackTar(t, fs, data),
|
||||
Metadata: &proto.Provision_Metadata{
|
||||
WorkspaceTransition: proto.WorkspaceTransition_STOP,
|
||||
},
|
||||
err = client.Send(&proto.Request{
|
||||
Type: &proto.Request_Plan{
|
||||
Plan: &proto.PlanRequest{
|
||||
Metadata: &proto.Metadata{
|
||||
WorkspaceTransition: proto.WorkspaceTransition_STOP,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -168,22 +201,16 @@ func TestEcho(t *testing.T) {
|
|||
complete, err := client.Recv()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t,
|
||||
stopResponses[0].GetComplete().Resources[0].Name,
|
||||
complete.GetComplete().Resources[0].Name,
|
||||
"STOP",
|
||||
complete.GetPlan().Resources[0].Name,
|
||||
)
|
||||
|
||||
// Do start.
|
||||
client, err = api.Provision(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = client.Send(&proto.Provision_Request{
|
||||
Type: &proto.Provision_Request_Plan{
|
||||
Plan: &proto.Provision_Plan{
|
||||
Config: &proto.Provision_Config{
|
||||
Directory: unpackTar(t, fs, data),
|
||||
Metadata: &proto.Provision_Metadata{
|
||||
WorkspaceTransition: proto.WorkspaceTransition_START,
|
||||
},
|
||||
err = client.Send(&proto.Request{
|
||||
Type: &proto.Request_Plan{
|
||||
Plan: &proto.PlanRequest{
|
||||
Metadata: &proto.Metadata{
|
||||
WorkspaceTransition: proto.WorkspaceTransition_START,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -193,31 +220,33 @@ func TestEcho(t *testing.T) {
|
|||
complete, err = client.Recv()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t,
|
||||
defaultResponses[0].GetComplete().Resources[0].Name,
|
||||
complete.GetComplete().Resources[0].Name,
|
||||
"DEFAULT",
|
||||
complete.GetPlan().Resources[0].Name,
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("ProvisionWithLogLevel", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, cancel := context.WithTimeout(ctx, testutil.WaitShort)
|
||||
defer cancel()
|
||||
|
||||
responses := []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
responses := []*proto.Response{{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{
|
||||
Level: proto.LogLevel_TRACE,
|
||||
Output: "log-output-trace",
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Type: &proto.Provision_Response_Log{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{
|
||||
Level: proto.LogLevel_INFO,
|
||||
Output: "log-output-info",
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "resource",
|
||||
}},
|
||||
|
@ -225,49 +254,62 @@ func TestEcho(t *testing.T) {
|
|||
},
|
||||
}}
|
||||
data, err := echo.Tar(&echo.Responses{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: responses,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
client, err := api.Provision(ctx)
|
||||
client, err := api.Session(ctx)
|
||||
require.NoError(t, err)
|
||||
err = client.Send(&proto.Provision_Request{
|
||||
Type: &proto.Provision_Request_Plan{
|
||||
Plan: &proto.Provision_Plan{
|
||||
Config: &proto.Provision_Config{
|
||||
Directory: unpackTar(t, fs, data),
|
||||
ProvisionerLogLevel: "debug",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
defer func() {
|
||||
err := client.Close()
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
err = client.Send(&proto.Request{Type: &proto.Request_Config{Config: &proto.Config{
|
||||
TemplateSourceArchive: data,
|
||||
ProvisionerLogLevel: "debug",
|
||||
}}})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Plan is required before apply
|
||||
err = client.Send(&proto.Request{Type: &proto.Request_Plan{Plan: &proto.PlanRequest{}}})
|
||||
require.NoError(t, err)
|
||||
complete, err := client.Recv()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, complete.GetPlan())
|
||||
|
||||
err = client.Send(&proto.Request{Type: &proto.Request_Apply{Apply: &proto.ApplyRequest{}}})
|
||||
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()
|
||||
complete, err = client.Recv()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, responses[2].GetComplete().Resources[0].Name,
|
||||
complete.GetComplete().Resources[0].Name)
|
||||
require.Equal(t, responses[2].GetApply().Resources[0].Name,
|
||||
complete.GetApply().Resources[0].Name)
|
||||
})
|
||||
}
|
||||
|
||||
func unpackTar(t *testing.T, fs afero.Fs, data []byte) string {
|
||||
directory := t.TempDir()
|
||||
reader := tar.NewReader(bytes.NewReader(data))
|
||||
for {
|
||||
header, err := reader.Next()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
// #nosec
|
||||
path := filepath.Join(directory, header.Name)
|
||||
file, err := fs.OpenFile(path, os.O_CREATE|os.O_RDWR, 0o600)
|
||||
require.NoError(t, err)
|
||||
_, err = io.CopyN(file, reader, 1<<20)
|
||||
require.ErrorIs(t, err, io.EOF)
|
||||
err = file.Close()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
return directory
|
||||
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 {
|
||||
return []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: name,
|
||||
}},
|
||||
},
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
)
|
||||
|
||||
type executor struct {
|
||||
logger slog.Logger
|
||||
server *server
|
||||
mut *sync.Mutex
|
||||
binaryPath string
|
||||
|
@ -50,8 +51,10 @@ func (e *executor) execWriteOutput(ctx, killCtx context.Context, args, env []str
|
|||
ctx, span := e.server.startTrace(ctx, fmt.Sprintf("exec - terraform %s", args[0]))
|
||||
defer span.End()
|
||||
span.SetAttributes(attribute.StringSlice("args", args))
|
||||
e.logger.Debug(ctx, "starting command", slog.F("args", args))
|
||||
|
||||
defer func() {
|
||||
e.logger.Debug(ctx, "closing writers", slog.Error(err))
|
||||
closeErr := stdOutWriter.Close()
|
||||
if err == nil && closeErr != nil {
|
||||
err = closeErr
|
||||
|
@ -62,6 +65,7 @@ func (e *executor) execWriteOutput(ctx, killCtx context.Context, args, env []str
|
|||
}
|
||||
}()
|
||||
if ctx.Err() != nil {
|
||||
e.logger.Debug(ctx, "context canceled before command started", slog.F("args", args))
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
|
@ -90,11 +94,14 @@ func (e *executor) execWriteOutput(ctx, killCtx context.Context, args, env []str
|
|||
)
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
e.logger.Debug(ctx, "failed to start command", slog.F("args", args))
|
||||
return err
|
||||
}
|
||||
interruptCommandOnCancel(ctx, killCtx, cmd)
|
||||
interruptCommandOnCancel(ctx, killCtx, e.logger, cmd)
|
||||
|
||||
return cmd.Wait()
|
||||
err = cmd.Wait()
|
||||
e.logger.Debug(ctx, "command done", slog.F("args", args), slog.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
// execParseJSON must only be called while the lock is held.
|
||||
|
@ -120,7 +127,7 @@ func (e *executor) execParseJSON(ctx, killCtx context.Context, args, env []strin
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
interruptCommandOnCancel(ctx, killCtx, cmd)
|
||||
interruptCommandOnCancel(ctx, killCtx, e.logger, cmd)
|
||||
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
|
@ -207,15 +214,23 @@ func (e *executor) init(ctx, killCtx context.Context, logr logSink) error {
|
|||
return e.execWriteOutput(ctx, killCtx, args, e.basicEnv(), outWriter, errWriter)
|
||||
}
|
||||
|
||||
func getPlanFilePath(workdir string) string {
|
||||
return filepath.Join(workdir, "terraform.tfplan")
|
||||
}
|
||||
|
||||
func getStateFilePath(workdir string) string {
|
||||
return filepath.Join(workdir, "terraform.tfstate")
|
||||
}
|
||||
|
||||
// revive:disable-next-line:flag-parameter
|
||||
func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr logSink, destroy bool) (*proto.Provision_Response, error) {
|
||||
func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr logSink, destroy bool) (*proto.PlanComplete, error) {
|
||||
ctx, span := e.server.startTrace(ctx, tracing.FuncName())
|
||||
defer span.End()
|
||||
|
||||
e.mut.Lock()
|
||||
defer e.mut.Unlock()
|
||||
|
||||
planfilePath := filepath.Join(e.workdir, "terraform.tfplan")
|
||||
planfilePath := getPlanFilePath(e.workdir)
|
||||
args := []string{
|
||||
"plan",
|
||||
"-no-color",
|
||||
|
@ -248,19 +263,10 @@ func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr l
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
planFileByt, err := os.ReadFile(planfilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &proto.Provision_Response{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Parameters: state.Parameters,
|
||||
Resources: state.Resources,
|
||||
GitAuthProviders: state.GitAuthProviders,
|
||||
Plan: planFileByt,
|
||||
},
|
||||
},
|
||||
return &proto.PlanComplete{
|
||||
Parameters: state.Parameters,
|
||||
Resources: state.Resources,
|
||||
GitAuthProviders: state.GitAuthProviders,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -346,7 +352,7 @@ func (e *executor) graph(ctx, killCtx context.Context) (string, error) {
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
interruptCommandOnCancel(ctx, killCtx, cmd)
|
||||
interruptCommandOnCancel(ctx, killCtx, e.logger, cmd)
|
||||
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
|
@ -357,33 +363,22 @@ func (e *executor) graph(ctx, killCtx context.Context) (string, error) {
|
|||
|
||||
func (e *executor) apply(
|
||||
ctx, killCtx context.Context,
|
||||
plan []byte,
|
||||
env []string,
|
||||
logr logSink,
|
||||
) (*proto.Provision_Response, error) {
|
||||
) (*proto.ApplyComplete, error) {
|
||||
ctx, span := e.server.startTrace(ctx, tracing.FuncName())
|
||||
defer span.End()
|
||||
|
||||
e.mut.Lock()
|
||||
defer e.mut.Unlock()
|
||||
|
||||
planFile, err := os.CreateTemp("", "coder-terrafrom-plan")
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("create plan file: %w", err)
|
||||
}
|
||||
_, err = planFile.Write(plan)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("write plan file: %w", err)
|
||||
}
|
||||
defer os.Remove(planFile.Name())
|
||||
|
||||
args := []string{
|
||||
"apply",
|
||||
"-no-color",
|
||||
"-auto-approve",
|
||||
"-input=false",
|
||||
"-json",
|
||||
planFile.Name(),
|
||||
getPlanFilePath(e.workdir),
|
||||
}
|
||||
|
||||
outWriter, doneOut := provisionLogWriter(logr)
|
||||
|
@ -395,7 +390,7 @@ func (e *executor) apply(
|
|||
<-doneErr
|
||||
}()
|
||||
|
||||
err = e.execWriteOutput(ctx, killCtx, args, env, outWriter, errWriter)
|
||||
err := e.execWriteOutput(ctx, killCtx, args, env, outWriter, errWriter)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("terraform apply: %w", err)
|
||||
}
|
||||
|
@ -408,15 +403,11 @@ func (e *executor) apply(
|
|||
if err != nil {
|
||||
return nil, xerrors.Errorf("read statefile %q: %w", statefilePath, err)
|
||||
}
|
||||
return &proto.Provision_Response{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Parameters: state.Parameters,
|
||||
Resources: state.Resources,
|
||||
GitAuthProviders: state.GitAuthProviders,
|
||||
State: stateContent,
|
||||
},
|
||||
},
|
||||
return &proto.ApplyComplete{
|
||||
Parameters: state.Parameters,
|
||||
Resources: state.Resources,
|
||||
GitAuthProviders: state.GitAuthProviders,
|
||||
State: stateContent,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -461,48 +452,28 @@ func (e *executor) state(ctx, killCtx context.Context) (*tfjson.State, error) {
|
|||
return state, nil
|
||||
}
|
||||
|
||||
func interruptCommandOnCancel(ctx, killCtx context.Context, cmd *exec.Cmd) {
|
||||
func interruptCommandOnCancel(ctx, killCtx context.Context, logger slog.Logger, cmd *exec.Cmd) {
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
var err error
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
// Interrupts aren't supported by Windows.
|
||||
_ = cmd.Process.Kill()
|
||||
err = cmd.Process.Kill()
|
||||
default:
|
||||
_ = cmd.Process.Signal(os.Interrupt)
|
||||
err = cmd.Process.Signal(os.Interrupt)
|
||||
}
|
||||
logger.Debug(ctx, "interrupted command", slog.F("args", cmd.Args), slog.Error(err))
|
||||
|
||||
case <-killCtx.Done():
|
||||
logger.Debug(ctx, "kill context ended", slog.F("args", cmd.Args))
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
type logSink interface {
|
||||
Log(*proto.Log)
|
||||
}
|
||||
|
||||
type streamLogSink struct {
|
||||
// Any errors writing to the stream will be logged to logger.
|
||||
logger slog.Logger
|
||||
stream proto.DRPCProvisioner_ProvisionStream
|
||||
}
|
||||
|
||||
var _ logSink = streamLogSink{}
|
||||
|
||||
func (s streamLogSink) Log(l *proto.Log) {
|
||||
err := s.stream.Send(&proto.Provision_Response{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
Log: l,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
s.logger.Warn(context.Background(), "write log to stream",
|
||||
slog.F("level", l.Level.String()),
|
||||
slog.F("message", l.Output),
|
||||
slog.Error(err),
|
||||
)
|
||||
}
|
||||
ProvisionLog(l proto.LogLevel, o string)
|
||||
}
|
||||
|
||||
// logWriter creates a WriteCloser that will log each line of text at the given level. The WriteCloser must be closed
|
||||
|
@ -526,7 +497,7 @@ func readAndLog(sink logSink, r io.Reader, done chan<- any, level proto.LogLevel
|
|||
continue
|
||||
}
|
||||
|
||||
sink.Log(&proto.Log{Level: level, Output: scanner.Text()})
|
||||
sink.ProvisionLog(level, scanner.Text())
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -543,7 +514,7 @@ func readAndLog(sink logSink, r io.Reader, done chan<- any, level proto.LogLevel
|
|||
if logLevel == proto.LogLevel_INFO {
|
||||
logLevel = proto.LogLevel_DEBUG
|
||||
}
|
||||
sink.Log(&proto.Log{Level: logLevel, Output: log.Message})
|
||||
sink.ProvisionLog(logLevel, log.Message)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -588,7 +559,7 @@ func provisionReadAndLog(sink logSink, r io.Reader, done chan<- any) {
|
|||
}
|
||||
|
||||
logLevel := convertTerraformLogLevel(log.Level, sink)
|
||||
sink.Log(&proto.Log{Level: logLevel, Output: log.Message})
|
||||
sink.ProvisionLog(logLevel, log.Message)
|
||||
|
||||
// If the diagnostic is provided, let's provide a bit more info!
|
||||
if log.Diagnostic == nil {
|
||||
|
@ -596,7 +567,7 @@ func provisionReadAndLog(sink logSink, r io.Reader, done chan<- any) {
|
|||
}
|
||||
logLevel = convertTerraformLogLevel(string(log.Diagnostic.Severity), sink)
|
||||
for _, diagLine := range strings.Split(FormatDiagnostic(log.Diagnostic), "\n") {
|
||||
sink.Log(&proto.Log{Level: logLevel, Output: diagLine})
|
||||
sink.ProvisionLog(logLevel, diagLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -614,10 +585,7 @@ func convertTerraformLogLevel(logLevel string, sink logSink) proto.LogLevel {
|
|||
case "error":
|
||||
return proto.LogLevel_ERROR
|
||||
default:
|
||||
sink.Log(&proto.Log{
|
||||
Level: proto.LogLevel_WARN,
|
||||
Output: fmt.Sprintf("unable to convert log level %s", logLevel),
|
||||
})
|
||||
sink.ProvisionLog(proto.LogLevel_WARN, fmt.Sprintf("unable to convert log level %s", logLevel))
|
||||
return proto.LogLevel_INFO
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@ type mockLogger struct {
|
|||
|
||||
var _ logSink = &mockLogger{}
|
||||
|
||||
func (m *mockLogger) Log(l *proto.Log) {
|
||||
m.logs = append(m.logs, l)
|
||||
func (m *mockLogger) ProvisionLog(l proto.LogLevel, o string) {
|
||||
m.logs = append(m.logs, &proto.Log{Level: l, Output: o})
|
||||
}
|
||||
|
||||
func TestLogWriter_Mainline(t *testing.T) {
|
||||
|
|
|
@ -12,18 +12,20 @@ import (
|
|||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/tracing"
|
||||
"github.com/coder/coder/v2/provisionersdk"
|
||||
"github.com/coder/coder/v2/provisionersdk/proto"
|
||||
)
|
||||
|
||||
// Parse extracts Terraform variables from source-code.
|
||||
func (s *server) Parse(request *proto.Parse_Request, stream proto.DRPCProvisioner_ParseStream) error {
|
||||
_, span := s.startTrace(stream.Context(), tracing.FuncName())
|
||||
func (s *server) Parse(sess *provisionersdk.Session, _ *proto.ParseRequest, _ <-chan struct{}) *proto.ParseComplete {
|
||||
ctx := sess.Context()
|
||||
_, span := s.startTrace(ctx, tracing.FuncName())
|
||||
defer span.End()
|
||||
|
||||
// Load the module and print any parse errors.
|
||||
module, diags := tfconfig.LoadModule(request.Directory)
|
||||
module, diags := tfconfig.LoadModule(sess.WorkDirectory)
|
||||
if diags.HasErrors() {
|
||||
return xerrors.Errorf("load module: %s", formatDiagnostics(request.Directory, diags))
|
||||
return provisionersdk.ParseErrorf("load module: %s", formatDiagnostics(sess.WorkDirectory, diags))
|
||||
}
|
||||
|
||||
// Sort variables by (filename, line) to make the ordering consistent
|
||||
|
@ -40,17 +42,13 @@ func (s *server) Parse(request *proto.Parse_Request, stream proto.DRPCProvisione
|
|||
for _, v := range variables {
|
||||
mv, err := convertTerraformVariable(v)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("can't convert the Terraform variable to a managed one: %w", err)
|
||||
return provisionersdk.ParseErrorf("can't convert the Terraform variable to a managed one: %s", err)
|
||||
}
|
||||
templateVariables = append(templateVariables, mv)
|
||||
}
|
||||
return stream.Send(&proto.Parse_Response{
|
||||
Type: &proto.Parse_Response_Complete{
|
||||
Complete: &proto.Parse_Complete{
|
||||
TemplateVariables: templateVariables,
|
||||
},
|
||||
},
|
||||
})
|
||||
return &proto.ParseComplete{
|
||||
TemplateVariables: templateVariables,
|
||||
}
|
||||
}
|
||||
|
||||
// Converts a Terraform variable to a template-wide variable, processed by Coder.
|
||||
|
|
|
@ -4,8 +4,6 @@ package terraform_test
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -21,9 +19,8 @@ func TestParse(t *testing.T) {
|
|||
testCases := []struct {
|
||||
Name string
|
||||
Files map[string]string
|
||||
Response *proto.Parse_Response
|
||||
// If ErrorContains is not empty, then response.Recv() should return an
|
||||
// error containing this string before a Complete response is returned.
|
||||
Response *proto.ParseComplete
|
||||
// If ErrorContains is not empty, then the ParseComplete should have an Error containing the given string
|
||||
ErrorContains string
|
||||
}{
|
||||
{
|
||||
|
@ -33,16 +30,12 @@ func TestParse(t *testing.T) {
|
|||
description = "Testing!"
|
||||
}`,
|
||||
},
|
||||
Response: &proto.Parse_Response{
|
||||
Type: &proto.Parse_Response_Complete{
|
||||
Complete: &proto.Parse_Complete{
|
||||
TemplateVariables: []*proto.TemplateVariable{
|
||||
{
|
||||
Name: "A",
|
||||
Description: "Testing!",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Response: &proto.ParseComplete{
|
||||
TemplateVariables: []*proto.TemplateVariable{
|
||||
{
|
||||
Name: "A",
|
||||
Description: "Testing!",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -54,15 +47,11 @@ func TestParse(t *testing.T) {
|
|||
default = "wow"
|
||||
}`,
|
||||
},
|
||||
Response: &proto.Parse_Response{
|
||||
Type: &proto.Parse_Response_Complete{
|
||||
Complete: &proto.Parse_Complete{
|
||||
TemplateVariables: []*proto.TemplateVariable{
|
||||
{
|
||||
Name: "A",
|
||||
DefaultValue: "wow",
|
||||
},
|
||||
},
|
||||
Response: &proto.ParseComplete{
|
||||
TemplateVariables: []*proto.TemplateVariable{
|
||||
{
|
||||
Name: "A",
|
||||
DefaultValue: "wow",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -76,15 +65,11 @@ func TestParse(t *testing.T) {
|
|||
}
|
||||
}`,
|
||||
},
|
||||
Response: &proto.Parse_Response{
|
||||
Type: &proto.Parse_Response_Complete{
|
||||
Complete: &proto.Parse_Complete{
|
||||
TemplateVariables: []*proto.TemplateVariable{
|
||||
{
|
||||
Name: "A",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Response: &proto.ParseComplete{
|
||||
TemplateVariables: []*proto.TemplateVariable{
|
||||
{
|
||||
Name: "A",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -104,27 +89,23 @@ func TestParse(t *testing.T) {
|
|||
"main2.tf": `variable "baz" { }
|
||||
variable "quux" { }`,
|
||||
},
|
||||
Response: &proto.Parse_Response{
|
||||
Type: &proto.Parse_Response_Complete{
|
||||
Complete: &proto.Parse_Complete{
|
||||
TemplateVariables: []*proto.TemplateVariable{
|
||||
{
|
||||
Name: "foo",
|
||||
Required: true,
|
||||
},
|
||||
{
|
||||
Name: "bar",
|
||||
Required: true,
|
||||
},
|
||||
{
|
||||
Name: "baz",
|
||||
Required: true,
|
||||
},
|
||||
{
|
||||
Name: "quux",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Response: &proto.ParseComplete{
|
||||
TemplateVariables: []*proto.TemplateVariable{
|
||||
{
|
||||
Name: "foo",
|
||||
Required: true,
|
||||
},
|
||||
{
|
||||
Name: "bar",
|
||||
Required: true,
|
||||
},
|
||||
{
|
||||
Name: "baz",
|
||||
Required: true,
|
||||
},
|
||||
{
|
||||
Name: "quux",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -139,19 +120,15 @@ func TestParse(t *testing.T) {
|
|||
sensitive = true
|
||||
}`,
|
||||
},
|
||||
Response: &proto.Parse_Response{
|
||||
Type: &proto.Parse_Response_Complete{
|
||||
Complete: &proto.Parse_Complete{
|
||||
TemplateVariables: []*proto.TemplateVariable{
|
||||
{
|
||||
Name: "A",
|
||||
Description: "Testing!",
|
||||
Type: "bool",
|
||||
DefaultValue: "true",
|
||||
Required: false,
|
||||
Sensitive: true,
|
||||
},
|
||||
},
|
||||
Response: &proto.ParseComplete{
|
||||
TemplateVariables: []*proto.TemplateVariable{
|
||||
{
|
||||
Name: "A",
|
||||
Description: "Testing!",
|
||||
Type: "bool",
|
||||
DefaultValue: "true",
|
||||
Required: false,
|
||||
Sensitive: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -166,19 +143,15 @@ func TestParse(t *testing.T) {
|
|||
sensitive = true
|
||||
}`,
|
||||
},
|
||||
Response: &proto.Parse_Response{
|
||||
Type: &proto.Parse_Response_Complete{
|
||||
Complete: &proto.Parse_Complete{
|
||||
TemplateVariables: []*proto.TemplateVariable{
|
||||
{
|
||||
Name: "A",
|
||||
Description: "Testing!",
|
||||
Type: "string",
|
||||
DefaultValue: "abc",
|
||||
Required: false,
|
||||
Sensitive: true,
|
||||
},
|
||||
},
|
||||
Response: &proto.ParseComplete{
|
||||
TemplateVariables: []*proto.TemplateVariable{
|
||||
{
|
||||
Name: "A",
|
||||
Description: "Testing!",
|
||||
Type: "string",
|
||||
DefaultValue: "abc",
|
||||
Required: false,
|
||||
Sensitive: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -193,19 +166,15 @@ func TestParse(t *testing.T) {
|
|||
sensitive = true
|
||||
}`,
|
||||
},
|
||||
Response: &proto.Parse_Response{
|
||||
Type: &proto.Parse_Response_Complete{
|
||||
Complete: &proto.Parse_Complete{
|
||||
TemplateVariables: []*proto.TemplateVariable{
|
||||
{
|
||||
Name: "A",
|
||||
Description: "Testing!",
|
||||
Type: "string",
|
||||
DefaultValue: "",
|
||||
Required: false,
|
||||
Sensitive: true,
|
||||
},
|
||||
},
|
||||
Response: &proto.ParseComplete{
|
||||
TemplateVariables: []*proto.TemplateVariable{
|
||||
{
|
||||
Name: "A",
|
||||
Description: "Testing!",
|
||||
Type: "string",
|
||||
DefaultValue: "",
|
||||
Required: false,
|
||||
Sensitive: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -219,19 +188,15 @@ func TestParse(t *testing.T) {
|
|||
sensitive = true
|
||||
}`,
|
||||
},
|
||||
Response: &proto.Parse_Response{
|
||||
Type: &proto.Parse_Response_Complete{
|
||||
Complete: &proto.Parse_Complete{
|
||||
TemplateVariables: []*proto.TemplateVariable{
|
||||
{
|
||||
Name: "A",
|
||||
Description: "Testing!",
|
||||
Type: "string",
|
||||
DefaultValue: "",
|
||||
Required: true,
|
||||
Sensitive: true,
|
||||
},
|
||||
},
|
||||
Response: &proto.ParseComplete{
|
||||
TemplateVariables: []*proto.TemplateVariable{
|
||||
{
|
||||
Name: "A",
|
||||
Description: "Testing!",
|
||||
Type: "string",
|
||||
DefaultValue: "",
|
||||
Required: true,
|
||||
Sensitive: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -243,40 +208,31 @@ func TestParse(t *testing.T) {
|
|||
t.Run(testCase.Name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Write all files to the temporary test directory.
|
||||
directory := t.TempDir()
|
||||
for path, content := range testCase.Files {
|
||||
err := os.WriteFile(filepath.Join(directory, path), []byte(content), 0o600)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
response, err := api.Parse(ctx, &proto.Parse_Request{
|
||||
Directory: directory,
|
||||
session := configure(ctx, t, api, &proto.Config{
|
||||
TemplateSourceArchive: makeTar(t, testCase.Files),
|
||||
})
|
||||
|
||||
err := session.Send(&proto.Request{Type: &proto.Request_Parse{Parse: &proto.ParseRequest{}}})
|
||||
require.NoError(t, err)
|
||||
|
||||
for {
|
||||
msg, err := response.Recv()
|
||||
if err != nil {
|
||||
if testCase.ErrorContains != "" {
|
||||
require.ErrorContains(t, err, testCase.ErrorContains)
|
||||
break
|
||||
}
|
||||
msg, err := session.Recv()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
if msg.GetComplete() == nil {
|
||||
continue
|
||||
}
|
||||
if testCase.ErrorContains != "" {
|
||||
t.Fatal("expected error but job completed successfully")
|
||||
require.Contains(t, msg.GetParse().GetError(), testCase.ErrorContains)
|
||||
break
|
||||
}
|
||||
|
||||
// Ignore logs in this test
|
||||
if msg.GetLog() != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Ensure the want and got are equivalent!
|
||||
want, err := json.Marshal(testCase.Response)
|
||||
require.NoError(t, err)
|
||||
got, err := json.Marshal(msg)
|
||||
got, err := json.Marshal(msg.GetParse())
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, string(want), string(got))
|
||||
|
|
|
@ -4,11 +4,10 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
"cdr.dev/slog"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/tracing"
|
||||
"github.com/coder/coder/v2/provisionersdk"
|
||||
|
@ -16,48 +15,23 @@ import (
|
|||
"github.com/coder/terraform-provider-coder/provider"
|
||||
)
|
||||
|
||||
// Provision executes `terraform apply` or `terraform plan` for dry runs.
|
||||
func (s *server) Provision(stream proto.DRPCProvisioner_ProvisionStream) error {
|
||||
ctx, span := s.startTrace(stream.Context(), tracing.FuncName())
|
||||
defer span.End()
|
||||
|
||||
request, err := stream.Recv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if request.GetCancel() != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
applyRequest = request.GetApply()
|
||||
planRequest = request.GetPlan()
|
||||
)
|
||||
|
||||
var config *proto.Provision_Config
|
||||
if applyRequest == nil && planRequest == nil {
|
||||
return nil
|
||||
} else if applyRequest != nil {
|
||||
config = applyRequest.Config
|
||||
} else if planRequest != nil {
|
||||
config = planRequest.Config
|
||||
}
|
||||
|
||||
// Create a context for graceful cancellation bound to the stream
|
||||
func (s *server) setupContexts(parent context.Context, canceledOrComplete <-chan struct{}) (
|
||||
ctx context.Context, cancel func(), killCtx context.Context, kill func(),
|
||||
) {
|
||||
// Create a context for graceful cancellation bound to the session
|
||||
// context. This ensures that we will perform graceful cancellation
|
||||
// even on connection loss.
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
ctx, cancel = context.WithCancel(parent)
|
||||
|
||||
// Create a separate context for forceful cancellation not tied to
|
||||
// the stream so that we can control when to terminate the process.
|
||||
killCtx, kill := context.WithCancel(context.Background())
|
||||
defer kill()
|
||||
killCtx, kill = context.WithCancel(context.Background())
|
||||
|
||||
// Ensure processes are eventually cleaned up on graceful
|
||||
// cancellation or disconnect.
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
s.logger.Debug(ctx, "graceful context done")
|
||||
|
||||
// TODO(mafredri): We should track this provision request as
|
||||
// part of graceful server shutdown procedure. Waiting on a
|
||||
|
@ -66,134 +40,131 @@ func (s *server) Provision(stream proto.DRPCProvisioner_ProvisionStream) error {
|
|||
defer t.Stop()
|
||||
select {
|
||||
case <-t.C:
|
||||
s.logger.Debug(ctx, "exit timeout hit")
|
||||
kill()
|
||||
case <-killCtx.Done():
|
||||
s.logger.Debug(ctx, "kill context done")
|
||||
}
|
||||
}()
|
||||
|
||||
// Process cancel
|
||||
go func() {
|
||||
for {
|
||||
request, err := stream.Recv()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if request.GetCancel() == nil {
|
||||
// We only process cancellation requests here.
|
||||
continue
|
||||
}
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
<-canceledOrComplete
|
||||
s.logger.Debug(ctx, "canceledOrComplete closed")
|
||||
cancel()
|
||||
}()
|
||||
return ctx, cancel, killCtx, kill
|
||||
}
|
||||
|
||||
sink := streamLogSink{
|
||||
logger: s.logger.Named("execution_logs"),
|
||||
stream: stream,
|
||||
}
|
||||
func (s *server) Plan(
|
||||
sess *provisionersdk.Session, request *proto.PlanRequest, canceledOrComplete <-chan struct{},
|
||||
) *proto.PlanComplete {
|
||||
ctx, span := s.startTrace(sess.Context(), tracing.FuncName())
|
||||
defer span.End()
|
||||
ctx, cancel, killCtx, kill := s.setupContexts(ctx, canceledOrComplete)
|
||||
defer cancel()
|
||||
defer kill()
|
||||
|
||||
e := s.executor(config.Directory)
|
||||
if err = e.checkMinVersion(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
logTerraformEnvVars(sink)
|
||||
|
||||
statefilePath := filepath.Join(config.Directory, "terraform.tfstate")
|
||||
if len(config.State) > 0 {
|
||||
err = os.WriteFile(statefilePath, config.State, 0o600)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("write statefile %q: %w", statefilePath, err)
|
||||
}
|
||||
e := s.executor(sess.WorkDirectory)
|
||||
if err := e.checkMinVersion(ctx); err != nil {
|
||||
return provisionersdk.PlanErrorf(err.Error())
|
||||
}
|
||||
logTerraformEnvVars(sess)
|
||||
|
||||
// If we're destroying, exit early if there's no state. This is necessary to
|
||||
// avoid any cases where a workspace is "locked out" of terraform due to
|
||||
// e.g. bad template param values and cannot be deleted. This is just for
|
||||
// contingency, in the future we will try harder to prevent workspaces being
|
||||
// broken this hard.
|
||||
if config.Metadata.WorkspaceTransition == proto.WorkspaceTransition_DESTROY && len(config.State) == 0 {
|
||||
_ = stream.Send(&proto.Provision_Response{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
Log: &proto.Log{
|
||||
Level: proto.LogLevel_INFO,
|
||||
Output: "The terraform state does not exist, there is nothing to do",
|
||||
},
|
||||
},
|
||||
})
|
||||
if request.Metadata.GetWorkspaceTransition() == proto.WorkspaceTransition_DESTROY && len(sess.Config.State) == 0 {
|
||||
sess.ProvisionLog(proto.LogLevel_INFO, "The terraform state does not exist, there is nothing to do")
|
||||
return &proto.PlanComplete{}
|
||||
}
|
||||
|
||||
return stream.Send(&proto.Provision_Response{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{},
|
||||
},
|
||||
})
|
||||
statefilePath := getStateFilePath(sess.WorkDirectory)
|
||||
if len(sess.Config.State) > 0 {
|
||||
err := os.WriteFile(statefilePath, sess.Config.State, 0o600)
|
||||
if err != nil {
|
||||
return provisionersdk.PlanErrorf("write statefile %q: %s", statefilePath, err)
|
||||
}
|
||||
}
|
||||
|
||||
s.logger.Debug(ctx, "running initialization")
|
||||
err = e.init(ctx, killCtx, sink)
|
||||
err := e.init(ctx, killCtx, sess)
|
||||
if err != nil {
|
||||
if ctx.Err() != nil {
|
||||
return stream.Send(&proto.Provision_Response{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Error: err.Error(),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
return xerrors.Errorf("initialize terraform: %w", err)
|
||||
s.logger.Debug(ctx, "init failed", slog.Error(err))
|
||||
return provisionersdk.PlanErrorf("initialize terraform: %s", err)
|
||||
}
|
||||
s.logger.Debug(ctx, "ran initialization")
|
||||
env, err := provisionEnv(config, request.GetPlan().GetRichParameterValues(), request.GetPlan().GetGitAuthProviders())
|
||||
|
||||
env, err := provisionEnv(sess.Config, request.Metadata, request.RichParameterValues, request.GitAuthProviders)
|
||||
if err != nil {
|
||||
return err
|
||||
return provisionersdk.PlanErrorf("setup env: %s", err)
|
||||
}
|
||||
|
||||
var resp *proto.Provision_Response
|
||||
if planRequest != nil {
|
||||
vars, err := planVars(planRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err = e.plan(
|
||||
ctx, killCtx, env, vars, sink,
|
||||
config.Metadata.WorkspaceTransition == proto.WorkspaceTransition_DESTROY,
|
||||
)
|
||||
if err != nil {
|
||||
if ctx.Err() != nil {
|
||||
return stream.Send(&proto.Provision_Response{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Error: err.Error(),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
return xerrors.Errorf("plan terraform: %w", err)
|
||||
}
|
||||
return stream.Send(resp)
|
||||
vars, err := planVars(request)
|
||||
if err != nil {
|
||||
return provisionersdk.PlanErrorf("plan vars: %s", err)
|
||||
}
|
||||
// Must be apply
|
||||
resp, err = e.apply(
|
||||
ctx, killCtx, applyRequest.Plan, env, sink,
|
||||
|
||||
resp, err := e.plan(
|
||||
ctx, killCtx, env, vars, sess,
|
||||
request.Metadata.GetWorkspaceTransition() == proto.WorkspaceTransition_DESTROY,
|
||||
)
|
||||
if err != nil {
|
||||
return provisionersdk.PlanErrorf(err.Error())
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func (s *server) Apply(
|
||||
sess *provisionersdk.Session, request *proto.ApplyRequest, canceledOrComplete <-chan struct{},
|
||||
) *proto.ApplyComplete {
|
||||
ctx, span := s.startTrace(sess.Context(), tracing.FuncName())
|
||||
defer span.End()
|
||||
ctx, cancel, killCtx, kill := s.setupContexts(ctx, canceledOrComplete)
|
||||
defer cancel()
|
||||
defer kill()
|
||||
|
||||
e := s.executor(sess.WorkDirectory)
|
||||
if err := e.checkMinVersion(ctx); err != nil {
|
||||
return provisionersdk.ApplyErrorf(err.Error())
|
||||
}
|
||||
logTerraformEnvVars(sess)
|
||||
|
||||
// Exit early if there is no plan file. This is necessary to
|
||||
// avoid any cases where a workspace is "locked out" of terraform due to
|
||||
// e.g. bad template param values and cannot be deleted. This is just for
|
||||
// contingency, in the future we will try harder to prevent workspaces being
|
||||
// broken this hard.
|
||||
if request.Metadata.GetWorkspaceTransition() == proto.WorkspaceTransition_DESTROY && len(sess.Config.State) == 0 {
|
||||
sess.ProvisionLog(proto.LogLevel_INFO, "The terraform plan does not exist, there is nothing to do")
|
||||
return &proto.ApplyComplete{}
|
||||
}
|
||||
|
||||
// Earlier in the session, Plan() will have written the state file and the plan file.
|
||||
statefilePath := getStateFilePath(sess.WorkDirectory)
|
||||
env, err := provisionEnv(sess.Config, request.Metadata, nil, nil)
|
||||
if err != nil {
|
||||
return provisionersdk.ApplyErrorf("provision env: %s", err)
|
||||
}
|
||||
resp, err := e.apply(
|
||||
ctx, killCtx, env, sess,
|
||||
)
|
||||
if err != nil {
|
||||
errorMessage := err.Error()
|
||||
// Terraform can fail and apply and still need to store it's state.
|
||||
// In this case, we return Complete with an explicit error message.
|
||||
stateData, _ := os.ReadFile(statefilePath)
|
||||
return stream.Send(&proto.Provision_Response{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
State: stateData,
|
||||
Error: errorMessage,
|
||||
},
|
||||
},
|
||||
})
|
||||
return &proto.ApplyComplete{
|
||||
State: stateData,
|
||||
Error: errorMessage,
|
||||
}
|
||||
}
|
||||
return stream.Send(resp)
|
||||
return resp
|
||||
}
|
||||
|
||||
func planVars(plan *proto.Provision_Plan) ([]string, error) {
|
||||
func planVars(plan *proto.PlanRequest) ([]string, error) {
|
||||
vars := []string{}
|
||||
for _, variable := range plan.VariableValues {
|
||||
vars = append(vars, fmt.Sprintf("%s=%s", variable.Name, variable.Value))
|
||||
|
@ -201,18 +172,21 @@ func planVars(plan *proto.Provision_Plan) ([]string, error) {
|
|||
return vars, nil
|
||||
}
|
||||
|
||||
func provisionEnv(config *proto.Provision_Config, richParams []*proto.RichParameterValue, gitAuth []*proto.GitAuthProvider) ([]string, error) {
|
||||
func provisionEnv(
|
||||
config *proto.Config, metadata *proto.Metadata,
|
||||
richParams []*proto.RichParameterValue, gitAuth []*proto.GitAuthProvider,
|
||||
) ([]string, error) {
|
||||
env := safeEnviron()
|
||||
env = append(env,
|
||||
"CODER_AGENT_URL="+config.Metadata.CoderUrl,
|
||||
"CODER_WORKSPACE_TRANSITION="+strings.ToLower(config.Metadata.WorkspaceTransition.String()),
|
||||
"CODER_WORKSPACE_NAME="+config.Metadata.WorkspaceName,
|
||||
"CODER_WORKSPACE_OWNER="+config.Metadata.WorkspaceOwner,
|
||||
"CODER_WORKSPACE_OWNER_EMAIL="+config.Metadata.WorkspaceOwnerEmail,
|
||||
"CODER_WORKSPACE_OWNER_OIDC_ACCESS_TOKEN="+config.Metadata.WorkspaceOwnerOidcAccessToken,
|
||||
"CODER_WORKSPACE_ID="+config.Metadata.WorkspaceId,
|
||||
"CODER_WORKSPACE_OWNER_ID="+config.Metadata.WorkspaceOwnerId,
|
||||
"CODER_WORKSPACE_OWNER_SESSION_TOKEN="+config.Metadata.WorkspaceOwnerSessionToken,
|
||||
"CODER_AGENT_URL="+metadata.GetCoderUrl(),
|
||||
"CODER_WORKSPACE_TRANSITION="+strings.ToLower(metadata.GetWorkspaceTransition().String()),
|
||||
"CODER_WORKSPACE_NAME="+metadata.GetWorkspaceName(),
|
||||
"CODER_WORKSPACE_OWNER="+metadata.GetWorkspaceOwner(),
|
||||
"CODER_WORKSPACE_OWNER_EMAIL="+metadata.GetWorkspaceOwnerEmail(),
|
||||
"CODER_WORKSPACE_OWNER_OIDC_ACCESS_TOKEN="+metadata.GetWorkspaceOwnerOidcAccessToken(),
|
||||
"CODER_WORKSPACE_ID="+metadata.GetWorkspaceId(),
|
||||
"CODER_WORKSPACE_OWNER_ID="+metadata.GetWorkspaceOwnerId(),
|
||||
"CODER_WORKSPACE_OWNER_SESSION_TOKEN="+metadata.GetWorkspaceOwnerSessionToken(),
|
||||
)
|
||||
for key, value := range provisionersdk.AgentScriptEnv() {
|
||||
env = append(env, key+"="+value)
|
||||
|
@ -258,10 +232,10 @@ func logTerraformEnvVars(sink logSink) {
|
|||
if !tfEnvSafeToPrint[parts[0]] {
|
||||
parts[1] = "<value redacted>"
|
||||
}
|
||||
sink.Log(&proto.Log{
|
||||
Level: proto.LogLevel_WARN,
|
||||
Output: fmt.Sprintf("terraform environment variable: %s=%s", parts[0], parts[1]),
|
||||
})
|
||||
sink.ProvisionLog(
|
||||
proto.LogLevel_WARN,
|
||||
fmt.Sprintf("terraform environment variable: %s=%s", parts[0], parts[1]),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
package terraform_test
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
@ -35,6 +37,7 @@ func setupProvisioner(t *testing.T, opts *provisionerServeOptions) (context.Cont
|
|||
opts = &provisionerServeOptions{}
|
||||
}
|
||||
cachePath := t.TempDir()
|
||||
workDir := t.TempDir()
|
||||
client, server := provisionersdk.MemTransportPipe()
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
serverErr := make(chan error, 1)
|
||||
|
@ -50,40 +53,75 @@ func setupProvisioner(t *testing.T, opts *provisionerServeOptions) (context.Cont
|
|||
go func() {
|
||||
serverErr <- terraform.Serve(ctx, &terraform.ServeOptions{
|
||||
ServeOptions: &provisionersdk.ServeOptions{
|
||||
Listener: server,
|
||||
Listener: server,
|
||||
Logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug),
|
||||
WorkDirectory: workDir,
|
||||
},
|
||||
BinaryPath: opts.binaryPath,
|
||||
CachePath: cachePath,
|
||||
Logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug),
|
||||
ExitTimeout: opts.exitTimeout,
|
||||
})
|
||||
}()
|
||||
api := proto.NewDRPCProvisionerClient(client)
|
||||
|
||||
return ctx, api
|
||||
}
|
||||
|
||||
func readProvisionLog(t *testing.T, response proto.DRPCProvisioner_ProvisionClient) (
|
||||
string,
|
||||
*proto.Provision_Complete,
|
||||
) {
|
||||
var (
|
||||
logBuf strings.Builder
|
||||
c *proto.Provision_Complete
|
||||
)
|
||||
func makeTar(t *testing.T, files map[string]string) []byte {
|
||||
t.Helper()
|
||||
var buffer bytes.Buffer
|
||||
writer := tar.NewWriter(&buffer)
|
||||
for name, content := range files {
|
||||
err := writer.WriteHeader(&tar.Header{
|
||||
Name: name,
|
||||
Size: int64(len(content)),
|
||||
Mode: 0o644,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = writer.Write([]byte(content))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
err := writer.Flush()
|
||||
require.NoError(t, err)
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
func configure(ctx context.Context, t *testing.T, client proto.DRPCProvisionerClient, config *proto.Config) proto.DRPCProvisioner_SessionClient {
|
||||
t.Helper()
|
||||
sess, err := client.Session(ctx)
|
||||
require.NoError(t, err)
|
||||
err = sess.Send(&proto.Request{Type: &proto.Request_Config{Config: config}})
|
||||
require.NoError(t, err)
|
||||
return sess
|
||||
}
|
||||
|
||||
func readProvisionLog(t *testing.T, response proto.DRPCProvisioner_SessionClient) string {
|
||||
var logBuf strings.Builder
|
||||
for {
|
||||
msg, err := response.Recv()
|
||||
require.NoError(t, err)
|
||||
|
||||
if log := msg.GetLog(); log != nil {
|
||||
t.Log(log.Level.String(), log.Output)
|
||||
_, _ = logBuf.WriteString(log.Output)
|
||||
}
|
||||
if c = msg.GetComplete(); c != nil {
|
||||
require.Empty(t, c.Error)
|
||||
break
|
||||
_, err = logBuf.WriteString(log.Output)
|
||||
require.NoError(t, err)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
return logBuf.String(), c
|
||||
return logBuf.String()
|
||||
}
|
||||
|
||||
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},
|
||||
}}})
|
||||
}
|
||||
|
||||
func sendApply(sess proto.DRPCProvisioner_SessionClient, transition proto.WorkspaceTransition) error {
|
||||
return sess.Send(&proto.Request{Type: &proto.Request_Apply{Apply: &proto.ApplyRequest{
|
||||
Metadata: &proto.Metadata{WorkspaceTransition: transition},
|
||||
}}})
|
||||
}
|
||||
|
||||
func TestProvision_Cancel(t *testing.T) {
|
||||
|
@ -109,9 +147,10 @@ func TestProvision_Cancel(t *testing.T) {
|
|||
wantLog: []string{"interrupt", "exit"},
|
||||
},
|
||||
{
|
||||
name: "Cancel apply",
|
||||
mode: "apply",
|
||||
startSequence: []string{"init", "apply_start"},
|
||||
// Provisioner requires a plan before an apply, so test cancel with plan.
|
||||
name: "Cancel plan",
|
||||
mode: "plan",
|
||||
startSequence: []string{"init", "plan_start"},
|
||||
wantLog: []string{"interrupt", "exit"},
|
||||
},
|
||||
}
|
||||
|
@ -131,24 +170,16 @@ func TestProvision_Cancel(t *testing.T) {
|
|||
ctx, api := setupProvisioner(t, &provisionerServeOptions{
|
||||
binaryPath: binPath,
|
||||
})
|
||||
|
||||
response, err := api.Provision(ctx)
|
||||
require.NoError(t, err)
|
||||
err = response.Send(&proto.Provision_Request{
|
||||
Type: &proto.Provision_Request_Apply{
|
||||
Apply: &proto.Provision_Apply{
|
||||
Config: &proto.Provision_Config{
|
||||
Directory: dir,
|
||||
Metadata: &proto.Provision_Metadata{},
|
||||
},
|
||||
},
|
||||
},
|
||||
sess := configure(ctx, t, api, &proto.Config{
|
||||
TemplateSourceArchive: makeTar(t, nil),
|
||||
})
|
||||
|
||||
err = sendPlan(sess, proto.WorkspaceTransition_START)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, line := range tt.startSequence {
|
||||
LoopStart:
|
||||
msg, err := response.Recv()
|
||||
msg, err := sess.Recv()
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Log(msg.Type)
|
||||
|
@ -160,22 +191,22 @@ func TestProvision_Cancel(t *testing.T) {
|
|||
require.Equal(t, line, log.Output)
|
||||
}
|
||||
|
||||
err = response.Send(&proto.Provision_Request{
|
||||
Type: &proto.Provision_Request_Cancel{
|
||||
Cancel: &proto.Provision_Cancel{},
|
||||
err = sess.Send(&proto.Request{
|
||||
Type: &proto.Request_Cancel{
|
||||
Cancel: &proto.CancelRequest{},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
var gotLog []string
|
||||
for {
|
||||
msg, err := response.Recv()
|
||||
msg, err := sess.Recv()
|
||||
require.NoError(t, err)
|
||||
|
||||
if log := msg.GetLog(); log != nil {
|
||||
gotLog = append(gotLog, log.Output)
|
||||
}
|
||||
if c := msg.GetComplete(); c != nil {
|
||||
if c := msg.GetPlan(); c != nil {
|
||||
require.Contains(t, c.Error, "exit status 1")
|
||||
break
|
||||
}
|
||||
|
@ -208,23 +239,17 @@ func TestProvision_CancelTimeout(t *testing.T) {
|
|||
exitTimeout: time.Second,
|
||||
})
|
||||
|
||||
response, err := api.Provision(ctx)
|
||||
require.NoError(t, err)
|
||||
err = response.Send(&proto.Provision_Request{
|
||||
Type: &proto.Provision_Request_Apply{
|
||||
Apply: &proto.Provision_Apply{
|
||||
Config: &proto.Provision_Config{
|
||||
Directory: dir,
|
||||
Metadata: &proto.Provision_Metadata{},
|
||||
},
|
||||
},
|
||||
},
|
||||
sess := configure(ctx, t, api, &proto.Config{
|
||||
TemplateSourceArchive: makeTar(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", "apply_start"} {
|
||||
for _, line := range []string{"init", "plan_start"} {
|
||||
LoopStart:
|
||||
msg, err := response.Recv()
|
||||
msg, err := sess.Recv()
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Log(msg.Type)
|
||||
|
@ -236,18 +261,14 @@ func TestProvision_CancelTimeout(t *testing.T) {
|
|||
require.Equal(t, line, log.Output)
|
||||
}
|
||||
|
||||
err = response.Send(&proto.Provision_Request{
|
||||
Type: &proto.Provision_Request_Cancel{
|
||||
Cancel: &proto.Provision_Cancel{},
|
||||
},
|
||||
})
|
||||
err = sess.Send(&proto.Request{Type: &proto.Request_Cancel{Cancel: &proto.CancelRequest{}}})
|
||||
require.NoError(t, err)
|
||||
|
||||
for {
|
||||
msg, err := response.Recv()
|
||||
msg, err := sess.Recv()
|
||||
require.NoError(t, err)
|
||||
|
||||
if c := msg.GetComplete(); c != nil {
|
||||
if c := msg.GetPlan(); c != nil {
|
||||
require.Contains(t, c.Error, "killed")
|
||||
break
|
||||
}
|
||||
|
@ -258,17 +279,18 @@ func TestProvision(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
Name string
|
||||
Files map[string]string
|
||||
Request *proto.Provision_Plan
|
||||
Name string
|
||||
Files map[string]string
|
||||
Metadata *proto.Metadata
|
||||
Request *proto.PlanRequest
|
||||
// Response may be nil to not check the response.
|
||||
Response *proto.Provision_Response
|
||||
// If ErrorContains is not empty, then response.Recv() should return an
|
||||
// error containing this string before a Complete response is returned.
|
||||
Response *proto.PlanComplete
|
||||
// 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
|
||||
Apply bool
|
||||
// If Apply is true, then send an Apply request and check we get the same Resources as in Response.
|
||||
Apply bool
|
||||
}{
|
||||
{
|
||||
Name: "missing-variable",
|
||||
|
@ -293,15 +315,11 @@ func TestProvision(t *testing.T) {
|
|||
Files: map[string]string{
|
||||
"main.tf": `resource "null_resource" "A" {}`,
|
||||
},
|
||||
Response: &proto.Provision_Response{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "A",
|
||||
Type: "null_resource",
|
||||
}},
|
||||
},
|
||||
},
|
||||
Response: &proto.PlanComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "A",
|
||||
Type: "null_resource",
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -309,15 +327,11 @@ func TestProvision(t *testing.T) {
|
|||
Files: map[string]string{
|
||||
"main.tf": `resource "null_resource" "A" {}`,
|
||||
},
|
||||
Response: &proto.Provision_Response{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "A",
|
||||
Type: "null_resource",
|
||||
}},
|
||||
},
|
||||
},
|
||||
Response: &proto.PlanComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "A",
|
||||
Type: "null_resource",
|
||||
}},
|
||||
},
|
||||
Apply: true,
|
||||
},
|
||||
|
@ -334,15 +348,11 @@ func TestProvision(t *testing.T) {
|
|||
}
|
||||
}`,
|
||||
},
|
||||
Response: &proto.Provision_Response{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "A",
|
||||
Type: "null_resource",
|
||||
}},
|
||||
},
|
||||
},
|
||||
Response: &proto.PlanComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "A",
|
||||
Type: "null_resource",
|
||||
}},
|
||||
},
|
||||
Apply: true,
|
||||
},
|
||||
|
@ -367,12 +377,8 @@ func TestProvision(t *testing.T) {
|
|||
Files: map[string]string{
|
||||
"main.tf": `resource "null_resource" "A" {}`,
|
||||
},
|
||||
Request: &proto.Provision_Plan{
|
||||
Config: &proto.Provision_Config{
|
||||
Metadata: &proto.Provision_Metadata{
|
||||
WorkspaceTransition: proto.WorkspaceTransition_DESTROY,
|
||||
},
|
||||
},
|
||||
Metadata: &proto.Metadata{
|
||||
WorkspaceTransition: proto.WorkspaceTransition_DESTROY,
|
||||
},
|
||||
ExpectLogContains: "nothing to do",
|
||||
},
|
||||
|
@ -406,7 +412,7 @@ func TestProvision(t *testing.T) {
|
|||
}
|
||||
}`,
|
||||
},
|
||||
Request: &proto.Provision_Plan{
|
||||
Request: &proto.PlanRequest{
|
||||
RichParameterValues: []*proto.RichParameterValue{
|
||||
{
|
||||
Name: "Example",
|
||||
|
@ -418,27 +424,23 @@ func TestProvision(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
Response: &proto.Provision_Response{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Parameters: []*proto.RichParameter{
|
||||
{
|
||||
Name: "Example",
|
||||
Type: "string",
|
||||
DefaultValue: "foobar",
|
||||
},
|
||||
{
|
||||
Name: "Sample",
|
||||
Type: "string",
|
||||
DefaultValue: "foobaz",
|
||||
},
|
||||
},
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "null_resource",
|
||||
}},
|
||||
Response: &proto.PlanComplete{
|
||||
Parameters: []*proto.RichParameter{
|
||||
{
|
||||
Name: "Example",
|
||||
Type: "string",
|
||||
DefaultValue: "foobar",
|
||||
},
|
||||
{
|
||||
Name: "Sample",
|
||||
Type: "string",
|
||||
DefaultValue: "foobaz",
|
||||
},
|
||||
},
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "null_resource",
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -488,7 +490,7 @@ func TestProvision(t *testing.T) {
|
|||
]
|
||||
}`,
|
||||
},
|
||||
Request: &proto.Provision_Plan{
|
||||
Request: &proto.PlanRequest{
|
||||
RichParameterValues: []*proto.RichParameterValue{
|
||||
{
|
||||
Name: "Example",
|
||||
|
@ -500,27 +502,23 @@ func TestProvision(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
Response: &proto.Provision_Response{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Parameters: []*proto.RichParameter{
|
||||
{
|
||||
Name: "Example",
|
||||
Type: "string",
|
||||
DefaultValue: "foobar",
|
||||
},
|
||||
{
|
||||
Name: "Sample",
|
||||
Type: "string",
|
||||
DefaultValue: "foobaz",
|
||||
},
|
||||
},
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "null_resource",
|
||||
}},
|
||||
Response: &proto.PlanComplete{
|
||||
Parameters: []*proto.RichParameter{
|
||||
{
|
||||
Name: "Example",
|
||||
Type: "string",
|
||||
DefaultValue: "foobar",
|
||||
},
|
||||
{
|
||||
Name: "Sample",
|
||||
Type: "string",
|
||||
DefaultValue: "foobaz",
|
||||
},
|
||||
},
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "null_resource",
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -550,25 +548,21 @@ func TestProvision(t *testing.T) {
|
|||
}
|
||||
`,
|
||||
},
|
||||
Request: &proto.Provision_Plan{
|
||||
Request: &proto.PlanRequest{
|
||||
GitAuthProviders: []*proto.GitAuthProvider{{
|
||||
Id: "github",
|
||||
AccessToken: "some-value",
|
||||
}},
|
||||
},
|
||||
Response: &proto.Provision_Response{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "null_resource",
|
||||
Metadata: []*proto.Resource_Metadata{{
|
||||
Key: "token",
|
||||
Value: "some-value",
|
||||
}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
Response: &proto.PlanComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "null_resource",
|
||||
Metadata: []*proto.Resource_Metadata{{
|
||||
Key: "token",
|
||||
Value: "some-value",
|
||||
}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -579,50 +573,26 @@ func TestProvision(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
ctx, api := setupProvisioner(t, nil)
|
||||
sess := configure(ctx, t, api, &proto.Config{
|
||||
TemplateSourceArchive: makeTar(t, testCase.Files),
|
||||
})
|
||||
|
||||
directory := t.TempDir()
|
||||
for path, content := range testCase.Files {
|
||||
err := os.WriteFile(filepath.Join(directory, path), []byte(content), 0o600)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
planRequest := &proto.Provision_Request{
|
||||
Type: &proto.Provision_Request_Plan{
|
||||
Plan: &proto.Provision_Plan{
|
||||
Config: &proto.Provision_Config{
|
||||
Directory: directory,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
planRequest := &proto.Request{Type: &proto.Request_Plan{Plan: &proto.PlanRequest{
|
||||
Metadata: testCase.Metadata,
|
||||
}}}
|
||||
if testCase.Request != nil {
|
||||
if planRequest.GetPlan().GetConfig() == nil {
|
||||
planRequest.GetPlan().Config = &proto.Provision_Config{}
|
||||
}
|
||||
planRequest.GetPlan().RichParameterValues = testCase.Request.RichParameterValues
|
||||
planRequest.GetPlan().GitAuthProviders = testCase.Request.GitAuthProviders
|
||||
if testCase.Request.Config != nil {
|
||||
planRequest.GetPlan().Config.State = testCase.Request.Config.State
|
||||
planRequest.GetPlan().Config.Metadata = testCase.Request.Config.Metadata
|
||||
}
|
||||
}
|
||||
if planRequest.GetPlan().Config.Metadata == nil {
|
||||
planRequest.GetPlan().Config.Metadata = &proto.Provision_Metadata{}
|
||||
planRequest = &proto.Request{Type: &proto.Request_Plan{Plan: testCase.Request}}
|
||||
}
|
||||
|
||||
gotExpectedLog := testCase.ExpectLogContains == ""
|
||||
|
||||
provision := func(req *proto.Provision_Request) *proto.Provision_Complete {
|
||||
response, err := api.Provision(ctx)
|
||||
provision := func(req *proto.Request) *proto.Response {
|
||||
err := sess.Send(req)
|
||||
require.NoError(t, err)
|
||||
err = response.Send(req)
|
||||
require.NoError(t, err)
|
||||
|
||||
var complete *proto.Provision_Complete
|
||||
|
||||
for {
|
||||
msg, err := response.Recv()
|
||||
if msg != nil && msg.GetLog() != nil {
|
||||
msg, err := sess.Recv()
|
||||
require.NoError(t, err)
|
||||
if msg.GetLog() != nil {
|
||||
if testCase.ExpectLogContains != "" && strings.Contains(msg.GetLog().Output, testCase.ExpectLogContains) {
|
||||
gotExpectedLog = true
|
||||
}
|
||||
|
@ -630,67 +600,51 @@ func TestProvision(t *testing.T) {
|
|||
t.Logf("log: [%s] %s", msg.GetLog().Level, msg.GetLog().Output)
|
||||
continue
|
||||
}
|
||||
if testCase.ErrorContains != "" {
|
||||
require.ErrorContains(t, err, testCase.ErrorContains)
|
||||
break
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if complete = msg.GetComplete(); complete == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
// Remove randomly generated data.
|
||||
for _, resource := range msg.GetComplete().Resources {
|
||||
sort.Slice(resource.Agents, func(i, j int) bool {
|
||||
return resource.Agents[i].Name < resource.Agents[j].Name
|
||||
})
|
||||
|
||||
for _, agent := range resource.Agents {
|
||||
agent.Id = ""
|
||||
if agent.GetToken() == "" {
|
||||
continue
|
||||
}
|
||||
agent.Auth = &proto.Agent_Token{}
|
||||
}
|
||||
}
|
||||
|
||||
if testCase.Response != nil {
|
||||
require.Equal(t, testCase.Response.GetComplete().Error, msg.GetComplete().Error)
|
||||
|
||||
resourcesGot, err := json.Marshal(msg.GetComplete().Resources)
|
||||
require.NoError(t, err)
|
||||
resourcesWant, err := json.Marshal(testCase.Response.GetComplete().Resources)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, string(resourcesWant), string(resourcesGot))
|
||||
|
||||
parametersGot, err := json.Marshal(msg.GetComplete().Parameters)
|
||||
require.NoError(t, err)
|
||||
parametersWant, err := json.Marshal(testCase.Response.GetComplete().Parameters)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(parametersWant), string(parametersGot))
|
||||
}
|
||||
break
|
||||
return msg
|
||||
}
|
||||
|
||||
return complete
|
||||
}
|
||||
|
||||
planComplete := provision(planRequest)
|
||||
resp := provision(planRequest)
|
||||
planComplete := resp.GetPlan()
|
||||
require.NotNil(t, planComplete)
|
||||
|
||||
if testCase.ErrorContains != "" {
|
||||
require.Contains(t, planComplete.GetError(), testCase.ErrorContains)
|
||||
}
|
||||
|
||||
if testCase.Response != nil {
|
||||
require.Equal(t, testCase.Response.Error, planComplete.Error)
|
||||
|
||||
// Remove randomly generated data.
|
||||
normalizeResources(planComplete.Resources)
|
||||
resourcesGot, err := json.Marshal(planComplete.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)
|
||||
require.NoError(t, err)
|
||||
parametersWant, err := json.Marshal(testCase.Response.Parameters)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(parametersWant), string(parametersGot))
|
||||
}
|
||||
|
||||
if testCase.Apply {
|
||||
require.NotNil(t, planComplete.Plan)
|
||||
provision(&proto.Provision_Request{
|
||||
Type: &proto.Provision_Request_Apply{
|
||||
Apply: &proto.Provision_Apply{
|
||||
Config: planRequest.GetPlan().GetConfig(),
|
||||
Plan: planComplete.Plan,
|
||||
},
|
||||
},
|
||||
})
|
||||
resp = provision(&proto.Request{Type: &proto.Request_Apply{Apply: &proto.ApplyRequest{
|
||||
Metadata: &proto.Metadata{WorkspaceTransition: proto.WorkspaceTransition_START},
|
||||
}}})
|
||||
applyComplete := resp.GetApply()
|
||||
require.NotNil(t, applyComplete)
|
||||
|
||||
if testCase.Response != nil {
|
||||
normalizeResources(applyComplete.Resources)
|
||||
resourcesGot, err := json.Marshal(applyComplete.Resources)
|
||||
require.NoError(t, err)
|
||||
resourcesWant, err := json.Marshal(testCase.Response.Resources)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(resourcesWant), string(resourcesGot))
|
||||
}
|
||||
}
|
||||
|
||||
if !gotExpectedLog {
|
||||
|
@ -700,6 +654,22 @@ func TestProvision(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func normalizeResources(resources []*proto.Resource) {
|
||||
for _, resource := range resources {
|
||||
sort.Slice(resource.Agents, func(i, j int) bool {
|
||||
return resource.Agents[i].Name < resource.Agents[j].Name
|
||||
})
|
||||
|
||||
for _, agent := range resource.Agents {
|
||||
agent.Id = ""
|
||||
if agent.GetToken() == "" {
|
||||
continue
|
||||
}
|
||||
agent.Auth = &proto.Agent_Token{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nolint:paralleltest
|
||||
func TestProvision_ExtraEnv(t *testing.T) {
|
||||
// #nosec
|
||||
|
@ -708,31 +678,15 @@ func TestProvision_ExtraEnv(t *testing.T) {
|
|||
t.Setenv("TF_SUPERSECRET", secretValue)
|
||||
|
||||
ctx, api := setupProvisioner(t, nil)
|
||||
sess := configure(ctx, t, api, &proto.Config{
|
||||
TemplateSourceArchive: makeTar(t, map[string]string{"main.tf": `resource "null_resource" "A" {}`}),
|
||||
})
|
||||
|
||||
directory := t.TempDir()
|
||||
path := filepath.Join(directory, "main.tf")
|
||||
err := os.WriteFile(path, []byte(`resource "null_resource" "A" {}`), 0o600)
|
||||
require.NoError(t, err)
|
||||
|
||||
request := &proto.Provision_Request{
|
||||
Type: &proto.Provision_Request_Plan{
|
||||
Plan: &proto.Provision_Plan{
|
||||
Config: &proto.Provision_Config{
|
||||
Directory: directory,
|
||||
Metadata: &proto.Provision_Metadata{
|
||||
WorkspaceTransition: proto.WorkspaceTransition_START,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
response, err := api.Provision(ctx)
|
||||
require.NoError(t, err)
|
||||
err = response.Send(request)
|
||||
err := sendPlan(sess, proto.WorkspaceTransition_START)
|
||||
require.NoError(t, err)
|
||||
found := false
|
||||
for {
|
||||
msg, err := response.Recv()
|
||||
msg, err := sess.Recv()
|
||||
require.NoError(t, err)
|
||||
|
||||
if log := msg.GetLog(); log != nil {
|
||||
|
@ -742,7 +696,7 @@ func TestProvision_ExtraEnv(t *testing.T) {
|
|||
}
|
||||
require.NotContains(t, log.Output, secretValue)
|
||||
}
|
||||
if c := msg.GetComplete(); c != nil {
|
||||
if c := msg.GetPlan(); c != nil {
|
||||
require.Empty(t, c.Error)
|
||||
break
|
||||
}
|
||||
|
@ -774,48 +728,19 @@ func TestProvision_SafeEnv(t *testing.T) {
|
|||
`
|
||||
|
||||
ctx, api := setupProvisioner(t, nil)
|
||||
|
||||
directory := t.TempDir()
|
||||
path := filepath.Join(directory, "main.tf")
|
||||
err := os.WriteFile(path, []byte(echoResource), 0o600)
|
||||
require.NoError(t, err)
|
||||
|
||||
response, err := api.Provision(ctx)
|
||||
require.NoError(t, err)
|
||||
err = response.Send(&proto.Provision_Request{
|
||||
Type: &proto.Provision_Request_Plan{
|
||||
Plan: &proto.Provision_Plan{
|
||||
Config: &proto.Provision_Config{
|
||||
Directory: directory,
|
||||
Metadata: &proto.Provision_Metadata{
|
||||
WorkspaceTransition: proto.WorkspaceTransition_START,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
sess := configure(ctx, t, api, &proto.Config{
|
||||
TemplateSourceArchive: makeTar(t, map[string]string{"main.tf": echoResource}),
|
||||
})
|
||||
|
||||
err := sendPlan(sess, proto.WorkspaceTransition_START)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, complete := readProvisionLog(t, response)
|
||||
_ = readProvisionLog(t, sess)
|
||||
|
||||
response, err = api.Provision(ctx)
|
||||
require.NoError(t, err)
|
||||
err = response.Send(&proto.Provision_Request{
|
||||
Type: &proto.Provision_Request_Apply{
|
||||
Apply: &proto.Provision_Apply{
|
||||
Config: &proto.Provision_Config{
|
||||
Directory: directory,
|
||||
Metadata: &proto.Provision_Metadata{
|
||||
WorkspaceTransition: proto.WorkspaceTransition_START,
|
||||
},
|
||||
},
|
||||
Plan: complete.GetPlan(),
|
||||
},
|
||||
},
|
||||
})
|
||||
err = sendApply(sess, proto.WorkspaceTransition_START)
|
||||
require.NoError(t, err)
|
||||
|
||||
log, _ := readProvisionLog(t, response)
|
||||
log := readProvisionLog(t, sess)
|
||||
require.Contains(t, log, passedValue)
|
||||
require.NotContains(t, log, secretValue)
|
||||
require.Contains(t, log, "CODER_")
|
||||
|
|
|
@ -24,7 +24,6 @@ type ServeOptions struct {
|
|||
BinaryPath string
|
||||
// CachePath must not be used by multiple processes at once.
|
||||
CachePath string
|
||||
Logger slog.Logger
|
||||
Tracer trace.Tracer
|
||||
|
||||
// ExitTimeout defines how long we will wait for a running Terraform
|
||||
|
@ -128,5 +127,6 @@ func (s *server) executor(workdir string) *executor {
|
|||
binaryPath: s.binaryPath,
|
||||
cachePath: s.cachePath,
|
||||
workdir: workdir,
|
||||
logger: s.logger.Named("executor"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,8 +22,9 @@ version)
|
|||
;;
|
||||
init)
|
||||
case "$MODE" in
|
||||
apply)
|
||||
plan)
|
||||
echo "init"
|
||||
exit 0
|
||||
;;
|
||||
init)
|
||||
sleep 10 &
|
||||
|
@ -39,7 +40,7 @@ init)
|
|||
;;
|
||||
esac
|
||||
;;
|
||||
apply)
|
||||
plan)
|
||||
sleep 10 &
|
||||
sleep_pid=$!
|
||||
|
||||
|
@ -47,14 +48,14 @@ apply)
|
|||
trap 'json_print interrupt; exit 1' INT
|
||||
trap 'json_print terminate; exit 2' TERM
|
||||
|
||||
json_print apply_start
|
||||
json_print plan_start
|
||||
wait
|
||||
json_print apply_end
|
||||
json_print plan_end
|
||||
;;
|
||||
plan)
|
||||
echo "plan not supported"
|
||||
apply)
|
||||
echo "apply not supported"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
exit 10
|
||||
|
|
|
@ -23,19 +23,19 @@ init)
|
|||
echo "init"
|
||||
exit 0
|
||||
;;
|
||||
apply)
|
||||
plan)
|
||||
trap 'json_print interrupt' INT
|
||||
|
||||
json_print apply_start
|
||||
json_print plan_start
|
||||
sleep 10 2>/dev/null >/dev/null
|
||||
json_print apply_end
|
||||
json_print plan_end
|
||||
|
||||
exit 0
|
||||
;;
|
||||
plan)
|
||||
echo "plan not supported"
|
||||
apply)
|
||||
echo "apply not supported"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
exit 10
|
||||
|
|
|
@ -819,7 +819,7 @@ type AcquiredJob_WorkspaceBuild struct {
|
|||
RichParameterValues []*proto.RichParameterValue `protobuf:"bytes,4,rep,name=rich_parameter_values,json=richParameterValues,proto3" json:"rich_parameter_values,omitempty"`
|
||||
VariableValues []*proto.VariableValue `protobuf:"bytes,5,rep,name=variable_values,json=variableValues,proto3" json:"variable_values,omitempty"`
|
||||
GitAuthProviders []*proto.GitAuthProvider `protobuf:"bytes,6,rep,name=git_auth_providers,json=gitAuthProviders,proto3" json:"git_auth_providers,omitempty"`
|
||||
Metadata *proto.Provision_Metadata `protobuf:"bytes,7,opt,name=metadata,proto3" json:"metadata,omitempty"`
|
||||
Metadata *proto.Metadata `protobuf:"bytes,7,opt,name=metadata,proto3" json:"metadata,omitempty"`
|
||||
State []byte `protobuf:"bytes,8,opt,name=state,proto3" json:"state,omitempty"`
|
||||
LogLevel string `protobuf:"bytes,9,opt,name=log_level,json=logLevel,proto3" json:"log_level,omitempty"`
|
||||
}
|
||||
|
@ -891,7 +891,7 @@ func (x *AcquiredJob_WorkspaceBuild) GetGitAuthProviders() []*proto.GitAuthProvi
|
|||
return nil
|
||||
}
|
||||
|
||||
func (x *AcquiredJob_WorkspaceBuild) GetMetadata() *proto.Provision_Metadata {
|
||||
func (x *AcquiredJob_WorkspaceBuild) GetMetadata() *proto.Metadata {
|
||||
if x != nil {
|
||||
return x.Metadata
|
||||
}
|
||||
|
@ -917,8 +917,8 @@ type AcquiredJob_TemplateImport struct {
|
|||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Metadata *proto.Provision_Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"`
|
||||
UserVariableValues []*proto.VariableValue `protobuf:"bytes,2,rep,name=user_variable_values,json=userVariableValues,proto3" json:"user_variable_values,omitempty"`
|
||||
Metadata *proto.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"`
|
||||
UserVariableValues []*proto.VariableValue `protobuf:"bytes,2,rep,name=user_variable_values,json=userVariableValues,proto3" json:"user_variable_values,omitempty"`
|
||||
}
|
||||
|
||||
func (x *AcquiredJob_TemplateImport) Reset() {
|
||||
|
@ -953,7 +953,7 @@ func (*AcquiredJob_TemplateImport) Descriptor() ([]byte, []int) {
|
|||
return file_provisionerd_proto_provisionerd_proto_rawDescGZIP(), []int{1, 1}
|
||||
}
|
||||
|
||||
func (x *AcquiredJob_TemplateImport) GetMetadata() *proto.Provision_Metadata {
|
||||
func (x *AcquiredJob_TemplateImport) GetMetadata() *proto.Metadata {
|
||||
if x != nil {
|
||||
return x.Metadata
|
||||
}
|
||||
|
@ -974,7 +974,7 @@ type AcquiredJob_TemplateDryRun struct {
|
|||
|
||||
RichParameterValues []*proto.RichParameterValue `protobuf:"bytes,2,rep,name=rich_parameter_values,json=richParameterValues,proto3" json:"rich_parameter_values,omitempty"`
|
||||
VariableValues []*proto.VariableValue `protobuf:"bytes,3,rep,name=variable_values,json=variableValues,proto3" json:"variable_values,omitempty"`
|
||||
Metadata *proto.Provision_Metadata `protobuf:"bytes,4,opt,name=metadata,proto3" json:"metadata,omitempty"`
|
||||
Metadata *proto.Metadata `protobuf:"bytes,4,opt,name=metadata,proto3" json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
func (x *AcquiredJob_TemplateDryRun) Reset() {
|
||||
|
@ -1023,7 +1023,7 @@ func (x *AcquiredJob_TemplateDryRun) GetVariableValues() []*proto.VariableValue
|
|||
return nil
|
||||
}
|
||||
|
||||
func (x *AcquiredJob_TemplateDryRun) GetMetadata() *proto.Provision_Metadata {
|
||||
func (x *AcquiredJob_TemplateDryRun) GetMetadata() *proto.Metadata {
|
||||
if x != nil {
|
||||
return x.Metadata
|
||||
}
|
||||
|
@ -1335,7 +1335,7 @@ var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{
|
|||
0x6f, 0x6e, 0x65, 0x72, 0x64, 0x1a, 0x26, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x65, 0x72, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x76,
|
||||
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x07, 0x0a,
|
||||
0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0xab, 0x0b, 0x0a, 0x0b, 0x41, 0x63, 0x71, 0x75, 0x69,
|
||||
0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x8d, 0x0b, 0x0a, 0x0b, 0x41, 0x63, 0x71, 0x75, 0x69,
|
||||
0x72, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x1d, 0x0a,
|
||||
0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
|
@ -1368,7 +1368,7 @@ var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{
|
|||
0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x41, 0x63, 0x71, 0x75, 0x69,
|
||||
0x72, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61,
|
||||
0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x63, 0x65,
|
||||
0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0xc1, 0x03, 0x0a, 0x0e, 0x57, 0x6f, 0x72,
|
||||
0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0xb7, 0x03, 0x0a, 0x0e, 0x57, 0x6f, 0x72,
|
||||
0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x2c, 0x0a, 0x12, 0x77,
|
||||
0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x69,
|
||||
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61,
|
||||
|
@ -1389,193 +1389,191 @@ var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{
|
|||
0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
|
||||
0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x47, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76,
|
||||
0x69, 0x64, 0x65, 0x72, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f,
|
||||
0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x3b, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61,
|
||||
0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64,
|
||||
0x61, 0x74, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x67,
|
||||
0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f,
|
||||
0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x1a, 0x9b, 0x01, 0x0a,
|
||||
0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12,
|
||||
0x3b, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
|
||||
0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
|
||||
0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x4c, 0x0a, 0x14,
|
||||
0x75, 0x73, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61,
|
||||
0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c,
|
||||
0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x75, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, 0x69,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x1a, 0xed, 0x01, 0x0a, 0x0e, 0x54,
|
||||
0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x53, 0x0a,
|
||||
0x15, 0x72, 0x69, 0x63, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f,
|
||||
0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50,
|
||||
0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x72,
|
||||
0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75,
|
||||
0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76,
|
||||
0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62,
|
||||
0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c,
|
||||
0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64,
|
||||
0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76,
|
||||
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61,
|
||||
0x64, 0x61, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x1a, 0x40, 0x0a, 0x12, 0x54, 0x72,
|
||||
0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79,
|
||||
0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
|
||||
0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x06, 0x0a, 0x04,
|
||||
0x74, 0x79, 0x70, 0x65, 0x22, 0xa5, 0x03, 0x0a, 0x09, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a,
|
||||
0x6f, 0x62, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72,
|
||||
0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12,
|
||||
0x51, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x62, 0x75, 0x69,
|
||||
0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f,
|
||||
0x62, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64,
|
||||
0x48, 0x00, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69,
|
||||
0x6c, 0x64, 0x12, 0x51, 0x0a, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x69,
|
||||
0x6d, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65,
|
||||
0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70,
|
||||
0x6f, 0x72, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49,
|
||||
0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x52, 0x0a, 0x10, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74,
|
||||
0x65, 0x5f, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x26, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x46,
|
||||
0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74,
|
||||
0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6c,
|
||||
0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x72, 0x72,
|
||||
0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65,
|
||||
0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x1a, 0x26, 0x0a, 0x0e, 0x57, 0x6f, 0x72, 0x6b,
|
||||
0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74,
|
||||
0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65,
|
||||
0x1a, 0x10, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f,
|
||||
0x72, 0x74, 0x1a, 0x10, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72,
|
||||
0x79, 0x52, 0x75, 0x6e, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xd8, 0x05, 0x0a,
|
||||
0x0c, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x12, 0x15, 0x0a,
|
||||
0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a,
|
||||
0x6f, 0x62, 0x49, 0x64, 0x12, 0x54, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
|
||||
0x65, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d,
|
||||
0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70,
|
||||
0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x48, 0x00, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b,
|
||||
0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x54, 0x0a, 0x0f, 0x74, 0x65,
|
||||
0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
|
||||
0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e,
|
||||
0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x00,
|
||||
0x52, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74,
|
||||
0x12, 0x55, 0x0a, 0x10, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x72, 0x79,
|
||||
0x5f, 0x72, 0x75, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65,
|
||||
0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44,
|
||||
0x72, 0x79, 0x52, 0x75, 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74,
|
||||
0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x1a, 0x5b, 0x0a, 0x0e, 0x57, 0x6f, 0x72, 0x6b, 0x73,
|
||||
0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61,
|
||||
0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12,
|
||||
0x33, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03,
|
||||
0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
|
||||
0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75,
|
||||
0x72, 0x63, 0x65, 0x73, 0x1a, 0x81, 0x02, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74,
|
||||
0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x3e, 0x0a, 0x0f, 0x73, 0x74, 0x61, 0x72, 0x74,
|
||||
0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52,
|
||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x0e, 0x73, 0x74, 0x6f, 0x70, 0x5f,
|
||||
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0d, 0x73, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x72, 0x69, 0x63, 0x68, 0x5f, 0x70, 0x61,
|
||||
0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63,
|
||||
0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x52, 0x0e, 0x72, 0x69, 0x63, 0x68,
|
||||
0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x67, 0x69,
|
||||
0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73,
|
||||
0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50,
|
||||
0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x1a, 0x45, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70,
|
||||
0x6c, 0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x33, 0x0a, 0x09, 0x72, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x42,
|
||||
0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xb0, 0x01, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12,
|
||||
0x2f, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,
|
||||
0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x4c,
|
||||
0x6f, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x12, 0x2b, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32,
|
||||
0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f,
|
||||
0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1d, 0x0a,
|
||||
0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||
0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x14, 0x0a, 0x05,
|
||||
0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61,
|
||||
0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0x8a, 0x02, 0x0a, 0x10, 0x55,
|
||||
0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
|
||||
0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61,
|
||||
0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52,
|
||||
0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61,
|
||||
0x74, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12,
|
||||
0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x09, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x4a, 0x04, 0x08, 0x03,
|
||||
0x10, 0x04, 0x1a, 0x91, 0x01, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49,
|
||||
0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
|
||||
0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73,
|
||||
0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08,
|
||||
0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x4c, 0x0a, 0x14, 0x75, 0x73, 0x65, 0x72,
|
||||
0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73,
|
||||
0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
|
||||
0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x52, 0x12, 0x75, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65,
|
||||
0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x1a, 0xe3, 0x01, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c,
|
||||
0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x53, 0x0a, 0x15, 0x72, 0x69, 0x63,
|
||||
0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75,
|
||||
0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d,
|
||||
0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x72, 0x69, 0x63, 0x68, 0x50,
|
||||
0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x43,
|
||||
0x0a, 0x0f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
||||
0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73,
|
||||
0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61,
|
||||
0x6c, 0x75, 0x65, 0x52, 0x0e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x73, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18,
|
||||
0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65,
|
||||
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x1a, 0x40, 0x0a, 0x12,
|
||||
0x54, 0x72, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74,
|
||||
0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x06,
|
||||
0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xa5, 0x03, 0x0a, 0x09, 0x46, 0x61, 0x69, 0x6c, 0x65,
|
||||
0x64, 0x4a, 0x6f, 0x62, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65,
|
||||
0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f,
|
||||
0x72, 0x12, 0x51, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x62,
|
||||
0x75, 0x69, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64,
|
||||
0x4a, 0x6f, 0x62, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69,
|
||||
0x6c, 0x64, 0x48, 0x00, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42,
|
||||
0x75, 0x69, 0x6c, 0x64, 0x12, 0x51, 0x0a, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65,
|
||||
0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x46, 0x61, 0x69,
|
||||
0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49,
|
||||
0x6d, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74,
|
||||
0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x52, 0x0a, 0x10, 0x74, 0x65, 0x6d, 0x70, 0x6c,
|
||||
0x61, 0x74, 0x65, 0x5f, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64,
|
||||
0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c,
|
||||
0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x74, 0x65, 0x6d,
|
||||
0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x65,
|
||||
0x72, 0x72, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x1a, 0x26, 0x0a, 0x0e, 0x57, 0x6f,
|
||||
0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05,
|
||||
0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61,
|
||||
0x74, 0x65, 0x1a, 0x10, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d,
|
||||
0x70, 0x6f, 0x72, 0x74, 0x1a, 0x10, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65,
|
||||
0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xd8,
|
||||
0x05, 0x0a, 0x0c, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x12,
|
||||
0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x02,
|
||||
0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x65, 0x72, 0x64, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x4c, 0x0a,
|
||||
0x12, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62,
|
||||
0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76,
|
||||
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65,
|
||||
0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61,
|
||||
0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x4c, 0x0a, 0x14, 0x75,
|
||||
0x73, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76,
|
||||
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65,
|
||||
0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x75, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, 0x69, 0x61,
|
||||
0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61,
|
||||
0x64, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d,
|
||||
0x65, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x7a, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74,
|
||||
0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08,
|
||||
0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08,
|
||||
0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x12, 0x43, 0x0a, 0x0f, 0x76, 0x61, 0x72, 0x69,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,
|
||||
0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
|
||||
0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x76,
|
||||
0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x4a, 0x04, 0x08,
|
||||
0x02, 0x10, 0x03, 0x22, 0x4a, 0x0a, 0x12, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f,
|
||||
0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62,
|
||||
0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64,
|
||||
0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x22,
|
||||
0x68, 0x0a, 0x13, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74,
|
||||
0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05,
|
||||
0x52, 0x0f, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65,
|
||||
0x64, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||
0x05, 0x52, 0x06, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x2a, 0x34, 0x0a, 0x09, 0x4c, 0x6f, 0x67,
|
||||
0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53,
|
||||
0x49, 0x4f, 0x4e, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x45, 0x4d, 0x4f, 0x4e, 0x10, 0x00, 0x12, 0x0f,
|
||||
0x0a, 0x0b, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x52, 0x10, 0x01, 0x32,
|
||||
0xec, 0x02, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x44,
|
||||
0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x3c, 0x0a, 0x0a, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65,
|
||||
0x4a, 0x6f, 0x62, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
|
||||
0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64,
|
||||
0x4a, 0x6f, 0x62, 0x12, 0x52, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f,
|
||||
0x74, 0x61, 0x12, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
|
||||
0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x54, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70,
|
||||
0x61, 0x63, 0x65, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x29, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43,
|
||||
0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x57, 0x6f, 0x72, 0x6b,
|
||||
0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x48, 0x00, 0x52, 0x0e, 0x77, 0x6f,
|
||||
0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x54, 0x0a, 0x0f,
|
||||
0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f,
|
||||
0x62, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74,
|
||||
0x48, 0x00, 0x52, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f,
|
||||
0x72, 0x74, 0x12, 0x55, 0x0a, 0x10, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64,
|
||||
0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70,
|
||||
0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74,
|
||||
0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6c,
|
||||
0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x1a, 0x5b, 0x0a, 0x0e, 0x57, 0x6f, 0x72,
|
||||
0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73,
|
||||
0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74,
|
||||
0x65, 0x12, 0x33, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02,
|
||||
0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73,
|
||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x1a, 0x81, 0x02, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c,
|
||||
0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x3e, 0x0a, 0x0f, 0x73, 0x74, 0x61,
|
||||
0x72, 0x74, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03,
|
||||
0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
|
||||
0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0e, 0x73, 0x74, 0x61, 0x72, 0x74,
|
||||
0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x0e, 0x73, 0x74, 0x6f,
|
||||
0x70, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
|
||||
0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
|
||||
0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0d, 0x73, 0x74, 0x6f, 0x70, 0x52, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x72, 0x69, 0x63, 0x68, 0x5f,
|
||||
0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52,
|
||||
0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x52, 0x0e, 0x72, 0x69,
|
||||
0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2c, 0x0a, 0x12,
|
||||
0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65,
|
||||
0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41, 0x75, 0x74,
|
||||
0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x1a, 0x45, 0x0a, 0x0e, 0x54, 0x65,
|
||||
0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x33, 0x0a, 0x09,
|
||||
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x73, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xb0, 0x01, 0x0a, 0x03, 0x4c, 0x6f,
|
||||
0x67, 0x12, 0x2f, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0e, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64,
|
||||
0x2e, 0x4c, 0x6f, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72,
|
||||
0x63, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x0e, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
|
||||
0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12,
|
||||
0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x14,
|
||||
0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73,
|
||||
0x74, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x05,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0x8a, 0x02, 0x0a,
|
||||
0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73,
|
||||
0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
|
||||
0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x12,
|
||||
0x4c, 0x0a, 0x12, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x72, 0x69,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61,
|
||||
0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70,
|
||||
0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x4c, 0x0a,
|
||||
0x14, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76,
|
||||
0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62,
|
||||
0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x75, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72,
|
||||
0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72,
|
||||
0x65, 0x61, 0x64, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, 0x65, 0x61,
|
||||
0x64, 0x6d, 0x65, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x7a, 0x0a, 0x11, 0x55, 0x70, 0x64,
|
||||
0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a,
|
||||
0x0a, 0x08, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
|
||||
0x52, 0x08, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x12, 0x43, 0x0a, 0x0f, 0x76, 0x61,
|
||||
0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20,
|
||||
0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
|
||||
0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52,
|
||||
0x0e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x4a,
|
||||
0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0x4a, 0x0a, 0x12, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51,
|
||||
0x75, 0x6f, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6a,
|
||||
0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62,
|
||||
0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x43, 0x6f, 0x73,
|
||||
0x74, 0x22, 0x68, 0x0a, 0x13, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x72, 0x65, 0x64,
|
||||
0x69, 0x74, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x05, 0x52, 0x0f, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x73, 0x75,
|
||||
0x6d, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x05, 0x52, 0x06, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x2a, 0x34, 0x0a, 0x09, 0x4c,
|
||||
0x6f, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x50, 0x52, 0x4f, 0x56,
|
||||
0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x45, 0x4d, 0x4f, 0x4e, 0x10, 0x00,
|
||||
0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x52, 0x10,
|
||||
0x01, 0x32, 0xec, 0x02, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
|
||||
0x72, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x3c, 0x0a, 0x0a, 0x41, 0x63, 0x71, 0x75, 0x69,
|
||||
0x72, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72,
|
||||
0x65, 0x64, 0x4a, 0x6f, 0x62, 0x12, 0x52, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51,
|
||||
0x75, 0x6f, 0x74, 0x61, 0x12, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x09, 0x55, 0x70, 0x64, 0x61, 0x74,
|
||||
0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x65, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x65, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x46, 0x61, 0x69, 0x6c, 0x4a, 0x6f, 0x62,
|
||||
0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e,
|
||||
0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76,
|
||||
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3e,
|
||||
0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1a, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d,
|
||||
0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76,
|
||||
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x2e,
|
||||
0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x64,
|
||||
0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x72, 0x6f, 0x76,
|
||||
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
|
||||
0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74,
|
||||
0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x09, 0x55, 0x70, 0x64,
|
||||
0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
|
||||
0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
|
||||
0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x46, 0x61, 0x69, 0x6c, 0x4a,
|
||||
0x6f, 0x62, 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
|
||||
0x64, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||
0x12, 0x3e, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12,
|
||||
0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43,
|
||||
0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||
0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63,
|
||||
0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x72,
|
||||
0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -1618,7 +1616,7 @@ var file_provisionerd_proto_provisionerd_proto_goTypes = []interface{}{
|
|||
(*proto.VariableValue)(nil), // 22: provisioner.VariableValue
|
||||
(*proto.RichParameterValue)(nil), // 23: provisioner.RichParameterValue
|
||||
(*proto.GitAuthProvider)(nil), // 24: provisioner.GitAuthProvider
|
||||
(*proto.Provision_Metadata)(nil), // 25: provisioner.Provision.Metadata
|
||||
(*proto.Metadata)(nil), // 25: provisioner.Metadata
|
||||
(*proto.Resource)(nil), // 26: provisioner.Resource
|
||||
(*proto.RichParameter)(nil), // 27: provisioner.RichParameter
|
||||
}
|
||||
|
@ -1642,12 +1640,12 @@ var file_provisionerd_proto_provisionerd_proto_depIdxs = []int32{
|
|||
23, // 16: provisionerd.AcquiredJob.WorkspaceBuild.rich_parameter_values:type_name -> provisioner.RichParameterValue
|
||||
22, // 17: provisionerd.AcquiredJob.WorkspaceBuild.variable_values:type_name -> provisioner.VariableValue
|
||||
24, // 18: provisionerd.AcquiredJob.WorkspaceBuild.git_auth_providers:type_name -> provisioner.GitAuthProvider
|
||||
25, // 19: provisionerd.AcquiredJob.WorkspaceBuild.metadata:type_name -> provisioner.Provision.Metadata
|
||||
25, // 20: provisionerd.AcquiredJob.TemplateImport.metadata:type_name -> provisioner.Provision.Metadata
|
||||
25, // 19: provisionerd.AcquiredJob.WorkspaceBuild.metadata:type_name -> provisioner.Metadata
|
||||
25, // 20: provisionerd.AcquiredJob.TemplateImport.metadata:type_name -> provisioner.Metadata
|
||||
22, // 21: provisionerd.AcquiredJob.TemplateImport.user_variable_values:type_name -> provisioner.VariableValue
|
||||
23, // 22: provisionerd.AcquiredJob.TemplateDryRun.rich_parameter_values:type_name -> provisioner.RichParameterValue
|
||||
22, // 23: provisionerd.AcquiredJob.TemplateDryRun.variable_values:type_name -> provisioner.VariableValue
|
||||
25, // 24: provisionerd.AcquiredJob.TemplateDryRun.metadata:type_name -> provisioner.Provision.Metadata
|
||||
25, // 24: provisionerd.AcquiredJob.TemplateDryRun.metadata:type_name -> provisioner.Metadata
|
||||
26, // 25: provisionerd.CompletedJob.WorkspaceBuild.resources:type_name -> provisioner.Resource
|
||||
26, // 26: provisionerd.CompletedJob.TemplateImport.start_resources:type_name -> provisioner.Resource
|
||||
26, // 27: provisionerd.CompletedJob.TemplateImport.stop_resources:type_name -> provisioner.Resource
|
||||
|
|
|
@ -19,12 +19,12 @@ message AcquiredJob {
|
|||
repeated provisioner.RichParameterValue rich_parameter_values = 4;
|
||||
repeated provisioner.VariableValue variable_values = 5;
|
||||
repeated provisioner.GitAuthProvider git_auth_providers = 6;
|
||||
provisioner.Provision.Metadata metadata = 7;
|
||||
provisioner.Metadata metadata = 7;
|
||||
bytes state = 8;
|
||||
string log_level = 9;
|
||||
}
|
||||
message TemplateImport {
|
||||
provisioner.Provision.Metadata metadata = 1;
|
||||
provisioner.Metadata metadata = 1;
|
||||
repeated provisioner.VariableValue user_variable_values = 2;
|
||||
}
|
||||
message TemplateDryRun {
|
||||
|
@ -32,7 +32,7 @@ message AcquiredJob {
|
|||
|
||||
repeated provisioner.RichParameterValue rich_parameter_values = 2;
|
||||
repeated provisioner.VariableValue variable_values = 3;
|
||||
provisioner.Provision.Metadata metadata = 4;
|
||||
provisioner.Metadata metadata = 4;
|
||||
}
|
||||
|
||||
string job_id = 1;
|
||||
|
@ -45,9 +45,9 @@ message AcquiredJob {
|
|||
TemplateImport template_import = 7;
|
||||
TemplateDryRun template_dry_run = 8;
|
||||
}
|
||||
// trace_metadata is currently used for tracing information only. It allows
|
||||
// jobs to be tied to the request that created them.
|
||||
map<string, string> trace_metadata = 9;
|
||||
// trace_metadata is currently used for tracing information only. It allows
|
||||
// jobs to be tied to the request that created them.
|
||||
map<string, string> trace_metadata = 9;
|
||||
}
|
||||
|
||||
message FailedJob {
|
||||
|
@ -113,7 +113,7 @@ message UpdateJobRequest {
|
|||
string job_id = 1;
|
||||
repeated Log logs = 2;
|
||||
repeated provisioner.TemplateVariable template_variables = 4;
|
||||
repeated provisioner.VariableValue user_variable_values = 5;
|
||||
repeated provisioner.VariableValue user_variable_values = 5;
|
||||
bytes readme = 6;
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,7 @@ message UpdateJobResponse {
|
|||
reserved 2;
|
||||
|
||||
bool canceled = 1;
|
||||
repeated provisioner.VariableValue variable_values = 3;
|
||||
repeated provisioner.VariableValue variable_values = 3;
|
||||
}
|
||||
|
||||
message CommitQuotaRequest {
|
||||
|
|
|
@ -12,7 +12,6 @@ import (
|
|||
"github.com/hashicorp/yamux"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/valyala/fasthttp/fasthttputil"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.14.0"
|
||||
|
@ -44,7 +43,6 @@ type Provisioners map[string]sdkproto.DRPCProvisionerClient
|
|||
|
||||
// Options provides customizations to the behavior of a provisioner daemon.
|
||||
type Options struct {
|
||||
Filesystem afero.Fs
|
||||
Logger slog.Logger
|
||||
TracerProvider trace.TracerProvider
|
||||
Metrics *Metrics
|
||||
|
@ -56,8 +54,6 @@ type Options struct {
|
|||
JobPollJitter time.Duration
|
||||
JobPollDebounce time.Duration
|
||||
Provisioners Provisioners
|
||||
// WorkDirectory must not be used by multiple processes at once.
|
||||
WorkDirectory string
|
||||
}
|
||||
|
||||
// New creates and starts a provisioner daemon.
|
||||
|
@ -80,9 +76,6 @@ func New(clientDialer Dialer, opts *Options) *Server {
|
|||
if opts.LogBufferInterval == 0 {
|
||||
opts.LogBufferInterval = 250 * time.Millisecond
|
||||
}
|
||||
if opts.Filesystem == nil {
|
||||
opts.Filesystem = afero.NewOsFs()
|
||||
}
|
||||
if opts.TracerProvider == nil {
|
||||
opts.TracerProvider = trace.NewNoopTracerProvider()
|
||||
}
|
||||
|
@ -405,8 +398,6 @@ func (p *Server) acquireJob(ctx context.Context) {
|
|||
Updater: p,
|
||||
QuotaCommitter: p,
|
||||
Logger: p.opts.Logger.Named("runner"),
|
||||
Filesystem: p.opts.Filesystem,
|
||||
WorkDirectory: p.opts.WorkDirectory,
|
||||
Provisioner: provisioner,
|
||||
UpdateInterval: p.opts.UpdateInterval,
|
||||
ForceCancelInterval: p.opts.ForceCancelInterval,
|
||||
|
|
|
@ -25,7 +25,6 @@ import (
|
|||
"cdr.dev/slog/sloggers/slogtest"
|
||||
"github.com/coder/coder/v2/provisionerd"
|
||||
"github.com/coder/coder/v2/provisionerd/proto"
|
||||
"github.com/coder/coder/v2/provisionerd/runner"
|
||||
"github.com/coder/coder/v2/provisionersdk"
|
||||
sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
|
@ -129,7 +128,7 @@ func TestProvisionerd(t *testing.T) {
|
|||
}),
|
||||
Type: &proto.AcquiredJob_TemplateImport_{
|
||||
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
||||
Metadata: &sdkproto.Provision_Metadata{},
|
||||
Metadata: &sdkproto.Metadata{},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
@ -144,10 +143,15 @@ func TestProvisionerd(t *testing.T) {
|
|||
}), nil
|
||||
}, provisionerd.Provisioners{
|
||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||
parse: func(request *sdkproto.Parse_Request, stream sdkproto.DRPCProvisioner_ParseStream) error {
|
||||
parse: func(_ *provisionersdk.Session, _ *sdkproto.ParseRequest, _ <-chan struct{}) *sdkproto.ParseComplete {
|
||||
closerMutex.Lock()
|
||||
defer closerMutex.Unlock()
|
||||
return closer.Close()
|
||||
err := closer.Close()
|
||||
c := &sdkproto.ParseComplete{}
|
||||
if err != nil {
|
||||
c.Error = err.Error()
|
||||
}
|
||||
return c
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
@ -180,7 +184,7 @@ func TestProvisionerd(t *testing.T) {
|
|||
}),
|
||||
Type: &proto.AcquiredJob_TemplateImport_{
|
||||
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
||||
Metadata: &sdkproto.Provision_Metadata{},
|
||||
Metadata: &sdkproto.Metadata{},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
@ -220,7 +224,7 @@ func TestProvisionerd(t *testing.T) {
|
|||
}),
|
||||
Type: &proto.AcquiredJob_TemplateImport_{
|
||||
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
||||
Metadata: &sdkproto.Provision_Metadata{},
|
||||
Metadata: &sdkproto.Metadata{},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
@ -235,9 +239,13 @@ func TestProvisionerd(t *testing.T) {
|
|||
}), nil
|
||||
}, provisionerd.Provisioners{
|
||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||
parse: func(request *sdkproto.Parse_Request, stream sdkproto.DRPCProvisioner_ParseStream) error {
|
||||
<-stream.Context().Done()
|
||||
return nil
|
||||
parse: func(
|
||||
_ *provisionersdk.Session,
|
||||
_ *sdkproto.ParseRequest,
|
||||
cancelOrComplete <-chan struct{},
|
||||
) *sdkproto.ParseComplete {
|
||||
<-cancelOrComplete
|
||||
return &sdkproto.ParseComplete{}
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
@ -255,7 +263,6 @@ func TestProvisionerd(t *testing.T) {
|
|||
didComplete atomic.Bool
|
||||
didLog atomic.Bool
|
||||
didAcquireJob atomic.Bool
|
||||
didDryRun = atomic.NewBool(true)
|
||||
didReadme atomic.Bool
|
||||
completeChan = make(chan struct{})
|
||||
completeOnce sync.Once
|
||||
|
@ -273,12 +280,12 @@ func TestProvisionerd(t *testing.T) {
|
|||
JobId: "test",
|
||||
Provisioner: "someprovisioner",
|
||||
TemplateSourceArchive: createTar(t, map[string]string{
|
||||
"test.txt": "content",
|
||||
runner.ReadmeFile: "# A cool template 😎\n",
|
||||
"test.txt": "content",
|
||||
provisionersdk.ReadmeFile: "# A cool template 😎\n",
|
||||
}),
|
||||
Type: &proto.AcquiredJob_TemplateImport_{
|
||||
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
||||
Metadata: &sdkproto.Provision_Metadata{},
|
||||
Metadata: &sdkproto.Metadata{},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
@ -299,54 +306,34 @@ func TestProvisionerd(t *testing.T) {
|
|||
}), nil
|
||||
}, provisionerd.Provisioners{
|
||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||
parse: func(request *sdkproto.Parse_Request, stream sdkproto.DRPCProvisioner_ParseStream) error {
|
||||
data, err := os.ReadFile(filepath.Join(request.Directory, "test.txt"))
|
||||
parse: func(
|
||||
s *provisionersdk.Session,
|
||||
_ *sdkproto.ParseRequest,
|
||||
cancelOrComplete <-chan struct{},
|
||||
) *sdkproto.ParseComplete {
|
||||
data, err := os.ReadFile(filepath.Join(s.WorkDirectory, "test.txt"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "content", string(data))
|
||||
|
||||
err = stream.Send(&sdkproto.Parse_Response{
|
||||
Type: &sdkproto.Parse_Response_Log{
|
||||
Log: &sdkproto.Log{
|
||||
Level: sdkproto.LogLevel_INFO,
|
||||
Output: "hello",
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = stream.Send(&sdkproto.Parse_Response{
|
||||
Type: &sdkproto.Parse_Response_Complete{
|
||||
Complete: &sdkproto.Parse_Complete{},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return nil
|
||||
s.ProvisionLog(sdkproto.LogLevel_INFO, "hello")
|
||||
return &sdkproto.ParseComplete{}
|
||||
},
|
||||
provision: func(stream sdkproto.DRPCProvisioner_ProvisionStream) error {
|
||||
request, err := stream.Recv()
|
||||
require.NoError(t, err)
|
||||
if request.GetApply() != nil {
|
||||
didDryRun.Store(false)
|
||||
plan: func(
|
||||
s *provisionersdk.Session,
|
||||
_ *sdkproto.PlanRequest,
|
||||
cancelOrComplete <-chan struct{},
|
||||
) *sdkproto.PlanComplete {
|
||||
s.ProvisionLog(sdkproto.LogLevel_INFO, "hello")
|
||||
return &sdkproto.PlanComplete{
|
||||
Resources: []*sdkproto.Resource{},
|
||||
}
|
||||
err = stream.Send(&sdkproto.Provision_Response{
|
||||
Type: &sdkproto.Provision_Response_Log{
|
||||
Log: &sdkproto.Log{
|
||||
Level: sdkproto.LogLevel_INFO,
|
||||
Output: "hello",
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = stream.Send(&sdkproto.Provision_Response{
|
||||
Type: &sdkproto.Provision_Response_Complete{
|
||||
Complete: &sdkproto.Provision_Complete{
|
||||
Resources: []*sdkproto.Resource{},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return nil
|
||||
},
|
||||
apply: func(
|
||||
_ *provisionersdk.Session,
|
||||
_ *sdkproto.ApplyRequest,
|
||||
_ <-chan struct{},
|
||||
) *sdkproto.ApplyComplete {
|
||||
t.Error("dry run should not apply")
|
||||
return &sdkproto.ApplyComplete{}
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
@ -355,7 +342,6 @@ func TestProvisionerd(t *testing.T) {
|
|||
require.NoError(t, closer.Close())
|
||||
assert.True(t, didLog.Load(), "should log some updates")
|
||||
assert.True(t, didComplete.Load(), "should complete the job")
|
||||
assert.True(t, didDryRun.Load(), "should be a dry run")
|
||||
})
|
||||
|
||||
t.Run("TemplateDryRun", func(t *testing.T) {
|
||||
|
@ -371,7 +357,7 @@ func TestProvisionerd(t *testing.T) {
|
|||
completeChan = make(chan struct{})
|
||||
completeOnce sync.Once
|
||||
|
||||
metadata = &sdkproto.Provision_Metadata{}
|
||||
metadata = &sdkproto.Metadata{}
|
||||
)
|
||||
|
||||
closer := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
||||
|
@ -414,16 +400,22 @@ func TestProvisionerd(t *testing.T) {
|
|||
}), nil
|
||||
}, provisionerd.Provisioners{
|
||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||
provision: func(stream sdkproto.DRPCProvisioner_ProvisionStream) error {
|
||||
err := stream.Send(&sdkproto.Provision_Response{
|
||||
Type: &sdkproto.Provision_Response_Complete{
|
||||
Complete: &sdkproto.Provision_Complete{
|
||||
Resources: []*sdkproto.Resource{},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return nil
|
||||
plan: func(
|
||||
_ *provisionersdk.Session,
|
||||
_ *sdkproto.PlanRequest,
|
||||
_ <-chan struct{},
|
||||
) *sdkproto.PlanComplete {
|
||||
return &sdkproto.PlanComplete{
|
||||
Resources: []*sdkproto.Resource{},
|
||||
}
|
||||
},
|
||||
apply: func(
|
||||
_ *provisionersdk.Session,
|
||||
_ *sdkproto.ApplyRequest,
|
||||
_ <-chan struct{},
|
||||
) *sdkproto.ApplyComplete {
|
||||
t.Error("dry run should not apply")
|
||||
return &sdkproto.ApplyComplete{}
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
@ -464,7 +456,7 @@ func TestProvisionerd(t *testing.T) {
|
|||
}),
|
||||
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
||||
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
||||
Metadata: &sdkproto.Provision_Metadata{},
|
||||
Metadata: &sdkproto.Metadata{},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
@ -482,24 +474,20 @@ func TestProvisionerd(t *testing.T) {
|
|||
}), nil
|
||||
}, provisionerd.Provisioners{
|
||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||
provision: func(stream sdkproto.DRPCProvisioner_ProvisionStream) error {
|
||||
err := stream.Send(&sdkproto.Provision_Response{
|
||||
Type: &sdkproto.Provision_Response_Log{
|
||||
Log: &sdkproto.Log{
|
||||
Level: sdkproto.LogLevel_DEBUG,
|
||||
Output: "wow",
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = stream.Send(&sdkproto.Provision_Response{
|
||||
Type: &sdkproto.Provision_Response_Complete{
|
||||
Complete: &sdkproto.Provision_Complete{},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return nil
|
||||
plan: func(
|
||||
s *provisionersdk.Session,
|
||||
_ *sdkproto.PlanRequest,
|
||||
cancelOrComplete <-chan struct{},
|
||||
) *sdkproto.PlanComplete {
|
||||
s.ProvisionLog(sdkproto.LogLevel_DEBUG, "wow")
|
||||
return &sdkproto.PlanComplete{}
|
||||
},
|
||||
apply: func(
|
||||
_ *provisionersdk.Session,
|
||||
_ *sdkproto.ApplyRequest,
|
||||
_ <-chan struct{},
|
||||
) *sdkproto.ApplyComplete {
|
||||
return &sdkproto.ApplyComplete{}
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
@ -540,7 +528,7 @@ func TestProvisionerd(t *testing.T) {
|
|||
}),
|
||||
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
||||
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
||||
Metadata: &sdkproto.Provision_Metadata{},
|
||||
Metadata: &sdkproto.Metadata{},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
@ -567,40 +555,46 @@ func TestProvisionerd(t *testing.T) {
|
|||
}), nil
|
||||
}, provisionerd.Provisioners{
|
||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||
provision: func(stream sdkproto.DRPCProvisioner_ProvisionStream) error {
|
||||
err := stream.Send(&sdkproto.Provision_Response{
|
||||
Type: &sdkproto.Provision_Response_Log{
|
||||
Log: &sdkproto.Log{
|
||||
Level: sdkproto.LogLevel_DEBUG,
|
||||
Output: "wow",
|
||||
plan: func(
|
||||
s *provisionersdk.Session,
|
||||
_ *sdkproto.PlanRequest,
|
||||
cancelOrComplete <-chan struct{},
|
||||
) *sdkproto.PlanComplete {
|
||||
s.ProvisionLog(sdkproto.LogLevel_DEBUG, "wow")
|
||||
return &sdkproto.PlanComplete{
|
||||
Resources: []*sdkproto.Resource{
|
||||
{
|
||||
DailyCost: 10,
|
||||
},
|
||||
{
|
||||
DailyCost: 15,
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = stream.Send(&sdkproto.Provision_Response{
|
||||
Type: &sdkproto.Provision_Response_Complete{
|
||||
Complete: &sdkproto.Provision_Complete{
|
||||
Resources: []*sdkproto.Resource{
|
||||
{
|
||||
DailyCost: 10,
|
||||
},
|
||||
{
|
||||
DailyCost: 15,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
apply: func(
|
||||
_ *provisionersdk.Session,
|
||||
_ *sdkproto.ApplyRequest,
|
||||
_ <-chan struct{},
|
||||
) *sdkproto.ApplyComplete {
|
||||
t.Error("should not apply when resources exceed quota")
|
||||
return &sdkproto.ApplyComplete{
|
||||
Resources: []*sdkproto.Resource{
|
||||
{
|
||||
DailyCost: 10,
|
||||
},
|
||||
{
|
||||
DailyCost: 15,
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return nil
|
||||
}
|
||||
},
|
||||
}),
|
||||
})
|
||||
require.Condition(t, closedWithin(completeChan, testutil.WaitShort))
|
||||
require.NoError(t, closer.Close())
|
||||
assert.True(t, didLog.Load(), "should log some updates")
|
||||
assert.False(t, didComplete.Load(), "should complete the job")
|
||||
assert.False(t, didComplete.Load(), "should not complete the job")
|
||||
assert.True(t, didFail.Load(), "should fail the job")
|
||||
})
|
||||
|
||||
|
@ -633,7 +627,7 @@ func TestProvisionerd(t *testing.T) {
|
|||
}),
|
||||
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
||||
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
||||
Metadata: &sdkproto.Provision_Metadata{},
|
||||
Metadata: &sdkproto.Metadata{},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
@ -646,14 +640,24 @@ func TestProvisionerd(t *testing.T) {
|
|||
}), nil
|
||||
}, provisionerd.Provisioners{
|
||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||
provision: func(stream sdkproto.DRPCProvisioner_ProvisionStream) error {
|
||||
return stream.Send(&sdkproto.Provision_Response{
|
||||
Type: &sdkproto.Provision_Response_Complete{
|
||||
Complete: &sdkproto.Provision_Complete{
|
||||
Error: "some error",
|
||||
},
|
||||
},
|
||||
})
|
||||
plan: func(
|
||||
s *provisionersdk.Session,
|
||||
_ *sdkproto.PlanRequest,
|
||||
cancelOrComplete <-chan struct{},
|
||||
) *sdkproto.PlanComplete {
|
||||
return &sdkproto.PlanComplete{
|
||||
Error: "some error",
|
||||
}
|
||||
},
|
||||
apply: func(
|
||||
_ *provisionersdk.Session,
|
||||
_ *sdkproto.ApplyRequest,
|
||||
_ <-chan struct{},
|
||||
) *sdkproto.ApplyComplete {
|
||||
t.Error("should not apply when plan errors")
|
||||
return &sdkproto.ApplyComplete{
|
||||
Error: "some error",
|
||||
}
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
@ -683,7 +687,7 @@ func TestProvisionerd(t *testing.T) {
|
|||
}),
|
||||
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
||||
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
||||
Metadata: &sdkproto.Provision_Metadata{},
|
||||
Metadata: &sdkproto.Metadata{},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
@ -712,31 +716,24 @@ func TestProvisionerd(t *testing.T) {
|
|||
}), nil
|
||||
}, provisionerd.Provisioners{
|
||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||
provision: func(stream sdkproto.DRPCProvisioner_ProvisionStream) error {
|
||||
// Ignore the first provision message!
|
||||
_, _ = stream.Recv()
|
||||
|
||||
err := stream.Send(&sdkproto.Provision_Response{
|
||||
Type: &sdkproto.Provision_Response_Log{
|
||||
Log: &sdkproto.Log{
|
||||
Level: sdkproto.LogLevel_DEBUG,
|
||||
Output: "in progress",
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
msg, err := stream.Recv()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, msg.GetCancel())
|
||||
|
||||
return stream.Send(&sdkproto.Provision_Response{
|
||||
Type: &sdkproto.Provision_Response_Complete{
|
||||
Complete: &sdkproto.Provision_Complete{
|
||||
Error: "some error",
|
||||
},
|
||||
},
|
||||
})
|
||||
plan: func(
|
||||
s *provisionersdk.Session,
|
||||
_ *sdkproto.PlanRequest,
|
||||
canceledOrComplete <-chan struct{},
|
||||
) *sdkproto.PlanComplete {
|
||||
s.ProvisionLog(sdkproto.LogLevel_DEBUG, "in progress")
|
||||
<-canceledOrComplete
|
||||
return &sdkproto.PlanComplete{
|
||||
Error: "some error",
|
||||
}
|
||||
},
|
||||
apply: func(
|
||||
_ *provisionersdk.Session,
|
||||
_ *sdkproto.ApplyRequest,
|
||||
_ <-chan struct{},
|
||||
) *sdkproto.ApplyComplete {
|
||||
t.Error("should not apply when shut down during plan")
|
||||
return &sdkproto.ApplyComplete{}
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
@ -768,7 +765,7 @@ func TestProvisionerd(t *testing.T) {
|
|||
}),
|
||||
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
||||
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
||||
Metadata: &sdkproto.Provision_Metadata{},
|
||||
Metadata: &sdkproto.Metadata{},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
@ -805,31 +802,24 @@ func TestProvisionerd(t *testing.T) {
|
|||
}), nil
|
||||
}, provisionerd.Provisioners{
|
||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||
provision: func(stream sdkproto.DRPCProvisioner_ProvisionStream) error {
|
||||
// Ignore the first provision message!
|
||||
_, _ = stream.Recv()
|
||||
|
||||
err := stream.Send(&sdkproto.Provision_Response{
|
||||
Type: &sdkproto.Provision_Response_Log{
|
||||
Log: &sdkproto.Log{
|
||||
Level: sdkproto.LogLevel_DEBUG,
|
||||
Output: "in progress",
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
msg, err := stream.Recv()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, msg.GetCancel())
|
||||
|
||||
return stream.Send(&sdkproto.Provision_Response{
|
||||
Type: &sdkproto.Provision_Response_Complete{
|
||||
Complete: &sdkproto.Provision_Complete{
|
||||
Error: "some error",
|
||||
},
|
||||
},
|
||||
})
|
||||
plan: func(
|
||||
s *provisionersdk.Session,
|
||||
_ *sdkproto.PlanRequest,
|
||||
canceledOrComplete <-chan struct{},
|
||||
) *sdkproto.PlanComplete {
|
||||
s.ProvisionLog(sdkproto.LogLevel_DEBUG, "in progress")
|
||||
<-canceledOrComplete
|
||||
return &sdkproto.PlanComplete{
|
||||
Error: "some error",
|
||||
}
|
||||
},
|
||||
apply: func(
|
||||
_ *provisionersdk.Session,
|
||||
_ *sdkproto.ApplyRequest,
|
||||
_ <-chan struct{},
|
||||
) *sdkproto.ApplyComplete {
|
||||
t.Error("should not apply when shut down during plan")
|
||||
return &sdkproto.ApplyComplete{}
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
@ -867,7 +857,7 @@ func TestProvisionerd(t *testing.T) {
|
|||
}),
|
||||
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
||||
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
||||
Metadata: &sdkproto.Provision_Metadata{},
|
||||
Metadata: &sdkproto.Metadata{},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
@ -898,16 +888,22 @@ func TestProvisionerd(t *testing.T) {
|
|||
return client, nil
|
||||
}, provisionerd.Provisioners{
|
||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||
provision: func(stream sdkproto.DRPCProvisioner_ProvisionStream) error {
|
||||
// Ignore the first provision message!
|
||||
_, _ = stream.Recv()
|
||||
return stream.Send(&sdkproto.Provision_Response{
|
||||
Type: &sdkproto.Provision_Response_Complete{
|
||||
Complete: &sdkproto.Provision_Complete{
|
||||
Error: "some error",
|
||||
},
|
||||
},
|
||||
})
|
||||
plan: func(
|
||||
_ *provisionersdk.Session,
|
||||
_ *sdkproto.PlanRequest,
|
||||
_ <-chan struct{},
|
||||
) *sdkproto.PlanComplete {
|
||||
return &sdkproto.PlanComplete{
|
||||
Error: "some error",
|
||||
}
|
||||
},
|
||||
apply: func(
|
||||
_ *provisionersdk.Session,
|
||||
_ *sdkproto.ApplyRequest,
|
||||
_ <-chan struct{},
|
||||
) *sdkproto.ApplyComplete {
|
||||
t.Error("should not apply when error during plan")
|
||||
return &sdkproto.ApplyComplete{}
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
@ -945,7 +941,7 @@ func TestProvisionerd(t *testing.T) {
|
|||
}),
|
||||
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
||||
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
||||
Metadata: &sdkproto.Provision_Metadata{},
|
||||
Metadata: &sdkproto.Metadata{},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
@ -977,14 +973,19 @@ func TestProvisionerd(t *testing.T) {
|
|||
return client, nil
|
||||
}, provisionerd.Provisioners{
|
||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||
provision: func(stream sdkproto.DRPCProvisioner_ProvisionStream) error {
|
||||
// Ignore the first provision message!
|
||||
_, _ = stream.Recv()
|
||||
return stream.Send(&sdkproto.Provision_Response{
|
||||
Type: &sdkproto.Provision_Response_Complete{
|
||||
Complete: &sdkproto.Provision_Complete{},
|
||||
},
|
||||
})
|
||||
plan: func(
|
||||
_ *provisionersdk.Session,
|
||||
_ *sdkproto.PlanRequest,
|
||||
_ <-chan struct{},
|
||||
) *sdkproto.PlanComplete {
|
||||
return &sdkproto.PlanComplete{}
|
||||
},
|
||||
apply: func(
|
||||
_ *provisionersdk.Session,
|
||||
_ *sdkproto.ApplyRequest,
|
||||
_ <-chan struct{},
|
||||
) *sdkproto.ApplyComplete {
|
||||
return &sdkproto.ApplyComplete{}
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
@ -1023,7 +1024,7 @@ func TestProvisionerd(t *testing.T) {
|
|||
}),
|
||||
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
||||
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
||||
Metadata: &sdkproto.Provision_Metadata{},
|
||||
Metadata: &sdkproto.Metadata{},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
@ -1056,24 +1057,21 @@ func TestProvisionerd(t *testing.T) {
|
|||
}), nil
|
||||
}, provisionerd.Provisioners{
|
||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||
provision: func(stream sdkproto.DRPCProvisioner_ProvisionStream) error {
|
||||
err := stream.Send(&sdkproto.Provision_Response{
|
||||
Type: &sdkproto.Provision_Response_Log{
|
||||
Log: &sdkproto.Log{
|
||||
Level: sdkproto.LogLevel_DEBUG,
|
||||
Output: "wow",
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = stream.Send(&sdkproto.Provision_Response{
|
||||
Type: &sdkproto.Provision_Response_Complete{
|
||||
Complete: &sdkproto.Provision_Complete{},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return nil
|
||||
plan: func(
|
||||
s *provisionersdk.Session,
|
||||
_ *sdkproto.PlanRequest,
|
||||
_ <-chan struct{},
|
||||
) *sdkproto.PlanComplete {
|
||||
s.ProvisionLog(sdkproto.LogLevel_DEBUG, "wow")
|
||||
return &sdkproto.PlanComplete{}
|
||||
},
|
||||
apply: func(
|
||||
s *provisionersdk.Session,
|
||||
_ *sdkproto.ApplyRequest,
|
||||
_ <-chan struct{},
|
||||
) *sdkproto.ApplyComplete {
|
||||
s.ProvisionLog(sdkproto.LogLevel_DEBUG, "wow")
|
||||
return &sdkproto.ApplyComplete{}
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
@ -1111,7 +1109,6 @@ func createProvisionerd(t *testing.T, dialer provisionerd.Dialer, provisioners p
|
|||
JobPollInterval: 50 * time.Millisecond,
|
||||
UpdateInterval: 50 * time.Millisecond,
|
||||
Provisioners: provisioners,
|
||||
WorkDirectory: t.TempDir(),
|
||||
})
|
||||
t.Cleanup(func() {
|
||||
_ = server.Close()
|
||||
|
@ -1172,15 +1169,15 @@ func createProvisionerClient(t *testing.T, done <-chan struct{}, server provisio
|
|||
_ = clientPipe.Close()
|
||||
_ = serverPipe.Close()
|
||||
})
|
||||
mux := drpcmux.New()
|
||||
err := sdkproto.DRPCRegisterProvisioner(mux, &server)
|
||||
require.NoError(t, err)
|
||||
srv := drpcserver.New(mux)
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
closed := make(chan struct{})
|
||||
go func() {
|
||||
defer close(closed)
|
||||
_ = srv.Serve(ctx, serverPipe)
|
||||
_ = provisionersdk.Serve(ctx, &server, &provisionersdk.ServeOptions{
|
||||
Listener: serverPipe,
|
||||
Logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug).Named("test-provisioner"),
|
||||
WorkDirectory: t.TempDir(),
|
||||
})
|
||||
}()
|
||||
t.Cleanup(func() {
|
||||
cancelFunc()
|
||||
|
@ -1200,16 +1197,21 @@ func createProvisionerClient(t *testing.T, done <-chan struct{}, server provisio
|
|||
}
|
||||
|
||||
type provisionerTestServer struct {
|
||||
parse func(request *sdkproto.Parse_Request, stream sdkproto.DRPCProvisioner_ParseStream) error
|
||||
provision func(stream sdkproto.DRPCProvisioner_ProvisionStream) error
|
||||
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
|
||||
}
|
||||
|
||||
func (p *provisionerTestServer) Parse(request *sdkproto.Parse_Request, stream sdkproto.DRPCProvisioner_ParseStream) error {
|
||||
return p.parse(request, stream)
|
||||
func (p *provisionerTestServer) Parse(s *provisionersdk.Session, r *sdkproto.ParseRequest, canceledOrComplete <-chan struct{}) *sdkproto.ParseComplete {
|
||||
return p.parse(s, r, canceledOrComplete)
|
||||
}
|
||||
|
||||
func (p *provisionerTestServer) Provision(stream sdkproto.DRPCProvisioner_ProvisionStream) error {
|
||||
return p.provision(stream)
|
||||
func (p *provisionerTestServer) Plan(s *provisionersdk.Session, r *sdkproto.PlanRequest, canceledOrComplete <-chan struct{}) *sdkproto.PlanComplete {
|
||||
return p.plan(s, r, canceledOrComplete)
|
||||
}
|
||||
|
||||
func (p *provisionerTestServer) Apply(s *provisionersdk.Session, r *sdkproto.ApplyRequest, canceledOrComplete <-chan struct{}) *sdkproto.ApplyComplete {
|
||||
return p.apply(s, r, canceledOrComplete)
|
||||
}
|
||||
|
||||
// Fulfills the protobuf interface for a ProvisionerDaemon with
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
package runner
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -18,7 +12,6 @@ import (
|
|||
|
||||
"github.com/google/uuid"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/spf13/afero"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.14.0"
|
||||
|
@ -54,14 +47,14 @@ type Runner struct {
|
|||
sender JobUpdater
|
||||
quotaCommitter QuotaCommitter
|
||||
logger slog.Logger
|
||||
filesystem afero.Fs
|
||||
workDirectory string
|
||||
provisioner sdkproto.DRPCProvisionerClient
|
||||
lastUpdate atomic.Pointer[time.Time]
|
||||
updateInterval time.Duration
|
||||
forceCancelInterval time.Duration
|
||||
logBufferInterval time.Duration
|
||||
|
||||
// session is the provisioning session with the (possibly remote) provisioner
|
||||
session sdkproto.DRPCProvisioner_SessionClient
|
||||
// closed when the Runner is finished sending any updates/failed/complete.
|
||||
done chan struct{}
|
||||
// active as long as we are not canceled
|
||||
|
@ -108,8 +101,6 @@ type Options struct {
|
|||
Updater JobUpdater
|
||||
QuotaCommitter QuotaCommitter
|
||||
Logger slog.Logger
|
||||
Filesystem afero.Fs
|
||||
WorkDirectory string
|
||||
Provisioner sdkproto.DRPCProvisionerClient
|
||||
UpdateInterval time.Duration
|
||||
ForceCancelInterval time.Duration
|
||||
|
@ -149,8 +140,6 @@ func New(
|
|||
sender: opts.Updater,
|
||||
quotaCommitter: opts.QuotaCommitter,
|
||||
logger: logger,
|
||||
filesystem: opts.Filesystem,
|
||||
workDirectory: opts.WorkDirectory,
|
||||
provisioner: opts.Provisioner,
|
||||
updateInterval: opts.UpdateInterval,
|
||||
forceCancelInterval: opts.ForceCancelInterval,
|
||||
|
@ -386,6 +375,14 @@ func (r *Runner) doCleanFinish(ctx context.Context) {
|
|||
r.setComplete(completedJob)
|
||||
}()
|
||||
|
||||
var err error
|
||||
r.session, err = r.provisioner.Session(ctx)
|
||||
if err != nil {
|
||||
failedJob = r.failedJobf("open session: %s", err)
|
||||
return
|
||||
}
|
||||
defer r.session.Close()
|
||||
|
||||
defer func() {
|
||||
ctx, span := r.startTrace(ctx, tracing.FuncName())
|
||||
defer span.End()
|
||||
|
@ -396,23 +393,6 @@ func (r *Runner) doCleanFinish(ctx context.Context) {
|
|||
Stage: "Cleaning Up",
|
||||
CreatedAt: time.Now().UnixMilli(),
|
||||
})
|
||||
|
||||
// Cleanup the work directory after execution.
|
||||
for attempt := 0; attempt < 5; attempt++ {
|
||||
err := r.filesystem.RemoveAll(r.workDirectory)
|
||||
if err != nil {
|
||||
// On Windows, open files cannot be removed.
|
||||
// When the provisioner daemon is shutting down,
|
||||
// it may take a few milliseconds for processes to exit.
|
||||
// See: https://github.com/golang/go/issues/50510
|
||||
r.logger.Debug(ctx, "failed to clean work directory; trying again", slog.Error(err))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
r.logger.Debug(ctx, "cleaned up work directory")
|
||||
break
|
||||
}
|
||||
|
||||
r.flushQueuedLogs(ctx)
|
||||
}()
|
||||
|
||||
|
@ -424,85 +404,19 @@ func (r *Runner) do(ctx context.Context) (*proto.CompletedJob, *proto.FailedJob)
|
|||
ctx, span := r.startTrace(ctx, tracing.FuncName())
|
||||
defer span.End()
|
||||
|
||||
err := r.filesystem.MkdirAll(r.workDirectory, 0o700)
|
||||
if err != nil {
|
||||
return nil, r.failedJobf("create work directory %q: %s", r.workDirectory, err)
|
||||
}
|
||||
|
||||
r.queueLog(ctx, &proto.Log{
|
||||
Source: proto.LogSource_PROVISIONER_DAEMON,
|
||||
Level: sdkproto.LogLevel_INFO,
|
||||
Stage: "Setting up",
|
||||
CreatedAt: time.Now().UnixMilli(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, r.failedJobf("write log: %s", err)
|
||||
}
|
||||
|
||||
r.logger.Info(ctx, "unpacking template source archive",
|
||||
slog.F("size_bytes", len(r.job.TemplateSourceArchive)),
|
||||
)
|
||||
|
||||
reader := tar.NewReader(bytes.NewBuffer(r.job.TemplateSourceArchive))
|
||||
for {
|
||||
header, err := reader.Next()
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
return nil, r.failedJobf("read template source archive: %s", err)
|
||||
}
|
||||
// #nosec
|
||||
headerPath := filepath.Join(r.workDirectory, header.Name)
|
||||
if !strings.HasPrefix(headerPath, filepath.Clean(r.workDirectory)) {
|
||||
return nil, r.failedJobf("tar attempts to target relative upper directory")
|
||||
}
|
||||
mode := header.FileInfo().Mode()
|
||||
if mode == 0 {
|
||||
mode = 0o600
|
||||
}
|
||||
switch header.Typeflag {
|
||||
case tar.TypeDir:
|
||||
err = r.filesystem.MkdirAll(headerPath, mode)
|
||||
if err != nil {
|
||||
return nil, r.failedJobf("mkdir %q: %s", headerPath, err)
|
||||
}
|
||||
r.logger.Debug(context.Background(), "extracted directory", slog.F("path", headerPath))
|
||||
case tar.TypeReg:
|
||||
file, err := r.filesystem.OpenFile(headerPath, os.O_CREATE|os.O_RDWR, mode)
|
||||
if err != nil {
|
||||
return nil, r.failedJobf("create file %q (mode %s): %s", headerPath, mode, err)
|
||||
}
|
||||
// Max file size of 10MiB.
|
||||
size, err := io.CopyN(file, reader, 10<<20)
|
||||
if errors.Is(err, io.EOF) {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
_ = file.Close()
|
||||
return nil, r.failedJobf("copy file %q: %s", headerPath, err)
|
||||
}
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
return nil, r.failedJobf("close file %q: %s", headerPath, err)
|
||||
}
|
||||
r.logger.Debug(context.Background(), "extracted file",
|
||||
slog.F("size_bytes", size),
|
||||
slog.F("path", headerPath),
|
||||
slog.F("mode", mode),
|
||||
)
|
||||
}
|
||||
}
|
||||
switch jobType := r.job.Type.(type) {
|
||||
case *proto.AcquiredJob_TemplateImport_:
|
||||
r.logger.Debug(context.Background(), "acquired job is template import",
|
||||
slog.F("user_variable_values", redactVariableValues(jobType.TemplateImport.UserVariableValues)),
|
||||
)
|
||||
|
||||
failedJob := r.runReadmeParse(ctx)
|
||||
if failedJob != nil {
|
||||
return nil, failedJob
|
||||
}
|
||||
return r.runTemplateImport(ctx)
|
||||
case *proto.AcquiredJob_TemplateDryRun_:
|
||||
r.logger.Debug(context.Background(), "acquired job is template dry-run",
|
||||
|
@ -525,6 +439,14 @@ func (r *Runner) do(ctx context.Context) (*proto.CompletedJob, *proto.FailedJob)
|
|||
}
|
||||
}
|
||||
|
||||
func (r *Runner) configure(config *sdkproto.Config) *proto.FailedJob {
|
||||
err := r.session.Send(&sdkproto.Request{Type: &sdkproto.Request_Config{Config: config}})
|
||||
if err != nil {
|
||||
return r.failedJobf("send config: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// heartbeatRoutine periodically sends updates on the job, which keeps coder server
|
||||
// from assuming the job is stalled, and allows the runner to learn if the job
|
||||
// has been canceled by the user.
|
||||
|
@ -577,45 +499,17 @@ func (r *Runner) heartbeatRoutine(ctx context.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
// ReadmeFile is the location we look for to extract documentation from template
|
||||
// versions.
|
||||
const ReadmeFile = "README.md"
|
||||
|
||||
func (r *Runner) runReadmeParse(ctx context.Context) *proto.FailedJob {
|
||||
ctx, span := r.startTrace(ctx, tracing.FuncName())
|
||||
defer span.End()
|
||||
|
||||
fi, err := afero.ReadFile(r.filesystem, path.Join(r.workDirectory, ReadmeFile))
|
||||
if err != nil {
|
||||
r.queueLog(ctx, &proto.Log{
|
||||
Source: proto.LogSource_PROVISIONER_DAEMON,
|
||||
Level: sdkproto.LogLevel_DEBUG,
|
||||
Stage: "No README.md provided",
|
||||
CreatedAt: time.Now().UnixMilli(),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = r.update(ctx, &proto.UpdateJobRequest{
|
||||
JobId: r.job.JobId,
|
||||
Logs: []*proto.Log{{
|
||||
Source: proto.LogSource_PROVISIONER_DAEMON,
|
||||
Level: sdkproto.LogLevel_INFO,
|
||||
Stage: "Adding README.md...",
|
||||
CreatedAt: time.Now().UnixMilli(),
|
||||
}},
|
||||
Readme: fi,
|
||||
})
|
||||
if err != nil {
|
||||
return r.failedJobf("write log: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *proto.FailedJob) {
|
||||
ctx, span := r.startTrace(ctx, tracing.FuncName())
|
||||
defer span.End()
|
||||
|
||||
failedJob := r.configure(&sdkproto.Config{
|
||||
TemplateSourceArchive: r.job.GetTemplateSourceArchive(),
|
||||
})
|
||||
if failedJob != nil {
|
||||
return nil, failedJob
|
||||
}
|
||||
|
||||
// Parse parameters and update the job with the parameter specs
|
||||
r.queueLog(ctx, &proto.Log{
|
||||
Source: proto.LogSource_PROVISIONER_DAEMON,
|
||||
|
@ -623,7 +517,7 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p
|
|||
Stage: "Parsing template parameters",
|
||||
CreatedAt: time.Now().UnixMilli(),
|
||||
})
|
||||
templateVariables, err := r.runTemplateImportParse(ctx)
|
||||
templateVariables, readme, err := r.runTemplateImportParse(ctx)
|
||||
if err != nil {
|
||||
return nil, r.failedJobf("run parse: %s", err)
|
||||
}
|
||||
|
@ -634,6 +528,7 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p
|
|||
JobId: r.job.JobId,
|
||||
TemplateVariables: templateVariables,
|
||||
UserVariableValues: r.job.GetTemplateImport().GetUserVariableValues(),
|
||||
Readme: readme,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, r.failedJobf("update job: %s", err)
|
||||
|
@ -646,7 +541,7 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p
|
|||
Stage: "Detecting persistent resources",
|
||||
CreatedAt: time.Now().UnixMilli(),
|
||||
})
|
||||
startProvision, err := r.runTemplateImportProvision(ctx, updateResponse.VariableValues, &sdkproto.Provision_Metadata{
|
||||
startProvision, err := r.runTemplateImportProvision(ctx, updateResponse.VariableValues, &sdkproto.Metadata{
|
||||
CoderUrl: r.job.GetTemplateImport().Metadata.CoderUrl,
|
||||
WorkspaceTransition: sdkproto.WorkspaceTransition_START,
|
||||
})
|
||||
|
@ -661,7 +556,7 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p
|
|||
Stage: "Detecting ephemeral resources",
|
||||
CreatedAt: time.Now().UnixMilli(),
|
||||
})
|
||||
stopProvision, err := r.runTemplateImportProvision(ctx, updateResponse.VariableValues, &sdkproto.Provision_Metadata{
|
||||
stopProvision, err := r.runTemplateImportProvision(ctx, updateResponse.VariableValues, &sdkproto.Metadata{
|
||||
CoderUrl: r.job.GetTemplateImport().Metadata.CoderUrl,
|
||||
WorkspaceTransition: sdkproto.WorkspaceTransition_STOP,
|
||||
})
|
||||
|
@ -682,25 +577,24 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p
|
|||
}, nil
|
||||
}
|
||||
|
||||
// Parses template variables and parameter schemas from source.
|
||||
func (r *Runner) runTemplateImportParse(ctx context.Context) ([]*sdkproto.TemplateVariable, error) {
|
||||
// Parses template variables and README from source.
|
||||
func (r *Runner) runTemplateImportParse(ctx context.Context) (
|
||||
vars []*sdkproto.TemplateVariable, readme []byte, err error,
|
||||
) {
|
||||
ctx, span := r.startTrace(ctx, tracing.FuncName())
|
||||
defer span.End()
|
||||
|
||||
stream, err := r.provisioner.Parse(ctx, &sdkproto.Parse_Request{
|
||||
Directory: r.workDirectory,
|
||||
})
|
||||
err = r.session.Send(&sdkproto.Request{Type: &sdkproto.Request_Parse{Parse: &sdkproto.ParseRequest{}}})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("parse source: %w", err)
|
||||
return nil, nil, xerrors.Errorf("parse source: %w", err)
|
||||
}
|
||||
defer stream.Close()
|
||||
for {
|
||||
msg, err := stream.Recv()
|
||||
msg, err := r.session.Recv()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("recv parse source: %w", err)
|
||||
return nil, nil, xerrors.Errorf("recv parse source: %w", err)
|
||||
}
|
||||
switch msgType := msg.Type.(type) {
|
||||
case *sdkproto.Parse_Response_Log:
|
||||
case *sdkproto.Response_Log:
|
||||
r.logger.Debug(context.Background(), "parse job logged",
|
||||
slog.F("level", msgType.Log.Level),
|
||||
slog.F("output", msgType.Log.Output),
|
||||
|
@ -713,14 +607,20 @@ func (r *Runner) runTemplateImportParse(ctx context.Context) ([]*sdkproto.Templa
|
|||
Output: msgType.Log.Output,
|
||||
Stage: "Parse parameters",
|
||||
})
|
||||
case *sdkproto.Parse_Response_Complete:
|
||||
case *sdkproto.Response_Parse:
|
||||
pc := msgType.Parse
|
||||
r.logger.Debug(context.Background(), "parse complete",
|
||||
slog.F("template_variables", msgType.Complete.TemplateVariables),
|
||||
slog.F("template_variables", pc.TemplateVariables),
|
||||
slog.F("readme_len", len(pc.Readme)),
|
||||
slog.F("error", pc.Error),
|
||||
)
|
||||
if pc.Error != "" {
|
||||
return nil, nil, xerrors.Errorf("parse error: %s", pc.Error)
|
||||
}
|
||||
|
||||
return msgType.Complete.TemplateVariables, nil
|
||||
return msgType.Parse.TemplateVariables, msgType.Parse.Readme, nil
|
||||
default:
|
||||
return nil, xerrors.Errorf("invalid message type %q received from provisioner",
|
||||
return nil, nil, xerrors.Errorf("invalid message type %q received from provisioner",
|
||||
reflect.TypeOf(msg.Type).String())
|
||||
}
|
||||
}
|
||||
|
@ -735,13 +635,18 @@ type templateImportProvision struct {
|
|||
// Performs a dry-run provision when importing a template.
|
||||
// This is used to detect resources that would be provisioned for a workspace in various states.
|
||||
// It doesn't define values for rich parameters as they're unknown during template import.
|
||||
func (r *Runner) runTemplateImportProvision(ctx context.Context, variableValues []*sdkproto.VariableValue, metadata *sdkproto.Provision_Metadata) (*templateImportProvision, error) {
|
||||
func (r *Runner) runTemplateImportProvision(ctx context.Context, variableValues []*sdkproto.VariableValue, metadata *sdkproto.Metadata) (*templateImportProvision, error) {
|
||||
return r.runTemplateImportProvisionWithRichParameters(ctx, variableValues, nil, metadata)
|
||||
}
|
||||
|
||||
// Performs a dry-run provision with provided rich parameters.
|
||||
// This is used to detect resources that would be provisioned for a workspace in various states.
|
||||
func (r *Runner) runTemplateImportProvisionWithRichParameters(ctx context.Context, variableValues []*sdkproto.VariableValue, richParameterValues []*sdkproto.RichParameterValue, metadata *sdkproto.Provision_Metadata) (*templateImportProvision, error) {
|
||||
func (r *Runner) runTemplateImportProvisionWithRichParameters(
|
||||
ctx context.Context,
|
||||
variableValues []*sdkproto.VariableValue,
|
||||
richParameterValues []*sdkproto.RichParameterValue,
|
||||
metadata *sdkproto.Metadata,
|
||||
) (*templateImportProvision, error) {
|
||||
ctx, span := r.startTrace(ctx, tracing.FuncName())
|
||||
defer span.End()
|
||||
|
||||
|
@ -754,46 +659,38 @@ func (r *Runner) runTemplateImportProvisionWithRichParameters(ctx context.Contex
|
|||
}
|
||||
// use the notStopped so that if we attempt to gracefully cancel, the stream will still be available for us
|
||||
// to send the cancel to the provisioner
|
||||
stream, err := r.provisioner.Provision(ctx)
|
||||
err := r.session.Send(&sdkproto.Request{Type: &sdkproto.Request_Plan{Plan: &sdkproto.PlanRequest{
|
||||
Metadata: metadata,
|
||||
RichParameterValues: richParameterValues,
|
||||
VariableValues: variableValues,
|
||||
}}})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("provision: %w", err)
|
||||
return nil, xerrors.Errorf("start provision: %w", err)
|
||||
}
|
||||
defer stream.Close()
|
||||
nevermind := make(chan struct{})
|
||||
defer close(nevermind)
|
||||
go func() {
|
||||
select {
|
||||
case <-nevermind:
|
||||
return
|
||||
case <-r.notStopped.Done():
|
||||
return
|
||||
case <-r.notCanceled.Done():
|
||||
_ = stream.Send(&sdkproto.Provision_Request{
|
||||
Type: &sdkproto.Provision_Request_Cancel{
|
||||
Cancel: &sdkproto.Provision_Cancel{},
|
||||
_ = r.session.Send(&sdkproto.Request{
|
||||
Type: &sdkproto.Request_Cancel{
|
||||
Cancel: &sdkproto.CancelRequest{},
|
||||
},
|
||||
})
|
||||
}
|
||||
}()
|
||||
err = stream.Send(&sdkproto.Provision_Request{
|
||||
Type: &sdkproto.Provision_Request_Plan{
|
||||
Plan: &sdkproto.Provision_Plan{
|
||||
Config: &sdkproto.Provision_Config{
|
||||
Directory: r.workDirectory,
|
||||
Metadata: metadata,
|
||||
},
|
||||
RichParameterValues: richParameterValues,
|
||||
VariableValues: variableValues,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("start provision: %w", err)
|
||||
}
|
||||
|
||||
for {
|
||||
msg, err := stream.Recv()
|
||||
msg, err := r.session.Recv()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("recv import provision: %w", err)
|
||||
}
|
||||
switch msgType := msg.Type.(type) {
|
||||
case *sdkproto.Provision_Response_Log:
|
||||
case *sdkproto.Response_Log:
|
||||
r.logger.Debug(context.Background(), "template import provision job logged",
|
||||
slog.F("level", msgType.Log.Level),
|
||||
slog.F("output", msgType.Log.Output),
|
||||
|
@ -805,25 +702,25 @@ func (r *Runner) runTemplateImportProvisionWithRichParameters(ctx context.Contex
|
|||
Output: msgType.Log.Output,
|
||||
Stage: stage,
|
||||
})
|
||||
case *sdkproto.Provision_Response_Complete:
|
||||
if msgType.Complete.Error != "" {
|
||||
case *sdkproto.Response_Plan:
|
||||
c := msgType.Plan
|
||||
if c.Error != "" {
|
||||
r.logger.Info(context.Background(), "dry-run provision failure",
|
||||
slog.F("error", msgType.Complete.Error),
|
||||
slog.F("error", c.Error),
|
||||
)
|
||||
|
||||
return nil, xerrors.New(msgType.Complete.Error)
|
||||
return nil, xerrors.New(c.Error)
|
||||
}
|
||||
|
||||
r.logger.Info(context.Background(), "parse dry-run provision successful",
|
||||
slog.F("resource_count", len(msgType.Complete.Resources)),
|
||||
slog.F("resources", msgType.Complete.Resources),
|
||||
slog.F("state_length", len(msgType.Complete.State)),
|
||||
slog.F("resource_count", len(c.Resources)),
|
||||
slog.F("resources", c.Resources),
|
||||
)
|
||||
|
||||
return &templateImportProvision{
|
||||
Resources: msgType.Complete.Resources,
|
||||
Parameters: msgType.Complete.Parameters,
|
||||
GitAuthProviders: msgType.Complete.GitAuthProviders,
|
||||
Resources: c.Resources,
|
||||
Parameters: c.Parameters,
|
||||
GitAuthProviders: c.GitAuthProviders,
|
||||
}, nil
|
||||
default:
|
||||
return nil, xerrors.Errorf("invalid message type %q received from provisioner",
|
||||
|
@ -864,6 +761,13 @@ func (r *Runner) runTemplateDryRun(ctx context.Context) (*proto.CompletedJob, *p
|
|||
metadata.WorkspaceOwnerId = id.String()
|
||||
}
|
||||
|
||||
failedJob := r.configure(&sdkproto.Config{
|
||||
TemplateSourceArchive: r.job.GetTemplateSourceArchive(),
|
||||
})
|
||||
if failedJob != nil {
|
||||
return nil, failedJob
|
||||
}
|
||||
|
||||
// Run the template import provision task since it's already a dry run.
|
||||
provision, err := r.runTemplateImportProvisionWithRichParameters(ctx,
|
||||
r.job.GetTemplateDryRun().GetVariableValues(),
|
||||
|
@ -884,41 +788,39 @@ func (r *Runner) runTemplateDryRun(ctx context.Context) (*proto.CompletedJob, *p
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (r *Runner) buildWorkspace(ctx context.Context, stage string, req *sdkproto.Provision_Request) (
|
||||
*sdkproto.Provision_Complete, *proto.FailedJob,
|
||||
func (r *Runner) buildWorkspace(ctx context.Context, stage string, req *sdkproto.Request) (
|
||||
*sdkproto.Response, *proto.FailedJob,
|
||||
) {
|
||||
// use the notStopped so that if we attempt to gracefully cancel, the stream
|
||||
// will still be available for us to send the cancel to the provisioner
|
||||
stream, err := r.provisioner.Provision(ctx)
|
||||
err := r.session.Send(req)
|
||||
if err != nil {
|
||||
return nil, r.failedWorkspaceBuildf("provision: %s", err)
|
||||
return nil, r.failedWorkspaceBuildf("start provision: %s", err)
|
||||
}
|
||||
defer stream.Close()
|
||||
nevermind := make(chan struct{})
|
||||
defer close(nevermind)
|
||||
go func() {
|
||||
select {
|
||||
case <-nevermind:
|
||||
return
|
||||
case <-r.notStopped.Done():
|
||||
return
|
||||
case <-r.notCanceled.Done():
|
||||
_ = stream.Send(&sdkproto.Provision_Request{
|
||||
Type: &sdkproto.Provision_Request_Cancel{
|
||||
Cancel: &sdkproto.Provision_Cancel{},
|
||||
_ = r.session.Send(&sdkproto.Request{
|
||||
Type: &sdkproto.Request_Cancel{
|
||||
Cancel: &sdkproto.CancelRequest{},
|
||||
},
|
||||
})
|
||||
}
|
||||
}()
|
||||
|
||||
err = stream.Send(req)
|
||||
if err != nil {
|
||||
return nil, r.failedWorkspaceBuildf("start provision: %s", err)
|
||||
}
|
||||
|
||||
for {
|
||||
msg, err := stream.Recv()
|
||||
msg, err := r.session.Recv()
|
||||
if err != nil {
|
||||
return nil, r.failedWorkspaceBuildf("recv workspace provision: %s", err)
|
||||
}
|
||||
switch msgType := msg.Type.(type) {
|
||||
case *sdkproto.Provision_Response_Log:
|
||||
case *sdkproto.Response_Log:
|
||||
r.logProvisionerJobLog(context.Background(), msgType.Log.Level, "workspace provisioner job logged",
|
||||
slog.F("level", msgType.Log.Level),
|
||||
slog.F("output", msgType.Log.Output),
|
||||
|
@ -932,33 +834,9 @@ func (r *Runner) buildWorkspace(ctx context.Context, stage string, req *sdkproto
|
|||
Output: msgType.Log.Output,
|
||||
Stage: stage,
|
||||
})
|
||||
case *sdkproto.Provision_Response_Complete:
|
||||
if msgType.Complete.Error != "" {
|
||||
r.logger.Warn(context.Background(), "provision failed; updating state",
|
||||
slog.F("state_length", len(msgType.Complete.State)),
|
||||
slog.F("error", msgType.Complete.Error),
|
||||
)
|
||||
|
||||
return nil, &proto.FailedJob{
|
||||
JobId: r.job.JobId,
|
||||
Error: msgType.Complete.Error,
|
||||
Type: &proto.FailedJob_WorkspaceBuild_{
|
||||
WorkspaceBuild: &proto.FailedJob_WorkspaceBuild{
|
||||
State: msgType.Complete.State,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
r.logger.Info(context.Background(), "provision successful",
|
||||
slog.F("resource_count", len(msgType.Complete.Resources)),
|
||||
slog.F("resources", msgType.Complete.Resources),
|
||||
slog.F("state_length", len(msgType.Complete.State)),
|
||||
)
|
||||
// Stop looping!
|
||||
return msgType.Complete, nil
|
||||
default:
|
||||
return nil, r.failedWorkspaceBuildf("invalid message type %T received from provisioner", msg.Type)
|
||||
// Stop looping!
|
||||
return msg, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1035,18 +913,19 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p
|
|||
applyStage = "Destroying workspace"
|
||||
}
|
||||
|
||||
config := &sdkproto.Provision_Config{
|
||||
Directory: r.workDirectory,
|
||||
Metadata: r.job.GetWorkspaceBuild().Metadata,
|
||||
State: r.job.GetWorkspaceBuild().State,
|
||||
|
||||
ProvisionerLogLevel: r.job.GetWorkspaceBuild().LogLevel,
|
||||
failedJob := r.configure(&sdkproto.Config{
|
||||
TemplateSourceArchive: r.job.GetTemplateSourceArchive(),
|
||||
State: r.job.GetWorkspaceBuild().State,
|
||||
ProvisionerLogLevel: r.job.GetWorkspaceBuild().LogLevel,
|
||||
})
|
||||
if failedJob != nil {
|
||||
return nil, failedJob
|
||||
}
|
||||
|
||||
completedPlan, failed := r.buildWorkspace(ctx, "Planning infrastructure", &sdkproto.Provision_Request{
|
||||
Type: &sdkproto.Provision_Request_Plan{
|
||||
Plan: &sdkproto.Provision_Plan{
|
||||
Config: config,
|
||||
resp, failed := r.buildWorkspace(ctx, "Planning infrastructure", &sdkproto.Request{
|
||||
Type: &sdkproto.Request_Plan{
|
||||
Plan: &sdkproto.PlanRequest{
|
||||
Metadata: r.job.GetWorkspaceBuild().Metadata,
|
||||
RichParameterValues: r.job.GetWorkspaceBuild().RichParameterValues,
|
||||
VariableValues: r.job.GetWorkspaceBuild().VariableValues,
|
||||
GitAuthProviders: r.job.GetWorkspaceBuild().GitAuthProviders,
|
||||
|
@ -1056,9 +935,31 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p
|
|||
if failed != nil {
|
||||
return nil, failed
|
||||
}
|
||||
planComplete := resp.GetPlan()
|
||||
if planComplete == nil {
|
||||
return nil, r.failedWorkspaceBuildf("invalid message type %T received from provisioner", resp.Type)
|
||||
}
|
||||
if planComplete.Error != "" {
|
||||
r.logger.Warn(context.Background(), "plan request failed",
|
||||
slog.F("error", planComplete.Error),
|
||||
)
|
||||
|
||||
return nil, &proto.FailedJob{
|
||||
JobId: r.job.JobId,
|
||||
Error: planComplete.Error,
|
||||
Type: &proto.FailedJob_WorkspaceBuild_{
|
||||
WorkspaceBuild: &proto.FailedJob_WorkspaceBuild{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
r.logger.Info(context.Background(), "plan request successful",
|
||||
slog.F("resource_count", len(planComplete.Resources)),
|
||||
slog.F("resources", planComplete.Resources),
|
||||
)
|
||||
r.flushQueuedLogs(ctx)
|
||||
if commitQuota {
|
||||
failed = r.commitQuota(ctx, completedPlan.GetResources())
|
||||
failed = r.commitQuota(ctx, planComplete.Resources)
|
||||
r.flushQueuedLogs(ctx)
|
||||
if failed != nil {
|
||||
return nil, failed
|
||||
|
@ -1072,25 +973,50 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p
|
|||
CreatedAt: time.Now().UnixMilli(),
|
||||
})
|
||||
|
||||
completedApply, failed := r.buildWorkspace(ctx, applyStage, &sdkproto.Provision_Request{
|
||||
Type: &sdkproto.Provision_Request_Apply{
|
||||
Apply: &sdkproto.Provision_Apply{
|
||||
Config: config,
|
||||
Plan: completedPlan.GetPlan(),
|
||||
resp, failed = r.buildWorkspace(ctx, applyStage, &sdkproto.Request{
|
||||
Type: &sdkproto.Request_Apply{
|
||||
Apply: &sdkproto.ApplyRequest{
|
||||
Metadata: r.job.GetWorkspaceBuild().Metadata,
|
||||
},
|
||||
},
|
||||
})
|
||||
if failed != nil {
|
||||
return nil, failed
|
||||
}
|
||||
applyComplete := resp.GetApply()
|
||||
if applyComplete == nil {
|
||||
return nil, r.failedWorkspaceBuildf("invalid message type %T received from provisioner", resp.Type)
|
||||
}
|
||||
if applyComplete.Error != "" {
|
||||
r.logger.Warn(context.Background(), "apply failed; updating state",
|
||||
slog.F("error", applyComplete.Error),
|
||||
slog.F("state_len", len(applyComplete.State)),
|
||||
)
|
||||
|
||||
return nil, &proto.FailedJob{
|
||||
JobId: r.job.JobId,
|
||||
Error: applyComplete.Error,
|
||||
Type: &proto.FailedJob_WorkspaceBuild_{
|
||||
WorkspaceBuild: &proto.FailedJob_WorkspaceBuild{
|
||||
State: applyComplete.State,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
r.logger.Info(context.Background(), "apply successful",
|
||||
slog.F("resource_count", len(applyComplete.Resources)),
|
||||
slog.F("resources", applyComplete.Resources),
|
||||
slog.F("state_len", len(applyComplete.State)),
|
||||
)
|
||||
r.flushQueuedLogs(ctx)
|
||||
|
||||
return &proto.CompletedJob{
|
||||
JobId: r.job.JobId,
|
||||
Type: &proto.CompletedJob_WorkspaceBuild_{
|
||||
WorkspaceBuild: &proto.CompletedJob_WorkspaceBuild{
|
||||
State: completedApply.GetState(),
|
||||
Resources: completedApply.GetResources(),
|
||||
State: applyComplete.State,
|
||||
Resources: applyComplete.Resources,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package provisionersdk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/coder/coder/v2/provisionersdk/proto"
|
||||
)
|
||||
|
||||
func ParseErrorf(format string, args ...any) *proto.ParseComplete {
|
||||
return &proto.ParseComplete{Error: fmt.Sprintf(format, args...)}
|
||||
}
|
||||
|
||||
func PlanErrorf(format string, args ...any) *proto.PlanComplete {
|
||||
return &proto.PlanComplete{Error: fmt.Sprintf(format, args...)}
|
||||
}
|
||||
|
||||
func ApplyErrorf(format string, args ...any) *proto.ApplyComplete {
|
||||
return &proto.ApplyComplete{Error: fmt.Sprintf(format, args...)}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -82,8 +82,8 @@ message InstanceIdentityAuth {
|
|||
}
|
||||
|
||||
message GitAuthProvider {
|
||||
string id = 1;
|
||||
string access_token = 2;
|
||||
string id = 1;
|
||||
string access_token = 2;
|
||||
}
|
||||
|
||||
// Agent represents a running agent on the workspace.
|
||||
|
@ -110,15 +110,15 @@ message Agent {
|
|||
string token = 9;
|
||||
string instance_id = 10;
|
||||
}
|
||||
int32 connection_timeout_seconds = 11;
|
||||
string troubleshooting_url = 12;
|
||||
string motd_file = 13;
|
||||
// Field 14 was bool login_before_ready = 14, now removed.
|
||||
int32 startup_script_timeout_seconds = 15;
|
||||
string shutdown_script = 16;
|
||||
int32 shutdown_script_timeout_seconds = 17;
|
||||
int32 connection_timeout_seconds = 11;
|
||||
string troubleshooting_url = 12;
|
||||
string motd_file = 13;
|
||||
// Field 14 was bool login_before_ready = 14, now removed.
|
||||
int32 startup_script_timeout_seconds = 15;
|
||||
string shutdown_script = 16;
|
||||
int32 shutdown_script_timeout_seconds = 17;
|
||||
repeated Metadata metadata = 18;
|
||||
string startup_script_behavior = 19;
|
||||
string startup_script_behavior = 19;
|
||||
}
|
||||
|
||||
enum AppSharingLevel {
|
||||
|
@ -168,96 +168,111 @@ message Resource {
|
|||
int32 daily_cost = 8;
|
||||
}
|
||||
|
||||
// Parse consumes source-code from a directory to produce inputs.
|
||||
message Parse {
|
||||
message Request {
|
||||
string directory = 1;
|
||||
}
|
||||
message Complete {
|
||||
reserved 2;
|
||||
|
||||
repeated TemplateVariable template_variables = 1;
|
||||
}
|
||||
message Response {
|
||||
oneof type {
|
||||
Log log = 1;
|
||||
Complete complete = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WorkspaceTransition is the desired outcome of a build
|
||||
enum WorkspaceTransition {
|
||||
START = 0;
|
||||
STOP = 1;
|
||||
DESTROY = 2;
|
||||
}
|
||||
|
||||
// Provision consumes source-code from a directory to produce resources.
|
||||
// Exactly one of Plan or Apply must be provided in a single session.
|
||||
message Provision {
|
||||
message Metadata {
|
||||
string coder_url = 1;
|
||||
WorkspaceTransition workspace_transition = 2;
|
||||
string workspace_name = 3;
|
||||
string workspace_owner = 4;
|
||||
string workspace_id = 5;
|
||||
string workspace_owner_id = 6;
|
||||
string workspace_owner_email = 7;
|
||||
string template_name = 8;
|
||||
string template_version = 9;
|
||||
string workspace_owner_oidc_access_token = 10;
|
||||
string workspace_owner_session_token = 11;
|
||||
}
|
||||
// Metadata is information about a workspace used in the execution of a build
|
||||
message Metadata {
|
||||
string coder_url = 1;
|
||||
WorkspaceTransition workspace_transition = 2;
|
||||
string workspace_name = 3;
|
||||
string workspace_owner = 4;
|
||||
string workspace_id = 5;
|
||||
string workspace_owner_id = 6;
|
||||
string workspace_owner_email = 7;
|
||||
string template_name = 8;
|
||||
string template_version = 9;
|
||||
string workspace_owner_oidc_access_token = 10;
|
||||
string workspace_owner_session_token = 11;
|
||||
}
|
||||
|
||||
// Config represents execution configuration shared by both Plan and
|
||||
// Apply commands.
|
||||
message Config {
|
||||
string directory = 1;
|
||||
bytes state = 2;
|
||||
Metadata metadata = 3;
|
||||
// Config represents execution configuration shared by all subsequent requests in the Session
|
||||
message Config {
|
||||
// template_source_archive is a tar of the template source files
|
||||
bytes template_source_archive = 1;
|
||||
// state is the provisioner state (if any)
|
||||
bytes state = 2;
|
||||
string provisioner_log_level = 3;
|
||||
}
|
||||
|
||||
string provisioner_log_level = 4;
|
||||
}
|
||||
// ParseRequest consumes source-code to produce inputs.
|
||||
message ParseRequest {
|
||||
}
|
||||
|
||||
message Plan {
|
||||
reserved 2;
|
||||
// ParseComplete indicates a request to parse completed.
|
||||
message ParseComplete {
|
||||
string error = 1;
|
||||
repeated TemplateVariable template_variables = 2;
|
||||
bytes readme = 3;
|
||||
}
|
||||
|
||||
// PlanRequest asks the provisioner to plan what resources & parameters it will create
|
||||
message PlanRequest {
|
||||
Metadata metadata = 1;
|
||||
repeated RichParameterValue rich_parameter_values = 2;
|
||||
repeated VariableValue variable_values = 3;
|
||||
repeated GitAuthProvider git_auth_providers = 4;
|
||||
}
|
||||
|
||||
// PlanComplete indicates a request to plan completed.
|
||||
message PlanComplete {
|
||||
string error = 1;
|
||||
repeated Resource resources = 2;
|
||||
repeated RichParameter parameters = 3;
|
||||
repeated string git_auth_providers = 4;
|
||||
}
|
||||
|
||||
// ApplyRequest asks the provisioner to apply the changes. Apply MUST be preceded by a successful plan request/response
|
||||
// in the same Session. The plan data is not transmitted over the wire and is cached by the provisioner in the Session.
|
||||
message ApplyRequest {
|
||||
Metadata metadata = 1;
|
||||
}
|
||||
|
||||
// ApplyComplete indicates a request to apply completed.
|
||||
message ApplyComplete {
|
||||
bytes state = 1;
|
||||
string error = 2;
|
||||
repeated Resource resources = 3;
|
||||
repeated RichParameter parameters = 4;
|
||||
repeated string git_auth_providers = 5;
|
||||
}
|
||||
|
||||
// CancelRequest requests that the previous request be canceled gracefully.
|
||||
message CancelRequest {}
|
||||
|
||||
message Request {
|
||||
oneof type {
|
||||
Config config = 1;
|
||||
repeated RichParameterValue rich_parameter_values = 3;
|
||||
repeated VariableValue variable_values = 4;
|
||||
repeated GitAuthProvider git_auth_providers = 5;
|
||||
ParseRequest parse = 2;
|
||||
PlanRequest plan = 3;
|
||||
ApplyRequest apply = 4;
|
||||
CancelRequest cancel = 5;
|
||||
}
|
||||
}
|
||||
|
||||
message Apply {
|
||||
Config config = 1;
|
||||
bytes plan = 2;
|
||||
}
|
||||
|
||||
message Cancel {}
|
||||
message Request {
|
||||
oneof type {
|
||||
Plan plan = 1;
|
||||
Apply apply = 2;
|
||||
Cancel cancel = 3;
|
||||
}
|
||||
}
|
||||
message Complete {
|
||||
bytes state = 1;
|
||||
string error = 2;
|
||||
repeated Resource resources = 3;
|
||||
repeated RichParameter parameters = 4;
|
||||
repeated string git_auth_providers = 5;
|
||||
bytes plan = 6;
|
||||
}
|
||||
message Response {
|
||||
oneof type {
|
||||
Log log = 1;
|
||||
Complete complete = 2;
|
||||
}
|
||||
message Response {
|
||||
oneof type {
|
||||
Log log = 1;
|
||||
ParseComplete parse = 2;
|
||||
PlanComplete plan = 3;
|
||||
ApplyComplete apply = 4;
|
||||
}
|
||||
}
|
||||
|
||||
service Provisioner {
|
||||
rpc Parse(Parse.Request) returns (stream Parse.Response);
|
||||
rpc Provision(stream Provision.Request) returns (stream Provision.Response);
|
||||
// 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.
|
||||
//
|
||||
// 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.
|
||||
rpc Session(stream Request) returns (stream Response);
|
||||
}
|
||||
|
|
|
@ -38,8 +38,7 @@ func (drpcEncoding_File_provisionersdk_proto_provisioner_proto) JSONUnmarshal(bu
|
|||
type DRPCProvisionerClient interface {
|
||||
DRPCConn() drpc.Conn
|
||||
|
||||
Parse(ctx context.Context, in *Parse_Request) (DRPCProvisioner_ParseClient, error)
|
||||
Provision(ctx context.Context) (DRPCProvisioner_ProvisionClient, error)
|
||||
Session(ctx context.Context) (DRPCProvisioner_SessionClient, error)
|
||||
}
|
||||
|
||||
type drpcProvisionerClient struct {
|
||||
|
@ -52,123 +51,69 @@ func NewDRPCProvisionerClient(cc drpc.Conn) DRPCProvisionerClient {
|
|||
|
||||
func (c *drpcProvisionerClient) DRPCConn() drpc.Conn { return c.cc }
|
||||
|
||||
func (c *drpcProvisionerClient) Parse(ctx context.Context, in *Parse_Request) (DRPCProvisioner_ParseClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, "/provisioner.Provisioner/Parse", drpcEncoding_File_provisionersdk_proto_provisioner_proto{})
|
||||
func (c *drpcProvisionerClient) Session(ctx context.Context) (DRPCProvisioner_SessionClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, "/provisioner.Provisioner/Session", drpcEncoding_File_provisionersdk_proto_provisioner_proto{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &drpcProvisioner_ParseClient{stream}
|
||||
if err := x.MsgSend(in, drpcEncoding_File_provisionersdk_proto_provisioner_proto{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &drpcProvisioner_SessionClient{stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type DRPCProvisioner_ParseClient interface {
|
||||
type DRPCProvisioner_SessionClient interface {
|
||||
drpc.Stream
|
||||
Recv() (*Parse_Response, error)
|
||||
Send(*Request) error
|
||||
Recv() (*Response, error)
|
||||
}
|
||||
|
||||
type drpcProvisioner_ParseClient struct {
|
||||
type drpcProvisioner_SessionClient struct {
|
||||
drpc.Stream
|
||||
}
|
||||
|
||||
func (x *drpcProvisioner_ParseClient) GetStream() drpc.Stream {
|
||||
func (x *drpcProvisioner_SessionClient) GetStream() drpc.Stream {
|
||||
return x.Stream
|
||||
}
|
||||
|
||||
func (x *drpcProvisioner_ParseClient) Recv() (*Parse_Response, error) {
|
||||
m := new(Parse_Response)
|
||||
if err := x.MsgRecv(m, drpcEncoding_File_provisionersdk_proto_provisioner_proto{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (x *drpcProvisioner_ParseClient) RecvMsg(m *Parse_Response) error {
|
||||
return x.MsgRecv(m, drpcEncoding_File_provisionersdk_proto_provisioner_proto{})
|
||||
}
|
||||
|
||||
func (c *drpcProvisionerClient) Provision(ctx context.Context) (DRPCProvisioner_ProvisionClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, "/provisioner.Provisioner/Provision", drpcEncoding_File_provisionersdk_proto_provisioner_proto{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &drpcProvisioner_ProvisionClient{stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type DRPCProvisioner_ProvisionClient interface {
|
||||
drpc.Stream
|
||||
Send(*Provision_Request) error
|
||||
Recv() (*Provision_Response, error)
|
||||
}
|
||||
|
||||
type drpcProvisioner_ProvisionClient struct {
|
||||
drpc.Stream
|
||||
}
|
||||
|
||||
func (x *drpcProvisioner_ProvisionClient) GetStream() drpc.Stream {
|
||||
return x.Stream
|
||||
}
|
||||
|
||||
func (x *drpcProvisioner_ProvisionClient) Send(m *Provision_Request) error {
|
||||
func (x *drpcProvisioner_SessionClient) Send(m *Request) error {
|
||||
return x.MsgSend(m, drpcEncoding_File_provisionersdk_proto_provisioner_proto{})
|
||||
}
|
||||
|
||||
func (x *drpcProvisioner_ProvisionClient) Recv() (*Provision_Response, error) {
|
||||
m := new(Provision_Response)
|
||||
func (x *drpcProvisioner_SessionClient) Recv() (*Response, error) {
|
||||
m := new(Response)
|
||||
if err := x.MsgRecv(m, drpcEncoding_File_provisionersdk_proto_provisioner_proto{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (x *drpcProvisioner_ProvisionClient) RecvMsg(m *Provision_Response) error {
|
||||
func (x *drpcProvisioner_SessionClient) RecvMsg(m *Response) error {
|
||||
return x.MsgRecv(m, drpcEncoding_File_provisionersdk_proto_provisioner_proto{})
|
||||
}
|
||||
|
||||
type DRPCProvisionerServer interface {
|
||||
Parse(*Parse_Request, DRPCProvisioner_ParseStream) error
|
||||
Provision(DRPCProvisioner_ProvisionStream) error
|
||||
Session(DRPCProvisioner_SessionStream) error
|
||||
}
|
||||
|
||||
type DRPCProvisionerUnimplementedServer struct{}
|
||||
|
||||
func (s *DRPCProvisionerUnimplementedServer) Parse(*Parse_Request, DRPCProvisioner_ParseStream) error {
|
||||
return drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
|
||||
}
|
||||
|
||||
func (s *DRPCProvisionerUnimplementedServer) Provision(DRPCProvisioner_ProvisionStream) error {
|
||||
func (s *DRPCProvisionerUnimplementedServer) Session(DRPCProvisioner_SessionStream) error {
|
||||
return drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
|
||||
}
|
||||
|
||||
type DRPCProvisionerDescription struct{}
|
||||
|
||||
func (DRPCProvisionerDescription) NumMethods() int { return 2 }
|
||||
func (DRPCProvisionerDescription) NumMethods() int { return 1 }
|
||||
|
||||
func (DRPCProvisionerDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver, interface{}, bool) {
|
||||
switch n {
|
||||
case 0:
|
||||
return "/provisioner.Provisioner/Parse", drpcEncoding_File_provisionersdk_proto_provisioner_proto{},
|
||||
return "/provisioner.Provisioner/Session", drpcEncoding_File_provisionersdk_proto_provisioner_proto{},
|
||||
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
||||
return nil, srv.(DRPCProvisionerServer).
|
||||
Parse(
|
||||
in1.(*Parse_Request),
|
||||
&drpcProvisioner_ParseStream{in2.(drpc.Stream)},
|
||||
Session(
|
||||
&drpcProvisioner_SessionStream{in1.(drpc.Stream)},
|
||||
)
|
||||
}, DRPCProvisionerServer.Parse, true
|
||||
case 1:
|
||||
return "/provisioner.Provisioner/Provision", drpcEncoding_File_provisionersdk_proto_provisioner_proto{},
|
||||
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
||||
return nil, srv.(DRPCProvisionerServer).
|
||||
Provision(
|
||||
&drpcProvisioner_ProvisionStream{in1.(drpc.Stream)},
|
||||
)
|
||||
}, DRPCProvisionerServer.Provision, true
|
||||
}, DRPCProvisionerServer.Session, true
|
||||
default:
|
||||
return "", nil, nil, nil, false
|
||||
}
|
||||
|
@ -178,41 +123,28 @@ func DRPCRegisterProvisioner(mux drpc.Mux, impl DRPCProvisionerServer) error {
|
|||
return mux.Register(impl, DRPCProvisionerDescription{})
|
||||
}
|
||||
|
||||
type DRPCProvisioner_ParseStream interface {
|
||||
type DRPCProvisioner_SessionStream interface {
|
||||
drpc.Stream
|
||||
Send(*Parse_Response) error
|
||||
Send(*Response) error
|
||||
Recv() (*Request, error)
|
||||
}
|
||||
|
||||
type drpcProvisioner_ParseStream struct {
|
||||
type drpcProvisioner_SessionStream struct {
|
||||
drpc.Stream
|
||||
}
|
||||
|
||||
func (x *drpcProvisioner_ParseStream) Send(m *Parse_Response) error {
|
||||
func (x *drpcProvisioner_SessionStream) Send(m *Response) error {
|
||||
return x.MsgSend(m, drpcEncoding_File_provisionersdk_proto_provisioner_proto{})
|
||||
}
|
||||
|
||||
type DRPCProvisioner_ProvisionStream interface {
|
||||
drpc.Stream
|
||||
Send(*Provision_Response) error
|
||||
Recv() (*Provision_Request, error)
|
||||
}
|
||||
|
||||
type drpcProvisioner_ProvisionStream struct {
|
||||
drpc.Stream
|
||||
}
|
||||
|
||||
func (x *drpcProvisioner_ProvisionStream) Send(m *Provision_Response) error {
|
||||
return x.MsgSend(m, drpcEncoding_File_provisionersdk_proto_provisioner_proto{})
|
||||
}
|
||||
|
||||
func (x *drpcProvisioner_ProvisionStream) Recv() (*Provision_Request, error) {
|
||||
m := new(Provision_Request)
|
||||
func (x *drpcProvisioner_SessionStream) Recv() (*Request, error) {
|
||||
m := new(Request)
|
||||
if err := x.MsgRecv(m, drpcEncoding_File_provisionersdk_proto_provisioner_proto{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (x *drpcProvisioner_ProvisionStream) RecvMsg(m *Provision_Request) error {
|
||||
func (x *drpcProvisioner_SessionStream) RecvMsg(m *Request) error {
|
||||
return x.MsgRecv(m, drpcEncoding_File_provisionersdk_proto_provisioner_proto{})
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ import (
|
|||
"storj.io/drpc/drpcmux"
|
||||
"storj.io/drpc/drpcserver"
|
||||
|
||||
"cdr.dev/slog"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/tracing"
|
||||
"github.com/coder/coder/v2/provisionersdk/proto"
|
||||
)
|
||||
|
@ -20,11 +22,19 @@ import (
|
|||
// ServeOptions are configurations to serve a provisioner.
|
||||
type ServeOptions struct {
|
||||
// Conn specifies a custom transport to serve the dRPC connection.
|
||||
Listener net.Listener
|
||||
Listener net.Listener
|
||||
Logger slog.Logger
|
||||
WorkDirectory string
|
||||
}
|
||||
|
||||
type Server interface {
|
||||
Parse(s *Session, r *proto.ParseRequest, canceledOrComplete <-chan struct{}) *proto.ParseComplete
|
||||
Plan(s *Session, r *proto.PlanRequest, canceledOrComplete <-chan struct{}) *proto.PlanComplete
|
||||
Apply(s *Session, r *proto.ApplyRequest, canceledOrComplete <-chan struct{}) *proto.ApplyComplete
|
||||
}
|
||||
|
||||
// Serve starts a dRPC connection for the provisioner and transport provided.
|
||||
func Serve(ctx context.Context, server proto.DRPCProvisionerServer, options *ServeOptions) error {
|
||||
func Serve(ctx context.Context, server Server, options *ServeOptions) error {
|
||||
if options == nil {
|
||||
options = &ServeOptions{}
|
||||
}
|
||||
|
@ -45,11 +55,22 @@ func Serve(ctx context.Context, server proto.DRPCProvisionerServer, options *Ser
|
|||
}()
|
||||
options.Listener = stdio
|
||||
}
|
||||
if options.WorkDirectory == "" {
|
||||
var err error
|
||||
options.WorkDirectory, err = os.MkdirTemp("", "coderprovisioner")
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to init temp work dir: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// dRPC is a drop-in replacement for gRPC with less generated code, and faster transports.
|
||||
// See: https://www.storj.io/blog/introducing-drpc-our-replacement-for-grpc
|
||||
mux := drpcmux.New()
|
||||
err := proto.DRPCRegisterProvisioner(mux, server)
|
||||
ps := &protoServer{
|
||||
server: server,
|
||||
opts: *options,
|
||||
}
|
||||
err := proto.DRPCRegisterProvisioner(mux, ps)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("register provisioner: %w", err)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/goleak"
|
||||
"storj.io/drpc/drpcerr"
|
||||
|
||||
"github.com/coder/coder/v2/provisionersdk"
|
||||
"github.com/coder/coder/v2/provisionersdk/proto"
|
||||
|
@ -28,17 +27,37 @@ func TestProvisionerSDK(t *testing.T) {
|
|||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
go func() {
|
||||
err := provisionersdk.Serve(ctx, &proto.DRPCProvisionerUnimplementedServer{}, &provisionersdk.ServeOptions{
|
||||
Listener: server,
|
||||
err := provisionersdk.Serve(ctx, unimplementedServer{}, &provisionersdk.ServeOptions{
|
||||
Listener: server,
|
||||
WorkDirectory: t.TempDir(),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
}()
|
||||
|
||||
api := proto.NewDRPCProvisionerClient(client)
|
||||
stream, err := api.Parse(context.Background(), &proto.Parse_Request{})
|
||||
s, err := api.Session(ctx)
|
||||
require.NoError(t, err)
|
||||
_, err = stream.Recv()
|
||||
require.Equal(t, drpcerr.Unimplemented, int(drpcerr.Code(err)))
|
||||
err = s.Send(&proto.Request{Type: &proto.Request_Config{Config: &proto.Config{}}})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = s.Send(&proto.Request{Type: &proto.Request_Parse{Parse: &proto.ParseRequest{}}})
|
||||
require.NoError(t, err)
|
||||
msg, err := s.Recv()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "unimplemented", msg.GetParse().GetError())
|
||||
|
||||
err = s.Send(&proto.Request{Type: &proto.Request_Plan{Plan: &proto.PlanRequest{}}})
|
||||
require.NoError(t, err)
|
||||
msg, err = s.Recv()
|
||||
require.NoError(t, err)
|
||||
// Plan has no error so that we're allowed to run Apply
|
||||
require.Equal(t, "", msg.GetPlan().GetError())
|
||||
|
||||
err = s.Send(&proto.Request{Type: &proto.Request_Apply{Apply: &proto.ApplyRequest{}}})
|
||||
require.NoError(t, err)
|
||||
msg, err = s.Recv()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "unimplemented", msg.GetApply().GetError())
|
||||
})
|
||||
|
||||
t.Run("ServeClosedPipe", func(t *testing.T) {
|
||||
|
@ -47,9 +66,24 @@ func TestProvisionerSDK(t *testing.T) {
|
|||
_ = client.Close()
|
||||
_ = server.Close()
|
||||
|
||||
err := provisionersdk.Serve(context.Background(), &proto.DRPCProvisionerUnimplementedServer{}, &provisionersdk.ServeOptions{
|
||||
Listener: server,
|
||||
err := provisionersdk.Serve(context.Background(), unimplementedServer{}, &provisionersdk.ServeOptions{
|
||||
Listener: server,
|
||||
WorkDirectory: t.TempDir(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
type unimplementedServer struct{}
|
||||
|
||||
func (unimplementedServer) Parse(_ *provisionersdk.Session, _ *proto.ParseRequest, _ <-chan struct{}) *proto.ParseComplete {
|
||||
return &proto.ParseComplete{Error: "unimplemented"}
|
||||
}
|
||||
|
||||
func (unimplementedServer) Plan(_ *provisionersdk.Session, _ *proto.PlanRequest, _ <-chan struct{}) *proto.PlanComplete {
|
||||
return &proto.PlanComplete{}
|
||||
}
|
||||
|
||||
func (unimplementedServer) Apply(_ *provisionersdk.Session, _ *proto.ApplyRequest, _ <-chan struct{}) *proto.ApplyComplete {
|
||||
return &proto.ApplyComplete{Error: "unimplemented"}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,318 @@
|
|||
package provisionersdk
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"cdr.dev/slog"
|
||||
"github.com/coder/coder/v2/provisionersdk/proto"
|
||||
)
|
||||
|
||||
// ReadmeFile is the location we look for to extract documentation from template
|
||||
// versions.
|
||||
const ReadmeFile = "README.md"
|
||||
|
||||
// protoServer is a wrapper that translates the dRPC protocol into a Session with method calls into the Server.
|
||||
type protoServer struct {
|
||||
server Server
|
||||
opts ServeOptions
|
||||
}
|
||||
|
||||
func (p *protoServer) Session(stream proto.DRPCProvisioner_SessionStream) error {
|
||||
sessID := uuid.New().String()
|
||||
s := &Session{
|
||||
Logger: p.opts.Logger.With(slog.F("session_id", sessID)),
|
||||
stream: stream,
|
||||
server: p.server,
|
||||
}
|
||||
sessDir := fmt.Sprintf("Session%s", sessID)
|
||||
s.WorkDirectory = filepath.Join(p.opts.WorkDirectory, sessDir)
|
||||
err := os.MkdirAll(s.WorkDirectory, 0o700)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("create work directory %q: %w", s.WorkDirectory, err)
|
||||
}
|
||||
defer func() {
|
||||
var err error
|
||||
// Cleanup the work directory after execution.
|
||||
for attempt := 0; attempt < 5; attempt++ {
|
||||
err = os.RemoveAll(s.WorkDirectory)
|
||||
if err != nil {
|
||||
// On Windows, open files cannot be removed.
|
||||
// When the provisioner daemon is shutting down,
|
||||
// it may take a few milliseconds for processes to exit.
|
||||
// See: https://github.com/golang/go/issues/50510
|
||||
s.Logger.Debug(s.Context(), "failed to clean work directory; trying again", slog.Error(err))
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
s.Logger.Debug(s.Context(), "cleaned up work directory")
|
||||
return
|
||||
}
|
||||
s.Logger.Error(s.Context(), "failed to clean up work directory after multiple attempts",
|
||||
slog.F("path", s.WorkDirectory), slog.Error(err))
|
||||
}()
|
||||
req, err := stream.Recv()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("receive config: %w", err)
|
||||
}
|
||||
config := req.GetConfig()
|
||||
if config == nil {
|
||||
return xerrors.New("first request must be Config")
|
||||
}
|
||||
s.Config = config
|
||||
if s.Config.ProvisionerLogLevel != "" {
|
||||
s.logLevel = proto.LogLevel_value[strings.ToUpper(s.Config.ProvisionerLogLevel)]
|
||||
}
|
||||
|
||||
err = s.extractArchive()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("extract archive: %w", err)
|
||||
}
|
||||
return s.handleRequests()
|
||||
}
|
||||
|
||||
func (s *Session) requestReader(done <-chan struct{}) <-chan *proto.Request {
|
||||
ch := make(chan *proto.Request)
|
||||
go func() {
|
||||
defer close(ch)
|
||||
for {
|
||||
req, err := s.stream.Recv()
|
||||
if err != nil {
|
||||
s.Logger.Info(s.Context(), "recv done on Session", slog.Error(err))
|
||||
return
|
||||
}
|
||||
select {
|
||||
case ch <- req:
|
||||
continue
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
func (s *Session) handleRequests() error {
|
||||
done := make(chan struct{})
|
||||
defer close(done)
|
||||
requests := s.requestReader(done)
|
||||
planned := false
|
||||
for req := range requests {
|
||||
if req.GetCancel() != nil {
|
||||
s.Logger.Warn(s.Context(), "ignoring cancel before request or after complete")
|
||||
continue
|
||||
}
|
||||
resp := &proto.Response{}
|
||||
if parse := req.GetParse(); parse != nil {
|
||||
r := &request[*proto.ParseRequest, *proto.ParseComplete]{
|
||||
req: parse,
|
||||
session: s,
|
||||
serverFn: s.server.Parse,
|
||||
cancels: requests,
|
||||
}
|
||||
complete, err := r.do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Handle README centrally, so that individual provisioners don't need to mess with it.
|
||||
readme, err := os.ReadFile(filepath.Join(s.WorkDirectory, ReadmeFile))
|
||||
if err == nil {
|
||||
complete.Readme = readme
|
||||
} else {
|
||||
s.Logger.Debug(s.Context(), "failed to parse readme (missing ok)", slog.Error(err))
|
||||
}
|
||||
resp.Type = &proto.Response_Parse{Parse: complete}
|
||||
}
|
||||
if plan := req.GetPlan(); plan != nil {
|
||||
r := &request[*proto.PlanRequest, *proto.PlanComplete]{
|
||||
req: plan,
|
||||
session: s,
|
||||
serverFn: s.server.Plan,
|
||||
cancels: requests,
|
||||
}
|
||||
complete, err := r.do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Type = &proto.Response_Plan{Plan: complete}
|
||||
if complete.Error == "" {
|
||||
planned = true
|
||||
}
|
||||
}
|
||||
if apply := req.GetApply(); apply != nil {
|
||||
if !planned {
|
||||
return xerrors.New("cannot apply before successful plan")
|
||||
}
|
||||
r := &request[*proto.ApplyRequest, *proto.ApplyComplete]{
|
||||
req: apply,
|
||||
session: s,
|
||||
serverFn: s.server.Apply,
|
||||
cancels: requests,
|
||||
}
|
||||
complete, err := r.do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Type = &proto.Response_Apply{Apply: complete}
|
||||
}
|
||||
err := s.stream.Send(resp)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("send response: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
Logger slog.Logger
|
||||
WorkDirectory string
|
||||
Config *proto.Config
|
||||
|
||||
server Server
|
||||
stream proto.DRPCProvisioner_SessionStream
|
||||
logLevel int32
|
||||
}
|
||||
|
||||
func (s *Session) Context() context.Context {
|
||||
return s.stream.Context()
|
||||
}
|
||||
|
||||
func (s *Session) extractArchive() error {
|
||||
ctx := s.Context()
|
||||
|
||||
s.Logger.Info(ctx, "unpacking template source archive",
|
||||
slog.F("size_bytes", len(s.Config.TemplateSourceArchive)),
|
||||
)
|
||||
|
||||
reader := tar.NewReader(bytes.NewBuffer(s.Config.TemplateSourceArchive))
|
||||
// for safety, nil out the reference on Config, since the reader now owns it.
|
||||
s.Config.TemplateSourceArchive = nil
|
||||
for {
|
||||
header, err := reader.Next()
|
||||
if err != nil {
|
||||
if xerrors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
return xerrors.Errorf("read template source archive: %w", err)
|
||||
}
|
||||
// Security: don't untar absolute or relative paths, as this can allow a malicious tar to overwrite
|
||||
// files outside the workdir.
|
||||
if !filepath.IsLocal(header.Name) {
|
||||
return xerrors.Errorf("refusing to extract to non-local path")
|
||||
}
|
||||
// nolint: gosec
|
||||
headerPath := filepath.Join(s.WorkDirectory, header.Name)
|
||||
if !strings.HasPrefix(headerPath, filepath.Clean(s.WorkDirectory)) {
|
||||
return xerrors.New("tar attempts to target relative upper directory")
|
||||
}
|
||||
mode := header.FileInfo().Mode()
|
||||
if mode == 0 {
|
||||
mode = 0o600
|
||||
}
|
||||
switch header.Typeflag {
|
||||
case tar.TypeDir:
|
||||
err = os.MkdirAll(headerPath, mode)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("mkdir %q: %w", headerPath, err)
|
||||
}
|
||||
s.Logger.Debug(context.Background(), "extracted directory",
|
||||
slog.F("path", headerPath),
|
||||
slog.F("mode", fmt.Sprintf("%O", mode)))
|
||||
case tar.TypeReg:
|
||||
file, err := os.OpenFile(headerPath, os.O_CREATE|os.O_RDWR, mode)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("create file %q (mode %s): %w", headerPath, mode, err)
|
||||
}
|
||||
// Max file size of 10MiB.
|
||||
size, err := io.CopyN(file, reader, 10<<20)
|
||||
if xerrors.Is(err, io.EOF) {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
_ = file.Close()
|
||||
return xerrors.Errorf("copy file %q: %w", headerPath, err)
|
||||
}
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("close file %q: %s", headerPath, err)
|
||||
}
|
||||
s.Logger.Debug(context.Background(), "extracted file",
|
||||
slog.F("size_bytes", size),
|
||||
slog.F("path", headerPath),
|
||||
slog.F("mode", mode),
|
||||
)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Session) ProvisionLog(level proto.LogLevel, output string) {
|
||||
if int32(level) < s.logLevel {
|
||||
return
|
||||
}
|
||||
|
||||
err := s.stream.Send(&proto.Response{Type: &proto.Response_Log{Log: &proto.Log{
|
||||
Level: level,
|
||||
Output: output,
|
||||
}}})
|
||||
if err != nil {
|
||||
s.Logger.Error(s.Context(), "failed to transmit log",
|
||||
slog.F("level", level), slog.F("output", output))
|
||||
}
|
||||
}
|
||||
|
||||
type pRequest interface {
|
||||
*proto.ParseRequest | *proto.PlanRequest | *proto.ApplyRequest
|
||||
}
|
||||
|
||||
type pComplete interface {
|
||||
*proto.ParseComplete | *proto.PlanComplete | *proto.ApplyComplete
|
||||
}
|
||||
|
||||
// request processes a single request call to the Server and returns its complete result, while also processing cancel
|
||||
// requests from the daemon. Provisioner implementations read from canceledOrComplete to be asynchronously informed
|
||||
// of cancel.
|
||||
type request[R pRequest, C pComplete] struct {
|
||||
req R
|
||||
session *Session
|
||||
cancels <-chan *proto.Request
|
||||
serverFn func(*Session, R, <-chan struct{}) C
|
||||
}
|
||||
|
||||
func (r *request[R, C]) do() (C, error) {
|
||||
canceledOrComplete := make(chan struct{})
|
||||
result := make(chan C)
|
||||
go func() {
|
||||
c := r.serverFn(r.session, r.req, canceledOrComplete)
|
||||
result <- c
|
||||
}()
|
||||
select {
|
||||
case req := <-r.cancels:
|
||||
close(canceledOrComplete)
|
||||
// wait for server to complete the request, even though we have canceled,
|
||||
// so that we can't start a new request, and so that if the job was close
|
||||
// to completion and the cancel was ignored, we return to complete.
|
||||
c := <-result
|
||||
// verify we got a cancel instead of another request or closed channel --- which is an error!
|
||||
if req.GetCancel() != nil {
|
||||
return c, nil
|
||||
}
|
||||
if req == nil {
|
||||
return c, xerrors.New("got nil while old request still processing")
|
||||
}
|
||||
return c, xerrors.Errorf("got new request %T while old request still processing", req.Type)
|
||||
case c := <-result:
|
||||
close(canceledOrComplete)
|
||||
return c, nil
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ const (
|
|||
MaxMessageSize = 4 << 20
|
||||
)
|
||||
|
||||
// MultiplexedConn returns a multiplexed dRPC connection from a yamux session.
|
||||
// MultiplexedConn returns a multiplexed dRPC connection from a yamux Session.
|
||||
func MultiplexedConn(session *yamux.Session) drpc.Conn {
|
||||
return &multiplexedDRPC{session}
|
||||
}
|
||||
|
|
|
@ -231,10 +231,10 @@ func setupRunnerTest(t *testing.T) (client *codersdk.Client, agentID uuid.UUID)
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
|
|
@ -48,10 +48,10 @@ func Test_Runner(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{
|
||||
Level: proto.LogLevel_INFO,
|
||||
Output: "hello from logs",
|
||||
|
@ -59,8 +59,8 @@ func Test_Runner(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{
|
||||
{
|
||||
Name: "example",
|
||||
|
@ -170,10 +170,10 @@ func Test_Runner(t *testing.T) {
|
|||
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Log{Log: &proto.Log{}},
|
||||
Type: &proto.Response_Log{Log: &proto.Log{}},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -282,10 +282,10 @@ func Test_Runner(t *testing.T) {
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{
|
||||
Level: proto.LogLevel_INFO,
|
||||
Output: "hello from logs",
|
||||
|
@ -293,8 +293,8 @@ func Test_Runner(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{
|
||||
{
|
||||
Name: "example",
|
||||
|
@ -407,11 +407,11 @@ func Test_Runner(t *testing.T) {
|
|||
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Error: "test error",
|
||||
},
|
||||
},
|
||||
|
|
|
@ -252,10 +252,10 @@ func setupRunnerTest(t *testing.T) (client *codersdk.Client, agentID uuid.UUID)
|
|||
authToken := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
|
|
@ -45,10 +45,10 @@ func Test_Runner(t *testing.T) {
|
|||
authToken3 := uuid.NewString()
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Log{
|
||||
Type: &proto.Response_Log{
|
||||
Log: &proto.Log{
|
||||
Level: proto.LogLevel_INFO,
|
||||
Output: "hello from logs",
|
||||
|
@ -56,8 +56,8 @@ func Test_Runner(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{
|
||||
{
|
||||
Name: "example1",
|
||||
|
@ -199,11 +199,11 @@ func Test_Runner(t *testing.T) {
|
|||
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{
|
||||
{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Error: "test error",
|
||||
},
|
||||
},
|
||||
|
|
|
@ -41,10 +41,10 @@ func TestRun(t *testing.T) {
|
|||
agentName = "agent"
|
||||
version = coderdtest.CreateTemplateVersion(t, client, firstUser.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
@ -154,10 +154,10 @@ func TestRun(t *testing.T) {
|
|||
agentName = "agent"
|
||||
version = coderdtest.CreateTemplateVersion(t, client, firstUser.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.ProvisionComplete,
|
||||
ProvisionApply: []*proto.Provision_Response{{
|
||||
Type: &proto.Provision_Response_Complete{
|
||||
Complete: &proto.Provision_Complete{
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
|
|
|
@ -8,10 +8,10 @@ import {
|
|||
Agent,
|
||||
App,
|
||||
AppSharingLevel,
|
||||
Parse_Complete,
|
||||
Parse_Response,
|
||||
Provision_Complete,
|
||||
Provision_Response,
|
||||
Response,
|
||||
ParseComplete,
|
||||
PlanComplete,
|
||||
ApplyComplete,
|
||||
Resource,
|
||||
RichParameter,
|
||||
} from "./provisionerGenerated"
|
||||
|
@ -337,11 +337,11 @@ type RecursivePartial<T> = {
|
|||
|
||||
interface EchoProvisionerResponses {
|
||||
// parse is for observing any Terraform variables
|
||||
parse?: RecursivePartial<Parse_Response>[]
|
||||
parse?: RecursivePartial<Response>[]
|
||||
// plan occurs when the template is imported
|
||||
plan?: RecursivePartial<Provision_Response>[]
|
||||
plan?: RecursivePartial<Response>[]
|
||||
// apply occurs when the workspace is built
|
||||
apply?: RecursivePartial<Provision_Response>[]
|
||||
apply?: RecursivePartial<Response>[]
|
||||
}
|
||||
|
||||
// createTemplateVersionTar consumes a series of echo provisioner protobufs and
|
||||
|
@ -353,109 +353,133 @@ const createTemplateVersionTar = async (
|
|||
responses = {}
|
||||
}
|
||||
if (!responses.parse) {
|
||||
responses.parse = [{}]
|
||||
responses.parse = [
|
||||
{
|
||||
parse: {},
|
||||
},
|
||||
]
|
||||
}
|
||||
if (!responses.apply) {
|
||||
responses.apply = [{}]
|
||||
responses.apply = [
|
||||
{
|
||||
apply: {},
|
||||
},
|
||||
]
|
||||
}
|
||||
if (!responses.plan) {
|
||||
responses.plan = responses.apply
|
||||
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 ?? [],
|
||||
gitAuthProviders: response.apply?.gitAuthProviders ?? [],
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const tar = new TarWriter()
|
||||
responses.parse.forEach((response, index) => {
|
||||
response.complete = {
|
||||
response.parse = {
|
||||
templateVariables: [],
|
||||
...response.complete,
|
||||
} as Parse_Complete
|
||||
error: "",
|
||||
readme: new Uint8Array(),
|
||||
...response.parse,
|
||||
} as ParseComplete
|
||||
tar.addFile(
|
||||
`${index}.parse.protobuf`,
|
||||
Parse_Response.encode(response as Parse_Response).finish(),
|
||||
Response.encode(response as Response).finish(),
|
||||
)
|
||||
})
|
||||
|
||||
const fillProvisionResponse = (
|
||||
response: RecursivePartial<Provision_Response>,
|
||||
) => {
|
||||
response.complete = {
|
||||
const fillResource = (resource: RecursivePartial<Resource>) => {
|
||||
if (resource.agents) {
|
||||
resource.agents = resource.agents?.map(
|
||||
(agent: RecursivePartial<Agent>) => {
|
||||
if (agent.apps) {
|
||||
agent.apps = agent.apps?.map((app: RecursivePartial<App>) => {
|
||||
return {
|
||||
command: "",
|
||||
displayName: "example",
|
||||
external: false,
|
||||
icon: "",
|
||||
sharingLevel: AppSharingLevel.PUBLIC,
|
||||
slug: "example",
|
||||
subdomain: false,
|
||||
url: "",
|
||||
...app,
|
||||
} as App
|
||||
})
|
||||
}
|
||||
return {
|
||||
apps: [],
|
||||
architecture: "amd64",
|
||||
connectionTimeoutSeconds: 300,
|
||||
directory: "",
|
||||
env: {},
|
||||
id: randomUUID(),
|
||||
metadata: [],
|
||||
motdFile: "",
|
||||
name: "dev",
|
||||
operatingSystem: "linux",
|
||||
shutdownScript: "",
|
||||
shutdownScriptTimeoutSeconds: 0,
|
||||
startupScript: "",
|
||||
startupScriptBehavior: "",
|
||||
startupScriptTimeoutSeconds: 300,
|
||||
troubleshootingUrl: "",
|
||||
token: randomUUID(),
|
||||
...agent,
|
||||
} as Agent
|
||||
},
|
||||
)
|
||||
}
|
||||
return {
|
||||
agents: [],
|
||||
dailyCost: 0,
|
||||
hide: false,
|
||||
icon: "",
|
||||
instanceType: "",
|
||||
metadata: [],
|
||||
name: "dev",
|
||||
type: "echo",
|
||||
...resource,
|
||||
} as Resource
|
||||
}
|
||||
|
||||
responses.apply.forEach((response, index) => {
|
||||
response.apply = {
|
||||
error: "",
|
||||
state: new Uint8Array(),
|
||||
resources: [],
|
||||
parameters: [],
|
||||
gitAuthProviders: [],
|
||||
plan: new Uint8Array(),
|
||||
...response.complete,
|
||||
} as Provision_Complete
|
||||
response.complete.resources = response.complete.resources?.map(
|
||||
(resource) => {
|
||||
if (resource.agents) {
|
||||
resource.agents = resource.agents?.map((agent) => {
|
||||
if (agent.apps) {
|
||||
agent.apps = agent.apps?.map((app) => {
|
||||
return {
|
||||
command: "",
|
||||
displayName: "example",
|
||||
external: false,
|
||||
icon: "",
|
||||
sharingLevel: AppSharingLevel.PUBLIC,
|
||||
slug: "example",
|
||||
subdomain: false,
|
||||
url: "",
|
||||
...app,
|
||||
} as App
|
||||
})
|
||||
}
|
||||
return {
|
||||
apps: [],
|
||||
architecture: "amd64",
|
||||
connectionTimeoutSeconds: 300,
|
||||
directory: "",
|
||||
env: {},
|
||||
id: randomUUID(),
|
||||
metadata: [],
|
||||
motdFile: "",
|
||||
name: "dev",
|
||||
operatingSystem: "linux",
|
||||
shutdownScript: "",
|
||||
shutdownScriptTimeoutSeconds: 0,
|
||||
startupScript: "",
|
||||
startupScriptBehavior: "",
|
||||
startupScriptTimeoutSeconds: 300,
|
||||
troubleshootingUrl: "",
|
||||
token: randomUUID(),
|
||||
...agent,
|
||||
} as Agent
|
||||
})
|
||||
}
|
||||
return {
|
||||
agents: [],
|
||||
dailyCost: 0,
|
||||
hide: false,
|
||||
icon: "",
|
||||
instanceType: "",
|
||||
metadata: [],
|
||||
name: "dev",
|
||||
type: "echo",
|
||||
...resource,
|
||||
} as Resource
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
responses.apply.forEach((response, index) => {
|
||||
fillProvisionResponse(response)
|
||||
...response.apply,
|
||||
} as ApplyComplete
|
||||
response.apply.resources = response.apply.resources?.map(fillResource)
|
||||
|
||||
tar.addFile(
|
||||
`${index}.provision.apply.protobuf`,
|
||||
Provision_Response.encode(response as Provision_Response).finish(),
|
||||
`${index}.apply.protobuf`,
|
||||
Response.encode(response as Response).finish(),
|
||||
)
|
||||
})
|
||||
responses.plan.forEach((response, index) => {
|
||||
fillProvisionResponse(response)
|
||||
response.plan = {
|
||||
error: "",
|
||||
resources: [],
|
||||
parameters: [],
|
||||
gitAuthProviders: [],
|
||||
...response.plan,
|
||||
} as PlanComplete
|
||||
response.plan.resources = response.plan.resources?.map(fillResource)
|
||||
|
||||
tar.addFile(
|
||||
`${index}.provision.plan.protobuf`,
|
||||
Provision_Response.encode(response as Provision_Response).finish(),
|
||||
`${index}.plan.protobuf`,
|
||||
Response.encode(response as Response).finish(),
|
||||
)
|
||||
})
|
||||
const tarFile = await tar.write()
|
||||
|
@ -512,16 +536,21 @@ export const echoResponsesWithParameters = (
|
|||
richParameters: RichParameter[],
|
||||
): EchoProvisionerResponses => {
|
||||
return {
|
||||
parse: [
|
||||
{
|
||||
parse: {},
|
||||
},
|
||||
],
|
||||
plan: [
|
||||
{
|
||||
complete: {
|
||||
plan: {
|
||||
parameters: richParameters,
|
||||
},
|
||||
},
|
||||
],
|
||||
apply: [
|
||||
{
|
||||
complete: {
|
||||
apply: {
|
||||
resources: [
|
||||
{
|
||||
name: "example",
|
||||
|
|
|
@ -21,6 +21,7 @@ export enum AppSharingLevel {
|
|||
UNRECOGNIZED = -1,
|
||||
}
|
||||
|
||||
/** WorkspaceTransition is the desired outcome of a build */
|
||||
export enum WorkspaceTransition {
|
||||
START = 0,
|
||||
STOP = 1,
|
||||
|
@ -177,29 +178,8 @@ export interface Resource_Metadata {
|
|||
isNull: boolean
|
||||
}
|
||||
|
||||
/** Parse consumes source-code from a directory to produce inputs. */
|
||||
export interface Parse {}
|
||||
|
||||
export interface Parse_Request {
|
||||
directory: string
|
||||
}
|
||||
|
||||
export interface Parse_Complete {
|
||||
templateVariables: TemplateVariable[]
|
||||
}
|
||||
|
||||
export interface Parse_Response {
|
||||
log?: Log | undefined
|
||||
complete?: Parse_Complete | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Provision consumes source-code from a directory to produce resources.
|
||||
* Exactly one of Plan or Apply must be provided in a single session.
|
||||
*/
|
||||
export interface Provision {}
|
||||
|
||||
export interface Provision_Metadata {
|
||||
/** Metadata is information about a workspace used in the execution of a build */
|
||||
export interface Metadata {
|
||||
coderUrl: string
|
||||
workspaceTransition: WorkspaceTransition
|
||||
workspaceName: string
|
||||
|
@ -213,49 +193,74 @@ export interface Provision_Metadata {
|
|||
workspaceOwnerSessionToken: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Config represents execution configuration shared by both Plan and
|
||||
* Apply commands.
|
||||
*/
|
||||
export interface Provision_Config {
|
||||
directory: string
|
||||
/** 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
|
||||
metadata: Provision_Metadata | undefined
|
||||
provisionerLogLevel: string
|
||||
}
|
||||
|
||||
export interface Provision_Plan {
|
||||
config: Provision_Config | undefined
|
||||
/** ParseRequest consumes source-code to produce inputs. */
|
||||
export interface ParseRequest {}
|
||||
|
||||
/** ParseComplete indicates a request to parse completed. */
|
||||
export interface ParseComplete {
|
||||
error: string
|
||||
templateVariables: TemplateVariable[]
|
||||
readme: Uint8Array
|
||||
}
|
||||
|
||||
/** PlanRequest asks the provisioner to plan what resources & parameters it will create */
|
||||
export interface PlanRequest {
|
||||
metadata: Metadata | undefined
|
||||
richParameterValues: RichParameterValue[]
|
||||
variableValues: VariableValue[]
|
||||
gitAuthProviders: GitAuthProvider[]
|
||||
}
|
||||
|
||||
export interface Provision_Apply {
|
||||
config: Provision_Config | undefined
|
||||
plan: Uint8Array
|
||||
/** PlanComplete indicates a request to plan completed. */
|
||||
export interface PlanComplete {
|
||||
error: string
|
||||
resources: Resource[]
|
||||
parameters: RichParameter[]
|
||||
gitAuthProviders: string[]
|
||||
}
|
||||
|
||||
export interface Provision_Cancel {}
|
||||
|
||||
export interface Provision_Request {
|
||||
plan?: Provision_Plan | undefined
|
||||
apply?: Provision_Apply | undefined
|
||||
cancel?: Provision_Cancel | undefined
|
||||
/**
|
||||
* ApplyRequest asks the provisioner to apply the changes. Apply MUST be preceded by a successful plan request/response
|
||||
* in the same Session. The plan data is not transmitted over the wire and is cached by the provisioner in the Session.
|
||||
*/
|
||||
export interface ApplyRequest {
|
||||
metadata: Metadata | undefined
|
||||
}
|
||||
|
||||
export interface Provision_Complete {
|
||||
/** ApplyComplete indicates a request to apply completed. */
|
||||
export interface ApplyComplete {
|
||||
state: Uint8Array
|
||||
error: string
|
||||
resources: Resource[]
|
||||
parameters: RichParameter[]
|
||||
gitAuthProviders: string[]
|
||||
plan: Uint8Array
|
||||
}
|
||||
|
||||
export interface Provision_Response {
|
||||
/** CancelRequest requests that the previous request be canceled gracefully. */
|
||||
export interface CancelRequest {}
|
||||
|
||||
export interface Request {
|
||||
config?: Config | undefined
|
||||
parse?: ParseRequest | undefined
|
||||
plan?: PlanRequest | undefined
|
||||
apply?: ApplyRequest | undefined
|
||||
cancel?: CancelRequest | undefined
|
||||
}
|
||||
|
||||
export interface Response {
|
||||
log?: Log | undefined
|
||||
complete?: Provision_Complete | undefined
|
||||
parse?: ParseComplete | undefined
|
||||
plan?: PlanComplete | undefined
|
||||
apply?: ApplyComplete | undefined
|
||||
}
|
||||
|
||||
export const Empty = {
|
||||
|
@ -648,60 +653,9 @@ export const Resource_Metadata = {
|
|||
},
|
||||
}
|
||||
|
||||
export const Parse = {
|
||||
encode(_: Parse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
|
||||
return writer
|
||||
},
|
||||
}
|
||||
|
||||
export const Parse_Request = {
|
||||
export const Metadata = {
|
||||
encode(
|
||||
message: Parse_Request,
|
||||
writer: _m0.Writer = _m0.Writer.create(),
|
||||
): _m0.Writer {
|
||||
if (message.directory !== "") {
|
||||
writer.uint32(10).string(message.directory)
|
||||
}
|
||||
return writer
|
||||
},
|
||||
}
|
||||
|
||||
export const Parse_Complete = {
|
||||
encode(
|
||||
message: Parse_Complete,
|
||||
writer: _m0.Writer = _m0.Writer.create(),
|
||||
): _m0.Writer {
|
||||
for (const v of message.templateVariables) {
|
||||
TemplateVariable.encode(v!, writer.uint32(10).fork()).ldelim()
|
||||
}
|
||||
return writer
|
||||
},
|
||||
}
|
||||
|
||||
export const Parse_Response = {
|
||||
encode(
|
||||
message: Parse_Response,
|
||||
writer: _m0.Writer = _m0.Writer.create(),
|
||||
): _m0.Writer {
|
||||
if (message.log !== undefined) {
|
||||
Log.encode(message.log, writer.uint32(10).fork()).ldelim()
|
||||
}
|
||||
if (message.complete !== undefined) {
|
||||
Parse_Complete.encode(message.complete, writer.uint32(18).fork()).ldelim()
|
||||
}
|
||||
return writer
|
||||
},
|
||||
}
|
||||
|
||||
export const Provision = {
|
||||
encode(_: Provision, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
|
||||
return writer
|
||||
},
|
||||
}
|
||||
|
||||
export const Provision_Metadata = {
|
||||
encode(
|
||||
message: Provision_Metadata,
|
||||
message: Metadata,
|
||||
writer: _m0.Writer = _m0.Writer.create(),
|
||||
): _m0.Writer {
|
||||
if (message.coderUrl !== "") {
|
||||
|
@ -741,96 +695,108 @@ export const Provision_Metadata = {
|
|||
},
|
||||
}
|
||||
|
||||
export const Provision_Config = {
|
||||
export const Config = {
|
||||
encode(
|
||||
message: Provision_Config,
|
||||
message: Config,
|
||||
writer: _m0.Writer = _m0.Writer.create(),
|
||||
): _m0.Writer {
|
||||
if (message.directory !== "") {
|
||||
writer.uint32(10).string(message.directory)
|
||||
if (message.templateSourceArchive.length !== 0) {
|
||||
writer.uint32(10).bytes(message.templateSourceArchive)
|
||||
}
|
||||
if (message.state.length !== 0) {
|
||||
writer.uint32(18).bytes(message.state)
|
||||
}
|
||||
if (message.metadata !== undefined) {
|
||||
Provision_Metadata.encode(
|
||||
message.metadata,
|
||||
writer.uint32(26).fork(),
|
||||
).ldelim()
|
||||
}
|
||||
if (message.provisionerLogLevel !== "") {
|
||||
writer.uint32(34).string(message.provisionerLogLevel)
|
||||
writer.uint32(26).string(message.provisionerLogLevel)
|
||||
}
|
||||
return writer
|
||||
},
|
||||
}
|
||||
|
||||
export const Provision_Plan = {
|
||||
export const ParseRequest = {
|
||||
encode(
|
||||
message: Provision_Plan,
|
||||
_: ParseRequest,
|
||||
writer: _m0.Writer = _m0.Writer.create(),
|
||||
): _m0.Writer {
|
||||
if (message.config !== undefined) {
|
||||
Provision_Config.encode(message.config, writer.uint32(10).fork()).ldelim()
|
||||
return writer
|
||||
},
|
||||
}
|
||||
|
||||
export const ParseComplete = {
|
||||
encode(
|
||||
message: ParseComplete,
|
||||
writer: _m0.Writer = _m0.Writer.create(),
|
||||
): _m0.Writer {
|
||||
if (message.error !== "") {
|
||||
writer.uint32(10).string(message.error)
|
||||
}
|
||||
for (const v of message.templateVariables) {
|
||||
TemplateVariable.encode(v!, writer.uint32(18).fork()).ldelim()
|
||||
}
|
||||
if (message.readme.length !== 0) {
|
||||
writer.uint32(26).bytes(message.readme)
|
||||
}
|
||||
return writer
|
||||
},
|
||||
}
|
||||
|
||||
export const PlanRequest = {
|
||||
encode(
|
||||
message: PlanRequest,
|
||||
writer: _m0.Writer = _m0.Writer.create(),
|
||||
): _m0.Writer {
|
||||
if (message.metadata !== undefined) {
|
||||
Metadata.encode(message.metadata, writer.uint32(10).fork()).ldelim()
|
||||
}
|
||||
for (const v of message.richParameterValues) {
|
||||
RichParameterValue.encode(v!, writer.uint32(26).fork()).ldelim()
|
||||
RichParameterValue.encode(v!, writer.uint32(18).fork()).ldelim()
|
||||
}
|
||||
for (const v of message.variableValues) {
|
||||
VariableValue.encode(v!, writer.uint32(34).fork()).ldelim()
|
||||
VariableValue.encode(v!, writer.uint32(26).fork()).ldelim()
|
||||
}
|
||||
for (const v of message.gitAuthProviders) {
|
||||
GitAuthProvider.encode(v!, writer.uint32(42).fork()).ldelim()
|
||||
GitAuthProvider.encode(v!, writer.uint32(34).fork()).ldelim()
|
||||
}
|
||||
return writer
|
||||
},
|
||||
}
|
||||
|
||||
export const Provision_Apply = {
|
||||
export const PlanComplete = {
|
||||
encode(
|
||||
message: Provision_Apply,
|
||||
message: PlanComplete,
|
||||
writer: _m0.Writer = _m0.Writer.create(),
|
||||
): _m0.Writer {
|
||||
if (message.config !== undefined) {
|
||||
Provision_Config.encode(message.config, writer.uint32(10).fork()).ldelim()
|
||||
if (message.error !== "") {
|
||||
writer.uint32(10).string(message.error)
|
||||
}
|
||||
if (message.plan.length !== 0) {
|
||||
writer.uint32(18).bytes(message.plan)
|
||||
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.gitAuthProviders) {
|
||||
writer.uint32(34).string(v!)
|
||||
}
|
||||
return writer
|
||||
},
|
||||
}
|
||||
|
||||
export const Provision_Cancel = {
|
||||
export const ApplyRequest = {
|
||||
encode(
|
||||
_: Provision_Cancel,
|
||||
message: ApplyRequest,
|
||||
writer: _m0.Writer = _m0.Writer.create(),
|
||||
): _m0.Writer {
|
||||
return writer
|
||||
},
|
||||
}
|
||||
|
||||
export const Provision_Request = {
|
||||
encode(
|
||||
message: Provision_Request,
|
||||
writer: _m0.Writer = _m0.Writer.create(),
|
||||
): _m0.Writer {
|
||||
if (message.plan !== undefined) {
|
||||
Provision_Plan.encode(message.plan, writer.uint32(10).fork()).ldelim()
|
||||
}
|
||||
if (message.apply !== undefined) {
|
||||
Provision_Apply.encode(message.apply, writer.uint32(18).fork()).ldelim()
|
||||
}
|
||||
if (message.cancel !== undefined) {
|
||||
Provision_Cancel.encode(message.cancel, writer.uint32(26).fork()).ldelim()
|
||||
if (message.metadata !== undefined) {
|
||||
Metadata.encode(message.metadata, writer.uint32(10).fork()).ldelim()
|
||||
}
|
||||
return writer
|
||||
},
|
||||
}
|
||||
|
||||
export const Provision_Complete = {
|
||||
export const ApplyComplete = {
|
||||
encode(
|
||||
message: Provision_Complete,
|
||||
message: ApplyComplete,
|
||||
writer: _m0.Writer = _m0.Writer.create(),
|
||||
): _m0.Writer {
|
||||
if (message.state.length !== 0) {
|
||||
|
@ -848,34 +814,76 @@ export const Provision_Complete = {
|
|||
for (const v of message.gitAuthProviders) {
|
||||
writer.uint32(42).string(v!)
|
||||
}
|
||||
if (message.plan.length !== 0) {
|
||||
writer.uint32(50).bytes(message.plan)
|
||||
return writer
|
||||
},
|
||||
}
|
||||
|
||||
export const CancelRequest = {
|
||||
encode(
|
||||
_: CancelRequest,
|
||||
writer: _m0.Writer = _m0.Writer.create(),
|
||||
): _m0.Writer {
|
||||
return writer
|
||||
},
|
||||
}
|
||||
|
||||
export const Request = {
|
||||
encode(
|
||||
message: Request,
|
||||
writer: _m0.Writer = _m0.Writer.create(),
|
||||
): _m0.Writer {
|
||||
if (message.config !== undefined) {
|
||||
Config.encode(message.config, writer.uint32(10).fork()).ldelim()
|
||||
}
|
||||
if (message.parse !== undefined) {
|
||||
ParseRequest.encode(message.parse, writer.uint32(18).fork()).ldelim()
|
||||
}
|
||||
if (message.plan !== undefined) {
|
||||
PlanRequest.encode(message.plan, writer.uint32(26).fork()).ldelim()
|
||||
}
|
||||
if (message.apply !== undefined) {
|
||||
ApplyRequest.encode(message.apply, writer.uint32(34).fork()).ldelim()
|
||||
}
|
||||
if (message.cancel !== undefined) {
|
||||
CancelRequest.encode(message.cancel, writer.uint32(42).fork()).ldelim()
|
||||
}
|
||||
return writer
|
||||
},
|
||||
}
|
||||
|
||||
export const Provision_Response = {
|
||||
export const Response = {
|
||||
encode(
|
||||
message: Provision_Response,
|
||||
message: Response,
|
||||
writer: _m0.Writer = _m0.Writer.create(),
|
||||
): _m0.Writer {
|
||||
if (message.log !== undefined) {
|
||||
Log.encode(message.log, writer.uint32(10).fork()).ldelim()
|
||||
}
|
||||
if (message.complete !== undefined) {
|
||||
Provision_Complete.encode(
|
||||
message.complete,
|
||||
writer.uint32(18).fork(),
|
||||
).ldelim()
|
||||
if (message.parse !== undefined) {
|
||||
ParseComplete.encode(message.parse, writer.uint32(18).fork()).ldelim()
|
||||
}
|
||||
if (message.plan !== undefined) {
|
||||
PlanComplete.encode(message.plan, writer.uint32(26).fork()).ldelim()
|
||||
}
|
||||
if (message.apply !== undefined) {
|
||||
ApplyComplete.encode(message.apply, writer.uint32(34).fork()).ldelim()
|
||||
}
|
||||
return writer
|
||||
},
|
||||
}
|
||||
|
||||
export interface Provisioner {
|
||||
Parse(request: Parse_Request): Observable<Parse_Response>
|
||||
Provision(
|
||||
request: Observable<Provision_Request>,
|
||||
): Observable<Provision_Response>
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
Session(request: Observable<Request>): Observable<Response>
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ test("app", async ({ context, page }) => {
|
|||
const template = await createTemplate(page, {
|
||||
apply: [
|
||||
{
|
||||
complete: {
|
||||
apply: {
|
||||
resources: [
|
||||
{
|
||||
agents: [
|
||||
|
|
|
@ -21,7 +21,7 @@ test("create workspace", async ({ page }) => {
|
|||
const template = await createTemplate(page, {
|
||||
apply: [
|
||||
{
|
||||
complete: {
|
||||
apply: {
|
||||
resources: [
|
||||
{
|
||||
name: "example",
|
||||
|
|
|
@ -15,7 +15,7 @@ test("ssh with agent " + agentVersion, async ({ page }) => {
|
|||
const template = await createTemplate(page, {
|
||||
apply: [
|
||||
{
|
||||
complete: {
|
||||
apply: {
|
||||
resources: [
|
||||
{
|
||||
agents: [
|
||||
|
|
|
@ -15,7 +15,7 @@ test("ssh with client " + clientVersion, async ({ page }) => {
|
|||
const template = await createTemplate(page, {
|
||||
apply: [
|
||||
{
|
||||
complete: {
|
||||
apply: {
|
||||
resources: [
|
||||
{
|
||||
agents: [
|
||||
|
|
|
@ -7,7 +7,7 @@ test("web terminal", async ({ context, page }) => {
|
|||
const template = await createTemplate(page, {
|
||||
apply: [
|
||||
{
|
||||
complete: {
|
||||
apply: {
|
||||
resources: [
|
||||
{
|
||||
agents: [
|
||||
|
|
Loading…
Reference in New Issue